Repository: gohugoio/hugo
Pull request #11175 (2023-06-27 08:57:44)

build(deps): bump github.com/evanw/esbuild from 0.18.5 to 0.18.10
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.18.5 to 0.18.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/evanw/esbuild/releases">github.com/evanw/esbuild's releases</a>.</em></p>
<blockquote>
<h2>v0.18.10</h2>
<ul>
<li>
<p>Fix a tree-shaking bug that removed side effects (<a href="https://redirect.github.com/evanw/esbuild/issues/3195">#3195</a>)</p>
<p>This fixes a regression in version 0.18.4 where combining <code>--minify-syntax</code> with <code>--keep-names</code> could cause expressions with side effects after a function declaration to be considered side-effect free for tree shaking purposes. The reason was because <code>--keep-names</code> generates an expression statement containing a call to a helper function after the function declaration with a special flag that makes the function call able to be tree shaken, and then <code>--minify-syntax</code> could potentially merge that expression statement with following expressions without clearing the flag. This release fixes the bug by clearing the flag when merging expression statements together.</p>
</li>
<li>
<p>Fix an incorrect warning about CSS nesting (<a href="https://redirect.github.com/evanw/esbuild/issues/3197">#3197</a>)</p>
<p>A warning is currently generated when transforming nested CSS to a browser that doesn't support <code>:is()</code> because transformed nested CSS may need to use that feature to represent nesting. This was previously always triggered when an at-rule was encountered in a declaration context. Typically the only case you would encounter this is when using CSS nesting within a selector rule. However, there is a case where that's not true: when using a margin at-rule such as <code>@top-left</code> within <code>@page</code>. This release avoids incorrectly generating a warning in this case by checking that the at-rule is within a selector rule before generating a warning.</p>
</li>
</ul>
<h2>v0.18.9</h2>
<ul>
<li>
<p>Fix <code>await using</code> declarations inside <code>async</code> generator functions</p>
<p>I forgot about the new <code>await using</code> declarations when implementing lowering for <code>async</code> generator functions in the previous release. This change fixes the transformation of <code>await using</code> declarations when they are inside lowered <code>async</code> generator functions:</p>
<pre lang="js"><code>// Original code
async function* foo() {
  await using x = await y
}
<p>// Old output (with --supported:async-generator=false)
function foo() {
return __asyncGenerator(this, null, function* () {
await using x = yield new __await(y);
});
}</p>
<p>// New output (with --supported:async-generator=false)
function foo() {
return __asyncGenerator(this, null, function* () {
var _stack = [];
try {
const x = __using(<em>stack, yield new __await(y), true);
} catch (</em>) {
var _error = _, _hasError = true;
} finally {
var _promise = __callDispose(_stack, _error, _hasError);
_promise &amp;&amp; (yield new __await(_promise));
}
});
}
</code></pre></p>
</li>
<li>
<p>Insert some prefixed CSS properties when appropriate (<a href="https://redirect.github.com/evanw/esbuild/issues/3122">#3122</a>)</p>
<p>With this release, esbuild will now insert prefixed CSS properties in certain cases when the <code>target</code> setting includes browsers that require a certain prefix. This is currently done for the following properties:</p>
<ul>
<li><code>appearance: *;</code> =&gt; <code>-webkit-appearance: *; -moz-appearance: *;</code></li>
<li><code>backdrop-filter: *;</code> =&gt; <code>-webkit-backdrop-filter: *;</code></li>
<li><code>background-clip: text</code> =&gt; <code>-webkit-background-clip: text;</code></li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/evanw/esbuild/blob/main/CHANGELOG.md">github.com/evanw/esbuild's changelog</a>.</em></p>
<blockquote>
<h2>0.18.10</h2>
<ul>
<li>
<p>Fix a tree-shaking bug that removed side effects (<a href="https://redirect.github.com/evanw/esbuild/issues/3195">#3195</a>)</p>
<p>This fixes a regression in version 0.18.4 where combining <code>--minify-syntax</code> with <code>--keep-names</code> could cause expressions with side effects after a function declaration to be considered side-effect free for tree shaking purposes. The reason was because <code>--keep-names</code> generates an expression statement containing a call to a helper function after the function declaration with a special flag that makes the function call able to be tree shaken, and then <code>--minify-syntax</code> could potentially merge that expression statement with following expressions without clearing the flag. This release fixes the bug by clearing the flag when merging expression statements together.</p>
</li>
<li>
<p>Fix an incorrect warning about CSS nesting (<a href="https://redirect.github.com/evanw/esbuild/issues/3197">#3197</a>)</p>
<p>A warning is currently generated when transforming nested CSS to a browser that doesn't support <code>:is()</code> because transformed nested CSS may need to use that feature to represent nesting. This was previously always triggered when an at-rule was encountered in a declaration context. Typically the only case you would encounter this is when using CSS nesting within a selector rule. However, there is a case where that's not true: when using a margin at-rule such as <code>@top-left</code> within <code>@page</code>. This release avoids incorrectly generating a warning in this case by checking that the at-rule is within a selector rule before generating a warning.</p>
</li>
</ul>
<h2>0.18.9</h2>
<ul>
<li>
<p>Fix <code>await using</code> declarations inside <code>async</code> generator functions</p>
<p>I forgot about the new <code>await using</code> declarations when implementing lowering for <code>async</code> generator functions in the previous release. This change fixes the transformation of <code>await using</code> declarations when they are inside lowered <code>async</code> generator functions:</p>
<pre lang="js"><code>// Original code
async function* foo() {
  await using x = await y
}
<p>// Old output (with --supported:async-generator=false)
function foo() {
return __asyncGenerator(this, null, function* () {
await using x = yield new __await(y);
});
}</p>
<p>// New output (with --supported:async-generator=false)
function foo() {
return __asyncGenerator(this, null, function* () {
var _stack = [];
try {
const x = __using(<em>stack, yield new __await(y), true);
} catch (</em>) {
var _error = _, _hasError = true;
} finally {
var _promise = __callDispose(_stack, _error, _hasError);
_promise &amp;&amp; (yield new __await(_promise));
}
});
}
</code></pre></p>
</li>
<li>
<p>Insert some prefixed CSS properties when appropriate (<a href="https://redirect.github.com/evanw/esbuild/issues/3122">#3122</a>)</p>
<p>With this release, esbuild will now insert prefixed CSS properties in certain cases when the <code>target</code> setting includes browsers that require a certain prefix. This is currently done for the following properties:</p>
<ul>
<li><code>appearance: *;</code> =&gt; <code>-webkit-appearance: *; -moz-appearance: *;</code></li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/evanw/esbuild/commit/cdb6c7ce3f9419e51bd855fd61d07d2c615fb30e"><code>cdb6c7c</code></a> publish 0.18.10 to npm</li>
<li><a href="https://github.com/evanw/esbuild/commit/8a15942647f5604660f81d7f2af6fd18e0dc7268"><code>8a15942</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/3197">#3197</a>: css nesting warning in selector subtree</li>
<li><a href="https://github.com/evanw/esbuild/commit/b677152aaff13d035a5e1602a5ab3c17cbd7ebca"><code>b677152</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/3195">#3195</a>: tree-shaking bug that didn't keep code</li>
<li><a href="https://github.com/evanw/esbuild/commit/d568ff038d012a7894a9d4334b75f8559bf2532e"><code>d568ff0</code></a> publish 0.18.9 to npm</li>
<li><a href="https://github.com/evanw/esbuild/commit/7e10dd60f9ff830c26aae12ecc2d2c2bc4ab45aa"><code>7e10dd6</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/2041">#2041</a>: add text loader data to source maps</li>
<li><a href="https://github.com/evanw/esbuild/commit/9ed47f51d29917f410c1e3ec632aa68361c43d31"><code>9ed47f5</code></a> fix <a href="https://redirect.github.com/evanw/esbuild/issues/3122">#3122</a>: prefix some more css properties</li>
<li><a href="https://github.com/evanw/esbuild/commit/9d8a7e5115e9dd95fe2e67ececd8aef595857791"><code>9d8a7e5</code></a> prefix some css properties (<a href="https://redirect.github.com/evanw/esbuild/issues/3122">#3122</a>)</li>
<li><a href="https://github.com/evanw/esbuild/commit/33322d2e4a84c075055ab5caf7c0726e6d5c1c0d"><code>33322d2</code></a> switch css minify from in-place to a new array</li>
<li><a href="https://github.com/evanw/esbuild/commit/a4e2a1e50dc3813b51a12bef79d8dc646e917c56"><code>a4e2a1e</code></a> minify: shorter printing of ts enum property keys</li>
<li><a href="https://github.com/evanw/esbuild/commit/dc913df9ff26e5aa7c3dd71db9787f488dd26c17"><code>dc913df</code></a> printer: extract enum inlining into a helper</li>
<li>Additional commits viewable in <a href="https://github.com/evanw/esbuild/compare/v0.18.5...v0.18.10">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/evanw/esbuild&package-manager=go_modules&previous-version=0.18.5&new-version=0.18.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go
index 71992e409..3e96bfc73 100644
--- a/commands/hugobuilder.go
+++ b/commands/hugobuilder.go
@@ -45,7 +45,6 @@ import (
 	"github.com/gohugoio/hugo/resources/page"
 	"github.com/gohugoio/hugo/tpl"
 	"github.com/gohugoio/hugo/watcher"
-	"github.com/spf13/afero"
 	"github.com/spf13/fsync"
 	"golang.org/x/sync/errgroup"
 	"golang.org/x/sync/semaphore"
@@ -419,18 +418,6 @@ func (c *hugoBuilder) build() error {
 			return err
 		}
 
-		if c.r.printPathWarnings {
-			hugofs.WalkFilesystems(h.Fs.PublishDir, func(fs afero.Fs) bool {
-				if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
-					dupes := dfs.ReportDuplicates()
-					if dupes != "" {
-						c.r.logger.Warnln("Duplicate target paths:", dupes)
-					}
-				}
-				return false
-			})
-		}
-
 		if c.r.printUnusedTemplates {
 			unusedTemplates := h.Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates()
 			for _, unusedTemplate := range unusedTemplates {
diff --git a/commands/new.go b/commands/new.go
index b5c4d2152..633f1becb 100644
--- a/commands/new.go
+++ b/commands/new.go
@@ -155,11 +155,12 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
 			},
 			&simpleCommand{
 				name:  "theme",
-				use:   "theme [path]",
-				short: "Create a new site (skeleton)",
-				long: `Create a new site in the provided directory.
-The new site will have the correct structure, but no content or theme yet.
-Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
+				use:   "theme [name]",
+				short: "Create a new theme (skeleton)",
+				long: `Create a new theme (skeleton) called [name] in ./themes.
+New theme is a skeleton. Please add content to the touched files. Add your
+name to the copyright line in the license and adjust the theme.toml file
+according to your needs.`,
 				run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
 					h, err := r.Hugo(flagsToCfg(cd, nil))
 					if err != nil {
diff --git a/common/collections/append.go b/common/collections/append.go
index 91abc46d3..8f1e21ea3 100644
--- a/common/collections/append.go
+++ b/common/collections/append.go
@@ -66,6 +66,10 @@ func Append(to any, from ...any) (any, error) {
 
 		if len(from) == 1 {
 			fromv := reflect.ValueOf(from[0])
+			if !fromv.IsValid() {
+				// from[0] is nil
+				return appendToInterfaceSliceFromValues(tov, fromv)
+			}
 			fromt := fromv.Type()
 			if fromt.Kind() == reflect.Slice {
 				fromt = fromt.Elem()
@@ -94,7 +98,7 @@ func Append(to any, from ...any) (any, error) {
 
 	for _, f := range from {
 		fv := reflect.ValueOf(f)
-		if !fv.Type().AssignableTo(tot) {
+		if !fv.IsValid() || !fv.Type().AssignableTo(tot) {
 			// Fall back to a []interface{} slice.
 			tov, _ := indirect(reflect.ValueOf(to))
 			return appendToInterfaceSlice(tov, from...)
@@ -109,6 +113,10 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro
 	var tos []any
 
 	for _, slice := range []reflect.Value{slice1, slice2} {
+		if !slice.IsValid() {
+			tos = append(tos, nil)
+			continue
+		}
 		for i := 0; i < slice.Len(); i++ {
 			tos = append(tos, slice.Index(i).Interface())
 		}
diff --git a/config/security/securityConfig.go b/config/security/securityConfig.go
index 5d0db2fb9..3d17b7a48 100644
--- a/config/security/securityConfig.go
+++ b/config/security/securityConfig.go
@@ -34,7 +34,7 @@ const securityConfigKey = "security"
 // DefaultConfig holds the default security policy.
 var DefaultConfig = Config{
 	Exec: Exec{
-		Allow: NewWhitelist(
+		Allow: MustNewWhitelist(
 			"^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
 			"^go$",                       // for Go Modules
 			"^npx$",                      // used by all Node tools (Babel, PostCSS).
@@ -42,14 +42,14 @@ var DefaultConfig = Config{
 		),
 		// These have been tested to work with Hugo's external programs
 		// on Windows, Linux and MacOS.
-		OsEnv: NewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$`),
+		OsEnv: MustNewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$`),
 	},
 	Funcs: Funcs{
-		Getenv: NewWhitelist("^HUGO_", "^CI$"),
+		Getenv: MustNewWhitelist("^HUGO_", "^CI$"),
 	},
 	HTTP: HTTP{
-		URLs:    NewWhitelist(".*"),
-		Methods: NewWhitelist("(?i)GET|POST"),
+		URLs:    MustNewWhitelist(".*"),
+		Methods: MustNewWhitelist("(?i)GET|POST"),
 	},
 }
 
@@ -221,7 +221,7 @@ func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType {
 
 		wl := types.ToStringSlicePreserveString(data)
 
-		return NewWhitelist(wl...), nil
+		return NewWhitelist(wl...)
 
 	}
 }
diff --git a/config/security/whitelist.go b/config/security/whitelist.go
index 72a80da2e..92eb3102f 100644
--- a/config/security/whitelist.go
+++ b/config/security/whitelist.go
@@ -45,9 +45,9 @@ func (w Whitelist) MarshalJSON() ([]byte, error) {
 // NewWhitelist creates a new Whitelist from zero or more patterns.
 // An empty patterns list or a pattern with the value 'none' will create
 // a whitelist that will Accept none.
-func NewWhitelist(patterns ...string) Whitelist {
+func NewWhitelist(patterns ...string) (Whitelist, error) {
 	if len(patterns) == 0 {
-		return Whitelist{acceptNone: true}
+		return Whitelist{acceptNone: true}, nil
 	}
 
 	var acceptSome bool
@@ -68,7 +68,7 @@ func NewWhitelist(patterns ...string) Whitelist {
 	if !acceptSome {
 		return Whitelist{
 			acceptNone: true,
-		}
+		}, nil
 	}
 
 	var patternsr []*regexp.Regexp
@@ -78,10 +78,23 @@ func NewWhitelist(patterns ...string) Whitelist {
 		if p == "" {
 			continue
 		}
-		patternsr = append(patternsr, regexp.MustCompile(p))
+		re, err := regexp.Compile(p)
+		if err != nil {
+			return Whitelist{}, fmt.Errorf("failed to compile whitelist pattern %q: %w", p, err)
+		}
+		patternsr = append(patternsr, re)
 	}
 
-	return Whitelist{patterns: patternsr, patternsStrings: patternsStrings}
+	return Whitelist{patterns: patternsr, patternsStrings: patternsStrings}, nil
+}
+
+// MustNewWhitelist creates a new Whitelist from zero or more patterns and panics on error.
+func MustNewWhitelist(patterns ...string) Whitelist {
+	w, err := NewWhitelist(patterns...)
+	if err != nil {
+		panic(err)
+	}
+	return w
 }
 
 // Accept reports whether name is whitelisted.
diff --git a/docs/content/en/content-management/urls.md b/docs/content/en/content-management/urls.md
index 48de64966..25f6f19ed 100644
--- a/docs/content/en/content-management/urls.md
+++ b/docs/content/en/content-management/urls.md
@@ -89,67 +89,149 @@ If you set both `slug` and `url` in front matter, the `url` value takes preceden
 
 ### Permalinks
 
-In your site configuration, set a URL pattern for regular pages within a top-level section. This is recursive, affecting descendant regular pages.
+In your site configuration, define a URL pattern for each top-level section. Each URL pattern can target a given language and/or [page kind].
 
-{{% note %}}
-The  `permalinks` defined in your site configuration do not apply to section pages. To adjust the URL for section pages, set `url` in front matter.
-{{% /note %}}
+Front matter `url` values override the URL patterns defined in the `permalinks` section of your site configuration.
 
-#### Examples {#permalinks-examples}
+[page kind]: https://gohugo.io/templates/section-templates/#page-kinds
+
+#### Monolingual examples {#permalinks-monolingual-examples}
 
 With this content structure:
 
 ```text
 content/
 ├── posts/
-│   ├── _index.md
-│   ├── post-1.md
-│   └── post-2.md
+│   ├── bash-in-slow-motion.md
+│   └── tls-in-a-nutshell.md
+├── tutorials/
+│   ├── git-for-beginners.md
+│   └── javascript-bundling-with-hugo.md
 └── _index.md
 ```
 
-Create a date-based hierarchy, recursively, for regular pages within the `posts` section:
+Render tutorials under "training", and render the posts under "articles" with a date-base hierarchy:
 
 {{< code-toggle file="hugo" copy=false >}}
-[permalinks]
-  posts = '/posts/:year/:month/:title/'
+[permalinks.page]
+posts = '/articles/:year/:month/:slug/'
+tutorials = '/training/:slug/'
+[permalinks.section]
+posts = '/articles/'
+tutorials = '/training/'
 {{< /code-toggle >}}
 
 The structure of the published site will be:
 
 ```text
 public/
-├── posts/
-│   ├── 2023/
-│   │   └── 03/
-│   │       ├── post-1/
-│   │       │   └── index.html
-│   │       └── post-2/
-│   │           └── index.html
-│   └── index.html
-├── favicon.ico
+├── articles/
+│   ├── 2023/
+│   │   ├── 04/
+│   │   │   └── bash-in-slow-motion/
+│   │   │       └── index.html
+│   │   └── 06/
+│   │       └── tls-in-a-nutshell/
+│   │           └── index.html
+│   └── index.html
+├── training/
+│   ├── git-for-beginners/
+│   │   └── index.html
+│   ├── javascript-bundling-with-hugo/
+│   │   └── index.html
+│   └── index.html
 └── index.html
 ```
 
 To create a date-based hierarchy for regular pages in the content root:
 
 {{< code-toggle file="hugo" copy=false >}}
-[permalinks]
-  '/' = '/:year/:month/:title/'
+[permalinks.page]
+"/" = "/:year/:month/:slug/"
 {{< /code-toggle >}}
 
-{{% note %}}
-A URL pattern defined for the content root is not recursive.
-{{% /note %}}
+Use the same approach with taxonomy terms. For example, to omit the taxonomy segment of the URL:
+
+{{< code-toggle file="hugo" copy=false >}}
+[permalinks.term]
+'tags' = '/:slug/'
+{{< /code-toggle >}}
+
+#### Multilingual example {#permalinks-multilingual-example}
 
-Use the same approach with taxonomies. For example, to omit the taxonomy segment of the URL:
+Use the `permalinks` configuration as a component of your localization strategy.
+
+With this content structure:
+
+```text
+content/
+├── de/
+│   ├── books/
+│   │   ├── les-miserables.md
+│   │   └── the-hunchback-of-notre-dame.md
+│   └── _index.md
+└── en/
+    ├── books/
+    │   ├── les-miserables.md
+    │   └── the-hunchback-of-notre-dame.md
+    └── _index.md
+```
+
+And this site configuration:
 
 {{< code-toggle file="hugo" copy=false >}}
-[permalinks]
-  'tags' = '/:title/'
+defaultContentLanguage = 'en'
+defaultContentLanguageInSubdir = true
+
+[languages.en]
+contentDir = 'content/en'
+languageCode = 'en-US'
+languageDirection = 'ltr'
+languageName = 'English'
+weight = 1
+
+[languages.en.permalinks.page]
+books = "/books/:slug/"
+
+[languages.en.permalinks.section]
+books = "/books/"
+
+[languages.es]
+contentDir = 'content/de'
+languageCode = 'es-ES'
+languageDirection = 'ltr'
+languageName = 'Español'
+weight = 2
+
+[languages.es.permalinks.page]
+books = "/libros/:slug/"
+
+[languages.es.permalinks.section]
+books = "/libros/"
 {{< /code-toggle >}}
 
-Front matter `url` values take precedence over URL patterns defined in `permalinks`.
+The structure of the published site will be:
+
+```text
+public/
+├── en/
+│   ├── books/
+│   │   ├── les-miserables/
+│   │   │   └── index.html
+│   │   ├── the-hunchback-of-notre-dame/
+│   │   │   └── index.html
+│   │   └── index.html
+│   └── index.html
+├── es/
+│   ├── libros/
+│   │   ├── les-miserables/
+│   │   │   └── index.html
+│   │   ├── the-hunchback-of-notre-dame/
+│   │   │   └── index.html
+│   │   └── index.html
+│   └── index.html
+└── index.html
+````
 
 #### Tokens
 
diff --git a/go.mod b/go.mod
index 58d7b1bd2..2d3d10ea5 100644
--- a/go.mod
+++ b/go.mod
@@ -25,7 +25,7 @@ require (
 	github.com/cli/safeexec v1.0.1
 	github.com/disintegration/gift v1.2.1
 	github.com/dustin/go-humanize v1.0.1
-	github.com/evanw/esbuild v0.18.5
+	github.com/evanw/esbuild v0.18.10
 	github.com/fatih/color v1.15.0
 	github.com/fortytw2/leaktest v1.3.0
 	github.com/frankban/quicktest v1.14.5
diff --git a/go.sum b/go.sum
index f3eda4d91..2d17831da 100644
--- a/go.sum
+++ b/go.sum
@@ -260,8 +260,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanw/esbuild v0.18.5 h1:B474TK1dD7ZQJOEE7BKqCmTerOYEIzxVH35UY4MOM0s=
-github.com/evanw/esbuild v0.18.5/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk=
+github.com/evanw/esbuild v0.18.10 h1:QPpRUR5aOMQ4LQQDA9zZFpSuyT8+luBoyvPqVy8Vuv0=
+github.com/evanw/esbuild v0.18.10/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk=
 github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
 github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index c552b9de8..ea73c493f 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -143,6 +143,21 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
 		if err := h.render(infol, conf); err != nil {
 			h.SendError(fmt.Errorf("render: %w", err))
 		}
+
+		if h.Configs.Base.LogPathWarnings {
+			// We need to do this before any post processing, as that may write to the same files twice
+			// and create false positives.
+			hugofs.WalkFilesystems(h.Fs.PublishDir, func(fs afero.Fs) bool {
+				if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
+					dupes := dfs.ReportDuplicates()
+					if dupes != "" {
+						h.Log.Warnln("Duplicate target paths:", dupes)
+					}
+				}
+				return false
+			})
+		}
+
 		if err := h.postProcess(infol); err != nil {
 			h.SendError(fmt.Errorf("postProcess: %w", err))
 		}


Test patch:
diff --git a/common/collections/append_test.go b/common/collections/append_test.go
index 415eb2f25..3c2aab2db 100644
--- a/common/collections/append_test.go
+++ b/common/collections/append_test.go
@@ -74,6 +74,9 @@ func TestAppend(t *testing.T) {
 			[]any{"c"},
 			false,
 		},
+		{[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}},
+		{[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}},
+		{[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}},
 	} {
 
 		result, err := Append(test.start, test.addend...)
diff --git a/config/security/whitelist_test.go b/config/security/whitelist_test.go
index 5c4196dff..89d1bc2b1 100644
--- a/config/security/whitelist_test.go
+++ b/config/security/whitelist_test.go
@@ -24,21 +24,21 @@ func TestWhitelist(t *testing.T) {
 	c := qt.New(t)
 
 	c.Run("none", func(c *qt.C) {
-		c.Assert(NewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
-		c.Assert(NewWhitelist().Accept("foo"), qt.IsFalse)
-		c.Assert(NewWhitelist("").Accept("foo"), qt.IsFalse)
-		c.Assert(NewWhitelist("  ", " ").Accept("foo"), qt.IsFalse)
+		c.Assert(MustNewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
+		c.Assert(MustNewWhitelist().Accept("foo"), qt.IsFalse)
+		c.Assert(MustNewWhitelist("").Accept("foo"), qt.IsFalse)
+		c.Assert(MustNewWhitelist("  ", " ").Accept("foo"), qt.IsFalse)
 		c.Assert(Whitelist{}.Accept("foo"), qt.IsFalse)
 	})
 
 	c.Run("One", func(c *qt.C) {
-		w := NewWhitelist("^foo.*")
+		w := MustNewWhitelist("^foo.*")
 		c.Assert(w.Accept("foo"), qt.IsTrue)
 		c.Assert(w.Accept("mfoo"), qt.IsFalse)
 	})
 
 	c.Run("Multiple", func(c *qt.C) {
-		w := NewWhitelist("^foo.*", "^bar.*")
+		w := MustNewWhitelist("^foo.*", "^bar.*")
 		c.Assert(w.Accept("foo"), qt.IsTrue)
 		c.Assert(w.Accept("bar"), qt.IsTrue)
 		c.Assert(w.Accept("mbar"), qt.IsFalse)
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index cb34cb28b..9c40fa7d0 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -381,7 +381,8 @@ func (s *IntegrationTestBuilder) initBuilder() error {
 			s.Assert(os.Chdir(s.Cfg.WorkingDir), qt.IsNil)
 			s.C.Cleanup(func() { os.Chdir(wd) })
 			sc := security.DefaultConfig
-			sc.Exec.Allow = security.NewWhitelist("npm")
+			sc.Exec.Allow, err = security.NewWhitelist("npm")
+			s.Assert(err, qt.IsNil)
 			ex := hexec.New(sc)
 			command, err := ex.New("npm", "install")
 			s.Assert(err, qt.IsNil)
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index e1fd537a8..73920bd49 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -834,7 +834,9 @@ func (s *sitesBuilder) GetPageRel(p page.Page, ref string) page.Page {
 
 func (s *sitesBuilder) NpmInstall() hexec.Runner {
 	sc := security.DefaultConfig
-	sc.Exec.Allow = security.NewWhitelist("npm")
+	var err error
+	sc.Exec.Allow, err = security.NewWhitelist("npm")
+	s.Assert(err, qt.IsNil)
 	ex := hexec.New(sc)
 	command, err := ex.New("npm", "install")
 	s.Assert(err, qt.IsNil)
diff --git a/markup/pandoc/convert_test.go b/markup/pandoc/convert_test.go
index 6a1535946..dec30c410 100644
--- a/markup/pandoc/convert_test.go
+++ b/markup/pandoc/convert_test.go
@@ -31,7 +31,9 @@ func TestConvert(t *testing.T) {
 	}
 	c := qt.New(t)
 	sc := security.DefaultConfig
-	sc.Exec.Allow = security.NewWhitelist("pandoc")
+	var err error
+	sc.Exec.Allow, err = security.NewWhitelist("pandoc")
+	c.Assert(err, qt.IsNil)
 	p, err := Provider.New(converter.ProviderConfig{Exec: hexec.New(sc), Logger: loggers.NewDefault()})
 	c.Assert(err, qt.IsNil)
 	conv, err := p.New(converter.DocumentContext{})
diff --git a/markup/rst/convert_test.go b/markup/rst/convert_test.go
index 9e98d0405..1897e650f 100644
--- a/markup/rst/convert_test.go
+++ b/markup/rst/convert_test.go
@@ -31,7 +31,7 @@ func TestConvert(t *testing.T) {
 	}
 	c := qt.New(t)
 	sc := security.DefaultConfig
-	sc.Exec.Allow = security.NewWhitelist("rst", "python")
+	sc.Exec.Allow = security.MustNewWhitelist("rst", "python")
 
 	p, err := Provider.New(
 		converter.ProviderConfig{
diff --git a/testscripts/commands/hugo__path-warnings-postprocess.txt b/testscripts/commands/hugo__path-warnings-postprocess.txt
new file mode 100644
index 000000000..0677da084
--- /dev/null
+++ b/testscripts/commands/hugo__path-warnings-postprocess.txt
@@ -0,0 +1,20 @@
+hugo --printPathWarnings
+
+! stdout 'Duplicate'
+
+-- hugo.toml --
+-- assets/css/styles.css --
+body {
+  background-color: #000;
+}
+-- content/p1.md --
+-- content/p2.md --
+-- content/p3.md --
+-- layouts/index.html --
+Home.
+-- layouts/_default/single.html --
+{{ $css := resources.Get "css/styles.css" }}
+{{ $css := $css | minify | fingerprint | resources.PostProcess  }}
+CSS: {{ $css.RelPermalink }}
+{{ .Title }}
+
diff --git a/testscripts/commands/hugo__path-warnings.txt b/testscripts/commands/hugo__path-warnings.txt
new file mode 100644
index 000000000..f7e3acd95
--- /dev/null
+++ b/testscripts/commands/hugo__path-warnings.txt
@@ -0,0 +1,26 @@
+hugo --printPathWarnings
+
+stdout 'Duplicate'
+
+-- hugo.toml --
+-- assets/css/styles.css --
+body {
+  background-color: #000;
+}
+-- content/p1.md --
+---
+url: /p1/
+---
+-- content/p2.md --
+---
+url: /p1/
+---
+-- content/p3.md --
+---
+url: /p1/
+---
+-- layouts/index.html --
+Home.
+-- layouts/_default/single.html --
+Single.
+
diff --git a/testscripts/commands/new.txt b/testscripts/commands/new.txt
index 92cc3b7da..4c77104b2 100644
--- a/testscripts/commands/new.txt
+++ b/testscripts/commands/new.txt
@@ -10,7 +10,7 @@ cd mysite
 checkfile hugo.toml
 
 hugo new theme -h
-stdout 'Create a new site in the provided directory'
+stdout 'Create a new theme \(skeleton\) called \[name\] in ./themes'
 hugo new theme mytheme
 stdout 'Creating theme'
 cd themes
diff --git a/tpl/collections/integration_test.go b/tpl/collections/integration_test.go
index da1d6e488..829aee355 100644
--- a/tpl/collections/integration_test.go
+++ b/tpl/collections/integration_test.go
@@ -104,3 +104,63 @@ func TestAppendSliceToASliceOfSlices(t *testing.T) {
 	}
 
 }
+
+func TestAppendNilToSlice(t *testing.T) {
+
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+-- layouts/index.html --
+{{ $obj := (slice "a") }}
+{{ $obj = $obj | append nil }}
+
+{{ $obj }}
+
+
+  `
+
+	for i := 0; i < 4; i++ {
+
+		b := hugolib.NewIntegrationTestBuilder(
+			hugolib.IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/index.html", "[a &lt;nil&gt;]")
+
+	}
+
+}
+
+func TestAppendNilsToSliceWithNils(t *testing.T) {
+
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+-- layouts/index.html --
+{{ $obj := (slice "a" nil "c") }}
+{{ $obj = $obj | append nil }}
+
+{{ $obj }}
+
+
+  `
+
+	for i := 0; i < 4; i++ {
+
+		b := hugolib.NewIntegrationTestBuilder(
+			hugolib.IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/index.html", "[a &lt;nil&gt; c &lt;nil&gt;]")
+
+	}
+
+}