Compare commits

...

138 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen 3fbcc4b7b3 Cache when not found in LookupLayout
Very visible when using the pprof mutex profiler.

```bash
name         old time/op    new time/op    delta
Baseline-10    58.4ms ± 1%    51.6ms ± 0%  -11.56%  (p=0.029 n=4+4)

name         old alloc/op   new alloc/op   delta
Baseline-10    64.3MB ± 0%    64.2MB ± 0%     ~     (p=0.114 n=4+4)

name         old allocs/op  new allocs/op  delta
Baseline-10      649k ± 0%      649k ± 0%     ~     (p=0.229 n=4+4)
```
2022-07-13 14:49:00 +02:00
Bjørn Erik Pedersen 223bf28004 parser/pageparser: Don't store the byte slices
On its own this change doesn't do any magic, but this is part of a bigger picture about making Hugo leaner in the
memory usage department.
2022-07-09 16:03:11 +02:00
Bjørn Erik Pedersen 72b0ccdb01 Make the baseline benchmark's test files stable
And add some assertions.
2022-06-30 12:36:55 +02:00
Bjørn Erik Pedersen 65e52a7f5c Merge commit 'bdf935d66c1f02dfc942a30e9fc00519bba3aacb' 2022-06-28 20:51:33 +02:00
Bjørn Erik Pedersen bdf935d66c Squashed 'docs/' changes from 30f32a624..e5aa641a6
e5aa641a6 remove erroneous foo
8307b474e Add hl_inline to highlight function documentation
8517b02d9 update GitHub 404 docs URL to prevent redirect
5c52c2715 remove aerobatic.com
3eb179558 Update syntax-highlighting.md
00cbe3f2f Check for nil Chroma .Aliases
45416515e netlify: Hugo 0.101.0
d5dcd1581 docs: Regen docshelper
4785e99b1 common: Add hugo.GoVersion
e679f1b7e resources/page: Add :slugorfilename attribute
f2cc72a1f Update spellcheck.yml (#1755)

git-subtree-dir: docs
git-subtree-split: e5aa641a62
2022-06-28 20:51:33 +02:00
Anthony Fok 8ebcaa5394 Accept vendor-specified build date if .git/ is unavailable
Fixes #10053
2022-06-28 20:51:18 +02:00
Anthony Fok 241481931f snap: Replace mage with "go build" and set VendorInfo=snap 2022-06-28 04:09:50 -06:00
Anthony Fok 5caed8a71f snap: Use interface names etc-gitconfig and gitconfig, Take 2
as recommended by @alexmurray to keep consistency with other snaps; see
https://forum.snapcraft.io/t/system-files-and-personal-files-request-for-hugo/30598

I neglected to make the rename of system-gitconfig → etc-gitconfig and
user-gitconfig → gitconfig to the apps.plugs section in commit fd3953c,
hence this commit to fix the store upload scan failure:

  interface 'system-gitconfig' not found in base declaration
  interface 'user-gitconfig' not found in base declaration
  unknown plugs interface name reference 'system-gitconfig'
  unknown plugs interface name reference 'user-gitconfig'

Special thanks to Alex Murray and Igor Ljubuncic for approving the
system-files and personal-files request for Hugo.

Fixes #6226
2022-06-27 09:27:30 -06:00
Bjørn Erik Pedersen d1278f696a Extract the baseline benchmark to a test 2022-06-26 09:56:01 +02:00
Bjørn Erik Pedersen 92f31ae63b Add a baseline benchmark 2022-06-25 18:26:40 +02:00
Anthony Fok fd3953c18c snap: Use interface names etc-gitconfig and gitconfig
as recommended by @alexmurray to keep consistency with other snaps; see
https://forum.snapcraft.io/t/system-files-and-personal-files-request-for-hugo/30598/2

One closer step towards fixing #6226
2022-06-24 00:59:37 -06:00
hugoreleaser 15463f835b releaser: Prepare repository for 0.102.0-DEV
[ci skip]
2022-06-16 07:51:58 +00:00
hugoreleaser 466fa43c16 releaser: Bump versions for release of 0.101.0
[ci skip]
2022-06-16 07:09:16 +00:00
hugoreleaser 6072ce0bcd releaser: Add release notes for 0.101.0
[ci skip]
2022-06-16 07:09:16 +00:00
Bjørn Erik Pedersen 2c5943ddc6 build: Update to Go 1.18.3
Fixes #9964
2022-06-16 08:27:08 +02:00
Bjørn Erik Pedersen 0cb459a20a docs: Regen docshelper 2022-06-16 07:23:38 +02:00
Bjørn Erik Pedersen 475f87f685 Squashed 'docs/' changes from 96fdc246c..30f32a624
30f32a624 Update scss-sass.md
503130186 Update scss-sass.md
cbfaa6704 Remove references to outdated Blackfriday markdown processor (#1748)
e43948d26 netlify: Hugo 0.100.2
aaabe0ee9 Merge branch 'tempv0.100.2'
bc615f257 tpl/path: Add path.BaseName function
5dddbf95e Add Chinese translation for _documentation and _index.md
b1e4f3200 Add translation for search tools
23281633a Update introduction.md
3abb5fb5d netlify: Hugo 0.100.1
644ed3b01 Update faq.md
54f377051 Add some lines about resources.Copy
82cf08355 Remove most Blackfriday references
bfbe5aaf3 netlify: Update to Hugo 0.100.0
8b6f8577f docs: Regen CLI docs
858a5d39c docs: Regen docs helper
5037d504f Merge commit 'e4bfe59c4e043c92d3992587d8c64d264b262a22'
03ca7be5b Fix indentation in highlight shortcode

git-subtree-dir: docs
git-subtree-split: 30f32a6243
2022-06-16 07:22:11 +02:00
Bjørn Erik Pedersen 604cfffc5b Merge commit '475f87f685439de0f907a9ffc29bfd1361eb1c59' 2022-06-16 07:22:11 +02:00
Bjørn Erik Pedersen d863dde6c6 markup/highlight: Add hl_inline option
Closes #9442
Closes #9635
Closes #9638
2022-06-15 20:04:20 +02:00
Bjørn Erik Pedersen 580b214a4c deps: Update github.com/alecthomas/chroma/v2 v2.1.0 => v2.2.0 2022-06-15 20:04:20 +02:00
dependabot[bot] ddb9547085 build(deps): bump github.com/clbanning/mxj/v2 from 2.5.5 to 2.5.6
Bumps [github.com/clbanning/mxj/v2](https://github.com/clbanning/mxj) from 2.5.5 to 2.5.6.
- [Release notes](https://github.com/clbanning/mxj/releases)
- [Commits](https://github.com/clbanning/mxj/compare/v2.5.5...v2.5.6)

---
updated-dependencies:
- dependency-name: github.com/clbanning/mxj/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 14:19:26 +02:00
dependabot[bot] 288b0fb15a build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.1 to 2.0.2
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 14:17:56 +02:00
dependabot[bot] 3e1344632d build(deps): bump golang.org/x/tools from 0.1.10 to 0.1.11
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.10 to 0.1.11.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.10...v0.1.11)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 12:12:23 +02:00
dependabot[bot] 7a9ce0eca5 build(deps): bump github.com/tdewolff/minify/v2 from 2.11.5 to 2.11.10
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.11.5 to 2.11.10.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.11.5...v2.11.10)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 12:11:23 +02:00
dependabot[bot] f2ba0cc8c5 build(deps): bump github.com/evanw/esbuild from 0.14.42 to 0.14.43
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.42 to 0.14.43.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.42...v0.14.43)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 12:10:55 +02:00
dependabot[bot] 62ceaabdce build(deps): bump github.com/getkin/kin-openapi from 0.94.0 to 0.97.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.94.0 to 0.97.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.94.0...v0.97.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 10:36:59 +02:00
Bjørn Erik Pedersen 35fa192838 deps: Udpate to github.com/alecthomas/chroma/v2
Fixes #9932
Fixes #9931
2022-06-14 14:08:40 +02:00
Khayyam Saleem 09ac733381 common: Add hugo.GoVersion
Closes #9849. This enables `hugo.GoVersion` in templates to access the
version of Go that the Hugo binary was built with.
2022-06-14 09:48:45 +02:00
Bjørn Erik Pedersen 66da1b7b2f resources: Panic on Copy of Resource with .Err
Fixes #10006
2022-06-14 09:47:41 +02:00
Dawid Potocki 5a9ecb82a3 resources/page: Add :slugorfilename attribute
Fixes #4739, #385
2022-06-13 21:45:03 +02:00
Bjørn Erik Pedersen cbc35c48d2 Respect NO_COLOR
Fixes #10004
2022-06-13 18:07:37 +02:00
Andreas Deininger 44f3c07969 readme: Update dependency list 2022-06-13 13:16:45 +02:00
Bjørn Erik Pedersen a5a4422aae Fix relURL with leading slash when baseURL includes a subdirectory
Fixes #9994
2022-06-13 11:40:25 +02:00
Bjørn Erik Pedersen 617e094482 js: Resolve index.esm.js
Same logic as for `index.{js,ts...}` files applies; if both `index.esm.js` and `index.js` exists (unlikely), you need to use the name
with extension when importing, else the `index.js` will win.

Fixes #8631
2022-06-12 20:09:37 +02:00
Bjørn Erik Pedersen cf12fa6161 Add animated GIF support
Note that this is for GIFs only (and not Webp).

Fixes #5030
2022-06-12 18:17:08 +02:00
Bjørn Erik Pedersen 2e1c81770a resources: Add a Gif source file to golden tests 2022-06-12 16:29:19 +02:00
hugoreleaser 4276075c78 releaser: Prepare repository for 0.101.0-DEV
[ci skip]
2022-06-08 11:07:37 +00:00
hugoreleaser d25cb2943f releaser: Bump versions for release of 0.100.2
[ci skip]
2022-06-08 10:25:57 +00:00
hugoreleaser 8b9bdc403d releaser: Add release notes for 0.100.2
[ci skip]
2022-06-08 10:25:57 +00:00
Bjørn Erik Pedersen 4e94d1db72 Update CONTRIBUTING.md 2022-06-07 17:22:17 +02:00
Bjørn Erik Pedersen 0566bbf7c7 Fix raw TOML dates in where/eq
Note that this has only been a problem with "raw dates" in TOML files in /data and similar. The predefined front matter
dates `.Date` etc. are converted to a Go Time and has worked fine even after upgrading to v2 of the go-toml lib.

Fixes #9979
2022-06-07 13:02:58 +02:00
Anthony Fok 534e7155bb deps: Update to github.com/pelletier/go-toml/v2 v2.0.1 2022-06-06 09:36:43 +02:00
Joe Mooring 953f215f32 tpl/path: Add path.BaseName function
Closes #9973
2022-06-06 09:36:16 +02:00
satotake 8e2fd55923 livereload: Use X-Forwarded-Host for Codespace
Codespace has 2 types of usage

1. in browser
2. vscode on local computer

As long as you select 2 (on local), Hugo handles livereload expectedly.
But if you use it in browser, Hugo does not reload on file change, as #9936 said.

This issue happens because `CheckOrigin` always fails.
Remote server could rewrite request host name.
Fix this by respecting `X-Forwarded-Host` header during origin checking

After merging this, you can preview changes lively with codespaece in browser.

```sh
hugo server --liveReloadPort 443
```

Close #9936
2022-06-05 11:16:24 +02:00
Bjørn Erik Pedersen 311b8008bf helpers: Fix panic with invalid defaultMarkdownHandler
Fixes #9968
2022-06-04 17:47:20 +02:00
Paul van Brouwershaven c7d5f9f067 resources: Register MediaTypes before build
Fixes #9971
2022-06-03 21:37:50 +02:00
hugoreleaser bfebd8c02c releaser: Prepare repository for 0.101.0-DEV
[ci skip]
2022-06-01 10:54:16 +00:00
hugoreleaser 0afb4866e3 releaser: Bump versions for release of 0.100.1
[ci skip]
2022-06-01 10:11:48 +00:00
hugoreleaser b1ec0c226f releaser: Add release notes for 0.100.1
[ci skip]
2022-06-01 10:11:48 +00:00
Bjørn Erik Pedersen 212d9e3017 Fix panic with markdownify/RenderString with shortcode on Page with no content file
Fixes #9959
2022-06-01 12:04:55 +02:00
hugoreleaser 4daac654d9 releaser: Prepare repository for 0.101.0-DEV
[ci skip]
2022-05-31 09:19:15 +00:00
hugoreleaser 27b077544d releaser: Bump versions for release of 0.100.0
[ci skip]
2022-05-31 08:37:12 +00:00
hugoreleaser 0f8343a2c2 releaser: Add release notes for 0.100.0
[ci skip]
2022-05-31 08:37:12 +00:00
Bjørn Erik Pedersen 3fcbee261a docs: Regen CLI docs 2022-05-31 10:24:01 +02:00
Bjørn Erik Pedersen db9d274275 docs: Regen docs helper 2022-05-31 10:23:20 +02:00
Bjørn Erik Pedersen 95baafeac4 Merge commit 'e4bfe59c4e043c92d3992587d8c64d264b262a22' 2022-05-31 10:22:05 +02:00
Bjørn Erik Pedersen e4bfe59c4e Squashed 'docs/' changes from 2d9da3a56..96fdc246c
96fdc246c Fix outdated i18n warnings flag mention on the multilingual page
39d197345 netlify: Hugo 0.99.1
fafd5070e Typo: Vriables > Variables
348c4e237 Update postcss.md
924fa76b6 netlify: Hugo 0.99.0
bb3f02ac1 Merge branch 'tempv0.99.0'
bdd0b6eaf Update usage.md
f562ff99d postcss: Fix import error handling
5d73f215f docs: Regen CLI docs

git-subtree-dir: docs
git-subtree-split: 96fdc246cc
2022-05-31 10:21:09 +02:00
Bjørn Erik Pedersen 6f7bf3f2d7 Fix indentation in highlight shortcode
This commit adds a new `.InnerDeindent` method to the shortcode context, which is `.Inner` with any
indendation removed. This is then used in the built-in `highlight` shortcode to prevent the extra
whitespace getting hightlighted.

Fixes #4717
2022-05-31 09:05:54 +02:00
Bjørn Erik Pedersen 9e904d756b Make .RenderString render shortcodes
Fixes #6703
2022-05-30 11:32:55 +02:00
Bjørn Erik Pedersen d2cfaede5b Improve shortcode indentation handling
* Record the leading whitespace (tabs, spaces) before the shortcode when parsing the page.
* Apply that indentation to the rendered result of shortcodes without inner content (where the user will apply indentation).

Fixes #9946
2022-05-30 11:32:55 +02:00
Bjørn Erik Pedersen 322d19a81f Add Markdown as an output format
The motivation behind this is not to make it easier to publish Markdown files, as that sounds unusual.

This is mainly meant for shortcodes that produces Markdown to be inlined.

You would do this by creating shortcodes with `*.md` suffix (e.g. `layouts/shortcodes/myshortcode.md`).

This output format is defined as plain text, and will use Go's much more lenient text template parser.

Updates #9821
2022-05-30 11:32:55 +02:00
dependabot[bot] 7cb484e121 build(deps): bump github.com/evanw/esbuild from 0.14.39 to 0.14.42
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.39 to 0.14.42.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.39...v0.14.42)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-30 11:32:04 +02:00
Bjørn Erik Pedersen 0b395f0b41 Run go mod tidy 2022-05-29 23:05:16 +02:00
Bjørn Erik Pedersen c1a83076bf Add a shortcode benchmark 2022-05-29 15:21:08 +02:00
Bjørn Erik Pedersen 0f8dc47037 Remove Blackfriday markdown engine
It has been deprecated for a long time, its v1 version is not maintained anymore, and there are many known issues. Goldmark should be
a mature replacement by now.

Closes #9934
2022-05-29 11:50:58 +02:00
Bjørn Erik Pedersen 3b478f50b7 Fix HasMenuCurrent and IsDescendant/IsAncestor when comparing to itself
There may be sites in the wild that depends on the faulty behaviour of IsDescendant/IsAncestor when comparing to itself, but

* The documentation and common sense says that a thing cannot be descendant or ancestor to itself.
* The bug introduced in `HasMenuCurrent` comes directly from that confusion.

Fixes #9846
2022-05-28 10:56:54 +02:00
dependabot[bot] f343b8eb78 build(deps): bump github.com/sanity-io/litter from 1.5.4 to 1.5.5
Bumps [github.com/sanity-io/litter](https://github.com/sanity-io/litter) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/sanity-io/litter/releases)
- [Changelog](https://github.com/sanity-io/litter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sanity-io/litter/compare/v1.5.4...v1.5.5)

---
updated-dependencies:
- dependency-name: github.com/sanity-io/litter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-27 20:04:35 +02:00
Bjørn Erik Pedersen 60ede146b6 deps: Update to github.com/tdewolff/minify/v2 v2.11.5 2022-05-27 19:12:20 +02:00
Bjørn Erik Pedersen dd9eaf19fd Don't use the baseURL /path as part of the resource cache key
As that prevents Hugo projects with sub paths in their `baseURL` to use themes with cached resources.

Fixes #9787
2022-05-27 19:11:16 +02:00
Bjørn Erik Pedersen 46a2ea6d0d postcss: Make the resource cache key more stable
By using the input map as the basis, which means the hash will not change if we add/rename/remove options.

This happened in Hugo 0.99, as we added a new options. This is unortunate.

Unfortunately this means that the cache keys for PostCSS will change one more time in 0.100, but will be stable going forward.

Note that we have implemented this pattern in all the other resource transformers.

Updates #9787
2022-05-27 18:38:44 +02:00
Bjørn Erik Pedersen 653ab2cc1f commands: Fix case where languages cannot be configured
There are some commands that needs to complete without a complete configuration.
2022-05-27 13:34:20 +02:00
Bjørn Erik Pedersen 52edea0fec github: Set HUGO_BUILD_TAGS: extended when running tests
Also fix TestDecodeConfig/Basic which started to fail in the extended build in 0.99.1.

Closes #9935
2022-05-27 11:55:03 +02:00
Cameron Moore 6a5acd753a metrics: Fix divide by zero error
Under certain conditions, `howSimilarString` could reach a divide-by-
zero situation which causes bogus values to print in the cache potential
column of the template hints output.  This situation essentially causes
a `int(math.NaN())` value to be returned and hilarity ensues thereafter.
2022-05-26 10:39:29 +02:00
Bjørn Erik Pedersen 805b21555e Fix error message when PostCSS config file is not found
Fixes #9927
2022-05-25 21:21:28 +02:00
Bjørn Erik Pedersen 8ca7052528 server: Skip watching dirs in ignoreFiles
Fixes #9838
2022-05-25 18:31:00 +02:00
Bjørn Erik Pedersen bb232a351a resources: Improve error message on .Resize etc. on SVGs
Fixes #9875
2022-05-25 18:06:20 +02:00
Bjørn Erik Pedersen 3854a6fa6c Fix Plainify edge cases
This commit replaces the main part of `helpers.StripHTML` with Go's implementation in its html/template package.

It's a little slower, but correctness is more important:

```bash
BenchmarkStripHTMLOld-10    	  680316	      1764 ns/op	     728 B/op	       4 allocs/op
BenchmarkStripHTMLNew-10    	  384520	      3099 ns/op	    2089 B/op	      10 allocs/op
```

Fixes #9199
Fixes #9909
Closes #9410
2022-05-25 17:55:23 +02:00
Bjørn Erik Pedersen cd0112a05a Add resources.Copy
Implemented by most Resource objects, but not Page (for now).

Fixes #9313
2022-05-25 10:35:31 +02:00
satotake 6f7fbe03b1 basefs: add noBuildLock flag
If the flag is enabled, `.hugo_build.lock` will not be created.
This ensures safe running on read-only filesystem etc.
Close #9780
2022-05-24 13:57:23 +02:00
satotake 2fc2e9c871 import: Fix importing jekyll site
Just use `afero.OsFs` without building site.
Close #9817
2022-05-21 23:44:40 +02:00
hugoreleaser e164834f0a releaser: Prepare repository for 0.100.0-DEV
[ci skip]
2022-05-18 12:01:09 +00:00
hugoreleaser d524067382 releaser: Bump versions for release of 0.99.1
[ci skip]
2022-05-18 11:18:14 +00:00
hugoreleaser 31ce89f7f4 releaser: Add release notes for 0.99.1
[ci skip]
2022-05-18 11:18:14 +00:00
Bjørn Erik Pedersen ee55fde5e5 releaser: Fix version replacement 2022-05-18 13:12:42 +02:00
Bjørn Erik Pedersen 2f9eac480f server: Fix multihost crash
As introduced in v0.99.0.

Fixes #9901
2022-05-18 11:10:04 +02:00
Bjørn Erik Pedersen 3a8189ee93 Update stale.yml 2022-05-17 13:57:01 +02:00
hugoreleaser 7bc3401eb5 common/hugo: Fix version logic
[ci skip]
2022-05-16 13:01:52 +02:00
hugoreleaser 1de333e7a3 releaser: Bump versions for release of 0.99.0
[ci skip]
2022-05-16 08:10:56 +00:00
hugoreleaser 35cb6eefb2 releaser: Add release notes for 0.99.0
[ci skip]
2022-05-16 08:10:55 +00:00
Bjørn Erik Pedersen 657d1a2d95 server: Refresh the error template
We cannot cache it forever, as that will not allow the end user to edit and preview it.
2022-05-16 09:24:30 +02:00
Bjørn Erik Pedersen 87a22eb6d6 server: Fix SIGINT handling after loading bad configuration
Also fix the config error messages.

Fixes #9664
2022-05-15 22:58:05 +02:00
Bjørn Erik Pedersen fc9f315d86 Improve SASS errors
Fixes #9897
2022-05-15 20:25:25 +02:00
Bjørn Erik Pedersen 4b189d8fd9 postcss: Fix import error handling
Note that we will now fail if `inlineImports` is enabled and we cannot resolve an import.

You can work around this by either:

* Use url imports or imports with media queries.
* Set `skipInlineImportsNotFound=true` in the options

Also get the argument order in the different NewFileError* funcs in line.

Fixes #9895
2022-05-15 20:25:25 +02:00
dependabot[bot] c2fa0a3320 build(deps): bump github.com/fsnotify/fsnotify from 1.5.3 to 1.5.4
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/fsnotify/fsnotify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-14 15:01:01 +02:00
Bjørn Erik Pedersen 48ea24f89a common/herrors: Remove unused struct 2022-05-14 13:56:28 +02:00
dependabot[bot] 9f563856c1 build(deps): bump github.com/evanw/esbuild from 0.14.38 to 0.14.39
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.38 to 0.14.39.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.38...v0.14.39)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-14 13:45:11 +02:00
Bjørn Erik Pedersen 5c96bda70a errors: Misc improvements
* Redo the server error template
* Always add the content file context if relevant
* Remove some now superflous error string matching
* Move the server error template to _server/error.html
* Add file context (with position) to codeblock render blocks
* Improve JS build errors

Fixes #9892
Fixes #9891
Fixes #9893
2022-05-14 13:40:56 +02:00
Bjørn Erik Pedersen 4a96df96d9 server: Always rebuild the files involved in an error
Fixes #9884
2022-05-14 13:40:56 +02:00
Bjørn Erik Pedersen e8537e6dd0 postcss: Fix line numbers in error messages
Fixes #9880
2022-05-14 13:40:55 +02:00
Bjørn Erik Pedersen 2fbdee7268 Update CONTRIBUTING.md
Add some info about commit subject prefixes.
2022-05-13 11:42:12 +02:00
Bjørn Erik Pedersen 91fe1b6c69 js: Bump test dependency
To work around a cache issue.
2022-05-13 11:08:23 +02:00
Bjørn Erik Pedersen 7de629121d deps: Update github.com/spf13/cast v1.4.1 => v1.5.0 2022-05-11 17:03:28 +02:00
Bjørn Erik Pedersen 9d7f166244 hugolib: Check for nil in shouldRender 2022-05-09 15:53:25 +02:00
Bjørn Erik Pedersen 51f08b0b6a Revise the use of htime.Since/htime.Now
We cannot (also, it doesn't add any value)  use that when the `clock` is set,

* To measure time (before that global is set)
* To compare file timestamps re cache eviction

Fixes #9868
2022-05-09 14:18:40 +02:00
Bjørn Erik Pedersen 860c51c314 tpl/collections: Make sort stable
Fixes #9865
2022-05-08 20:45:54 +02:00
Bjørn Erik Pedersen 855e5869c6 docs: Regen CLI docs 2022-05-08 16:57:29 +02:00
Bjørn Erik Pedersen 327aaed6d8 Squashed 'docs/' changes from 7030fe3a2..2d9da3a56
2d9da3a56 Update build-options.md
5d910e24d Update introduction.md
111482a8d netlify: Hugo 0.98.0
d5505ca6f docs: Regen docs helper

git-subtree-dir: docs
git-subtree-split: 2d9da3a563
2022-05-08 16:56:42 +02:00
Bjørn Erik Pedersen 1c7759028e Merge commit '327aaed6d8ca57d8e5e3acb99ff53402ff1c556d' 2022-05-08 16:56:42 +02:00
Bjørn Erik Pedersen 35c88a7f90 Use configured timeZone for the clock
And some other related adjustments.

Updates #8787
2022-05-08 16:56:26 +02:00
satotake e77ca3c105 Add clock cli flag
Close #8787
2022-05-08 16:56:26 +02:00
Bjørn Erik Pedersen f2946da9e8 Improve error messages, esp. when the server is running
* Add file context to minifier errors when publishing
* Misc fixes (see issues)
* Allow custom server error template in layouts/server/error.html

To get to this, this commit also cleans up and simplifies the code surrounding errors and files. This also removes the usage of `github.com/pkg/errors`, mostly because of https://github.com/pkg/errors/issues/223 -- but also because most of this is now built-in to Go.

Fixes #9852
Fixes #9857
Fixes #9863
2022-05-06 19:43:22 +02:00
Bjørn Erik Pedersen 6eea32bd6b tpl: Improve godoc 2022-05-06 19:43:22 +02:00
nathannaveen a6d545854a github: Add permissions to test action 2022-05-03 09:23:47 +02:00
Bjørn Erik Pedersen e5f2173169 tpl/crypto: Add example for FNV32a 2022-04-28 17:47:17 +02:00
hugoreleaser 89c1655ec8 releaser: Prepare repository for 0.99.0-DEV
[ci skip]
2022-04-28 11:02:45 +00:00
hugoreleaser 165d299cde releaser: Bump versions for release of 0.98.0
[ci skip]
2022-04-28 10:23:30 +00:00
hugoreleaser e94dc6710a releaser: Add release notes for 0.98.0
[ci skip]
2022-04-28 10:23:30 +00:00
Bjørn Erik Pedersen a4fff5753f docs: Regen docs helper 2022-04-28 11:53:03 +02:00
Bjørn Erik Pedersen 3902f9a476 Squashed 'docs/' changes from 4c5edacfe..7030fe3a2
7030fe3a2 Remove .hvm file
dd0f98831 Add details for DigitalOcean (#1730)
ff0c8a6e7 netlify: Hugo 0.97.3
e254724a7 Delete hosting-on-bitbucket.md (#1723)
f4304fb7e Remove references to mmark (#1727)
255319023 remove broken link to codeplex.com (#1725)
06cc3856b delete broken link (#1724)
ec80dc069 Revert "netlify: Hugo 0.97.2"
211bad5de netlify: Hugo 0.97.2
cd549bca9 Revert "netlify: Update to Hugo 0.97.1" (#1722)
1039a0a48 netlify: Update to Hugo 0.97.1
a28e0d0d3 Update AddDate method
9077b443f Update lists.md
6b174329a Update lists.md
d87d9e296 netlify: Hugo 0.97.0
fa7db7c7e Merge branch 'tempv0.97.0'
dd6df54b5 Fix broken anchor links (#1717)
6b6743976 Remove StackImpact showcase
d00a7eda9 Fix for Cloudflare docs location (#1716)
890866722 Add git required FAQ & remove ZIP from Quick Start (#1713)
73e82d911 resources: Add `key` to reources.GetRemote options map
6795cd2a1 Correct possible typos re: Alias robots usage (#1165)
efd5d186a Hosting on Cloudflare Pages (#1709)
9239c5f2a deps: Update github.com/tdewolff/minify/v2 v2.10.0 => v2.11.0
859573bc8 Merge commit 'ec920363cdeb687c8bcac9c242767d366fb058cb'
21f108f12 docs: Regen CLI docs
9b6e7afd2 Add environment as a new filter to _cascade.target

git-subtree-dir: docs
git-subtree-split: 7030fe3a2e
2022-04-28 11:52:15 +02:00
Bjørn Erik Pedersen 4852a37653 Merge commit '3902f9a4767fe6e62ac5146728d8311b8cd227e0' 2022-04-28 11:52:15 +02:00
Bjørn Erik Pedersen fa80fe3c8a Some godoc adjustments and image struct renames 2022-04-27 23:53:56 +02:00
Bjørn Erik Pedersen 11047534e4 tpl/crypto: Add FNV32a
Main motivation to get a integer from a string.
2022-04-27 23:53:56 +02:00
Bjørn Erik Pedersen d7b54a4c37 markup/goldmark: Fix attribute nilpointer
Fixes 9819
2022-04-27 23:53:56 +02:00
Bjørn Erik Pedersen 13ceef7599 deps: Update to gocloud.dev v0.24.0
Note that the v0.25.0 version is broken (for Hugo, at least).
2022-04-27 12:12:43 +02:00
dependabot[bot] 942d0dd2c7 build(deps): bump github.com/mitchellh/mapstructure from 1.4.3 to 1.5.0
Bumps [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) from 1.4.3 to 1.5.0.
- [Release notes](https://github.com/mitchellh/mapstructure/releases)
- [Changelog](https://github.com/mitchellh/mapstructure/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitchellh/mapstructure/compare/v1.4.3...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/mitchellh/mapstructure
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-27 10:06:21 +02:00
Joe Mooring a022ca271b deps: Update github.com/yuin/goldmark v1.4.11 => v1.4.12
Fixes #9054
Fixes #9756
Fixes #9757
2022-04-27 10:04:00 +02:00
dependabot[bot] d56b33955f build(deps): bump github.com/evanw/esbuild from 0.14.36 to 0.14.38
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.36 to 0.14.38.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.36...v0.14.38)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-27 10:03:12 +02:00
Joe Mooring 55e28c239e deps: Update github.com/tdewolff/minify/v2 v2.11.1 => v2.11.2
Fixes #9820
2022-04-26 20:01:45 +02:00
Bjørn Erik Pedersen 9a888c243a Some godoc adjustments 2022-04-23 22:22:50 +02:00
Bjørn Erik Pedersen 05b45c35c8 tpl/lang: Handle nil values in lang.Merge 2022-04-23 22:22:50 +02:00
Bjørn Erik Pedersen 625be77e08 resources/page: Mark some more interface methods as internal
For the new documenttion.
2022-04-23 22:22:50 +02:00
Bjørn Erik Pedersen 097fd588ca Deprecate page.Author and page.Authors
These are not documented, and they don't belong on Page. We should consider having author a first class citizen of Hugo, but as it is not it's better modelled as a taxonomy.
2022-04-23 22:22:50 +02:00
hugoreleaser 41cc4e4ba3 releaser: Prepare repository for 0.98.0-DEV
[ci skip]
2022-04-18 18:01:38 +00:00
hugoreleaser 078053a43d releaser: Bump versions for release of 0.97.3
[ci skip]
2022-04-18 17:22:19 +00:00
hugoreleaser 7d9f888080 releaser: Add release notes for 0.97.3
[ci skip]
2022-04-18 17:22:19 +00:00
Bjørn Erik Pedersen 9b352f04a3 Fix syncing of /static regression
As introduced in Hugo `v0.76.1`.

And add a proper test for it.

Fixes  #9794
Closes #9788
2022-04-18 19:17:27 +02:00
Bjørn Erik Pedersen e66e2e9ce5 Revert "Revert "Fix PostProcess regression for hugo server""
This reverts commit 6c35a1a9ea.

Updates #9794
2022-04-18 19:17:27 +02:00
hugoreleaser 5de6f8a02c releaser: Prepare repository for 0.98.0-DEV
[ci skip]
2022-04-17 09:16:40 +00:00
370 changed files with 6511 additions and 5164 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
defaults: &defaults
docker:
- image: bepsays/ci-goreleaser:1.1800.000
- image: bepsays/ci-goreleaser:1.1800.300
environment:
CGO_ENABLED: "0"
+3 -3
View File
@@ -23,9 +23,9 @@ jobs:
with:
operations-per-run: 999
days-before-issue-stale: 365
days-before-pr-stale: 200
days-before-issue-close: 21
days-before-pr-close: 21
days-before-pr-stale: 365
days-before-issue-close: 56
days-before-pr-close: 56
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
+6 -2
View File
@@ -1,5 +1,7 @@
on: [push, pull_request]
name: Test
permissions:
contents: read
jobs:
test:
env:
@@ -48,6 +50,7 @@ jobs:
- if: matrix.os == 'windows-latest'
run: |
choco install pandoc
choco install mingw --version 10.2.0 --allow-downgrade
- run: pandoc -v
- if: matrix.os == 'ubuntu-latest'
name: Install dart-sass-embedded Linux
@@ -70,10 +73,11 @@ jobs:
echo "6ae442129dbb3334bc21ef851261da6c0c1b560da790ca2e1350871d00ab816d sass_embedded-1.0.0-beta.6-windows-x64.zip" | sha256sum -c;
unzip sass_embedded-1.0.0-beta.6-windows-x64.zip;
echo "$env:GITHUB_WORKSPACE/sass_embedded/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
- name: Test
- name: Check
run: |
mage -v test
mage -v check;
env:
HUGO_BUILD_TAGS: extended
- name: Build Docs
env:
HUGO_BUILD_TAGS: extended
+11 -8
View File
@@ -83,19 +83,22 @@ To make the contribution process as seamless as possible, we ask for the followi
### Git Commit Message Guidelines
This [blog article](http://chris.beams.io/posts/git-commit/) is a good resource for learning how to write good commit messages,
This [blog article](https://cbea.ms/git-commit/) is a good resource for learning how to write good commit messages,
the most important part being that each commit message should have a title/subject in imperative mood starting with a capital letter and no trailing period:
*"Return error on wrong use of the Paginator"*, **NOT** *"returning some error."*
*"js: Return error when option x is not set"*, **NOT** *"returning some error."*
Most title/subjects should have a lower-cased prefix with a colon and one whitespace. The prefix can be:
* The name of the package where (most of) the changes are made (e.g. `media: Add text/calendar`)
* If the package name is deeply nested/long, try to shorten it from the left side, e.g. `markup/goldmark` is OK, `resources/resource_transformers/js` can be shortened to `js`.
* If this commit touches several packages with a common functional topic, use that as a prefix, e.g. `errors: Resolve correct line numbers`)
* If this commit touches many packages without a common functional topic, prefix with `all:` (e.g. `all: Reformat Go code`)
* If this is a documentation update, prefix with `docs:`.
* If nothing of the above applies, just leave the prefix out.
Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*.
Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*.
Sometimes it makes sense to prefix the commit message with the package name (or docs folder) all lowercased ending with a colon.
That is fine, but the rest of the rules above apply.
So it is "tpl: Add emojify template func", not "tpl: add emojify template func.", and "docs: Document emoji", not "doc: document emoji."
Please use a short and descriptive branch name, e.g. **NOT** "patch-1". It's very common but creates a naming conflict each time when a submission is pulled for a review.
An example:
```text
+74 -52
View File
@@ -1,6 +1,6 @@
<a href="https://gohugo.io/"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/hugo-logo-wide.svg?sanitize=true" alt="Hugo" width="565"></a>
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](http://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](https://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
[Website](https://gohugo.io) |
[Forum](https://discourse.gohugo.io) |
@@ -130,97 +130,119 @@ Hugo stands on the shoulder of many great open source libraries.
If you run `hugo env -v` you will get a complete and up to date list.
In Hugo 0.89.0 that list is, in lexical order:
In Hugo 0.100.1 that list is, in lexical order:
```
cloud.google.com/go/storage="v1.10.0"
cloud.google.com/go="v0.87.0"
github.com/Azure/azure-pipeline-go="v0.2.2"
github.com/Azure/azure-storage-blob-go="v0.9.0"
cloud.google.com/go/compute="v1.6.1"
cloud.google.com/go/iam="v0.3.0"
cloud.google.com/go/storage="v1.22.0"
cloud.google.com/go="v0.101.0"
github.com/Azure/azure-pipeline-go="v0.2.3"
github.com/Azure/azure-storage-blob-go="v0.14.0"
github.com/Azure/go-autorest/autorest/adal="v0.9.15"
github.com/Azure/go-autorest/autorest/date="v0.3.0"
github.com/Azure/go-autorest/autorest="v0.11.20"
github.com/Azure/go-autorest/logger="v0.2.1"
github.com/Azure/go-autorest/tracing="v0.6.0"
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
github.com/BurntSushi/toml="v0.3.1"
github.com/PuerkitoBio/purell="v1.1.1"
github.com/PuerkitoBio/urlesc="v0.0.0-20170810143723-de5bf2ad4578"
github.com/alecthomas/chroma="v0.9.4"
github.com/alecthomas/chroma="v0.10.0"
github.com/armon/go-radix="v1.0.0"
github.com/aws/aws-sdk-go="v1.41.14"
github.com/aws/aws-sdk-go-v2/config="v1.7.0"
github.com/aws/aws-sdk-go-v2/credentials="v1.4.0"
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.5.0"
github.com/aws/aws-sdk-go-v2/internal/ini="v1.2.2"
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.3.0"
github.com/aws/aws-sdk-go-v2/service/sso="v1.4.0"
github.com/aws/aws-sdk-go-v2/service/sts="v1.7.0"
github.com/aws/aws-sdk-go-v2="v1.9.0"
github.com/aws/aws-sdk-go="v1.43.5"
github.com/aws/smithy-go="v1.8.0"
github.com/bep/clock="v0.3.0"
github.com/bep/debounce="v1.2.0"
github.com/bep/gitmap="v1.1.2"
github.com/bep/godartsass="v0.12.0"
github.com/bep/golibsass="v1.0.0"
github.com/bep/goat="v0.5.0"
github.com/bep/godartsass="v0.14.0"
github.com/bep/golibsass="v1.1.0"
github.com/bep/gowebp="v0.1.0"
github.com/bep/overlayfs="v0.6.0"
github.com/bep/tmc="v0.5.1"
github.com/clbanning/mxj/v2="v2.5.5"
github.com/cli/safeexec="v1.0.0"
github.com/cpuguy83/go-md2man/v2="v2.0.0"
github.com/cpuguy83/go-md2man/v2="v2.0.1"
github.com/disintegration/gift="v1.2.1"
github.com/dlclark/regexp2="v1.4.0"
github.com/dustin/go-humanize="v1.0.0"
github.com/evanw/esbuild="v0.13.12"
github.com/fsnotify/fsnotify="v1.5.1"
github.com/getkin/kin-openapi="v0.80.0"
github.com/evanw/esbuild="v0.14.42"
github.com/frankban/quicktest="v1.14.3"
github.com/fsnotify/fsnotify="v1.5.4"
github.com/getkin/kin-openapi="v0.94.0"
github.com/ghodss/yaml="v1.0.0"
github.com/go-openapi/jsonpointer="v0.19.5"
github.com/go-openapi/swag="v0.19.5"
github.com/gobuffalo/flect="v0.2.3"
github.com/gobuffalo/flect="v0.2.5"
github.com/gobwas/glob="v0.2.3"
github.com/gohugoio/go-i18n/v2="v2.1.3-0.20210430103248-4c28c89f8013"
github.com/gohugoio/locales="v0.14.0"
github.com/gohugoio/localescompressed="v0.14.0"
github.com/golang/groupcache="v0.0.0-20200121045136-8c9f03a8e57e"
github.com/gohugoio/localescompressed="v1.0.1"
github.com/golang-jwt/jwt/v4="v4.0.0"
github.com/golang/groupcache="v0.0.0-20210331224755-41bb18bfe9da"
github.com/golang/protobuf="v1.5.2"
github.com/google/go-cmp="v0.5.6"
github.com/google/uuid="v1.1.2"
github.com/google/wire="v0.4.0"
github.com/googleapis/gax-go/v2="v2.0.5"
github.com/googleapis/gax-go="v2.0.2+incompatible"
github.com/gorilla/websocket="v1.4.2"
github.com/google/go-cmp="v0.5.8"
github.com/google/uuid="v1.3.0"
github.com/google/wire="v0.5.0"
github.com/googleapis/gax-go/v2="v2.3.0"
github.com/googleapis/go-type-adapters="v1.0.0"
github.com/gorilla/websocket="v1.5.0"
github.com/hairyhenderson/go-codeowners="v0.2.3-0.20201026200250-cdc7c0759690"
github.com/inconshreveable/mousetrap="v1.0.0"
github.com/jdkato/prose="v1.2.1"
github.com/jmespath/go-jmespath="v0.4.0"
github.com/kyokomi/emoji/v2="v2.2.8"
github.com/kr/pretty="v0.3.0"
github.com/kr/text="v0.2.0"
github.com/kyokomi/emoji/v2="v2.2.9"
github.com/mailru/easyjson="v0.0.0-20190626092158-b2ccc519800e"
github.com/mattn/go-ieproxy="v0.0.1"
github.com/mattn/go-isatty="v0.0.14"
github.com/mattn/go-runewidth="v0.0.9"
github.com/miekg/mmark="v1.3.6"
github.com/mitchellh/hashstructure="v1.1.0"
github.com/mitchellh/mapstructure="v1.4.2"
github.com/mitchellh/mapstructure="v1.5.0"
github.com/muesli/smartcrop="v0.3.0"
github.com/niklasfasching/go-org="v1.5.0"
github.com/niklasfasching/go-org="v1.6.2"
github.com/olekukonko/tablewriter="v0.0.5"
github.com/pelletier/go-toml/v2="v2.0.0-beta.3.0.20210727221244-fa0796069526"
github.com/pkg/errors="v0.9.1"
github.com/rogpeppe/go-internal="v1.8.0"
github.com/russross/blackfriday/v2="v2.0.1"
github.com/russross/blackfriday="v1.5.3-0.20200218234912-41c5fccfd6f6"
github.com/pelletier/go-toml/v2="v2.0.0-beta.7.0.20220408132554-2377ac4bc04c"
github.com/rogpeppe/go-internal="v1.8.1"
github.com/russross/blackfriday/v2="v2.1.0"
github.com/rwcarlsen/goexif="v0.0.0-20190401172101-9e8deecbddbd"
github.com/sanity-io/litter="v1.5.1"
github.com/sanity-io/litter="v1.5.5"
github.com/sass/libsass="3.6.5"
github.com/shurcooL/sanitized_anchor_name="v1.0.0"
github.com/spf13/afero="v1.6.0"
github.com/spf13/cast="v1.4.1"
github.com/spf13/cobra="v1.2.1"
github.com/spf13/afero="v1.8.2"
github.com/spf13/cast="v1.5.0"
github.com/spf13/cobra="v1.4.0"
github.com/spf13/fsync="v0.9.0"
github.com/spf13/jwalterweatherman="v1.1.0"
github.com/spf13/pflag="v1.0.5"
github.com/tdewolff/minify/v2="v2.9.22"
github.com/tdewolff/parse/v2="v2.5.21"
github.com/tdewolff/minify/v2="v2.11.5"
github.com/tdewolff/parse/v2="v2.5.31"
github.com/webmproject/libwebp="v1.2.0"
github.com/yuin/goldmark-highlighting="v0.0.0-20200307114337-60d527fdb691"
github.com/yuin/goldmark="v1.4.2"
github.com/yuin/goldmark="v1.4.12"
go.opencensus.io="v0.23.0"
gocloud.dev="v0.20.0"
golang.org/x/image="v0.0.0-20210220032944-ac19c3e999fb"
golang.org/x/net="v0.0.0-20210614182718-04defd469f4e"
golang.org/x/oauth2="v0.0.0-20210628180205-a41e5a781914"
go.uber.org/atomic="v1.9.0"
gocloud.dev="v0.24.0"
golang.org/x/crypto="v0.0.0-20211108221036-ceb1ce70b4fa"
golang.org/x/image="v0.0.0-20211028202545-6944b10bf410"
golang.org/x/net="v0.0.0-20220425223048-2871e0cb64e4"
golang.org/x/oauth2="v0.0.0-20220411215720-9780585627b5"
golang.org/x/sync="v0.0.0-20210220032951-036812b2e83c"
golang.org/x/sys="v0.0.0-20210908233432-aa78b53d3365"
golang.org/x/sys="v0.0.0-20220422013727-9388b58f7150"
golang.org/x/text="v0.3.7"
golang.org/x/xerrors="v0.0.0-20200804184101-5ec99f83aff1"
google.golang.org/api="v0.51.0"
google.golang.org/genproto="v0.0.0-20210716133855-ce7ef5c701ea"
google.golang.org/grpc="v1.39.0"
google.golang.org/protobuf="v1.27.1"
golang.org/x/tools="v0.1.10"
golang.org/x/xerrors="v0.0.0-20220411194840-2f41105eb62f"
google.golang.org/api="v0.76.0"
google.golang.org/genproto="v0.0.0-20220426171045-31bebdecfb46"
google.golang.org/grpc="v1.46.0"
google.golang.org/protobuf="v1.28.0"
gopkg.in/yaml.v2="v2.4.0"
```
+3
View File
@@ -295,6 +295,9 @@ func (c *Cache) isExpired(modTime time.Time) bool {
if c.maxAge < 0 {
return false
}
// Note the use of time.Since here.
// We cannot use Hugo's global Clock for this.
return c.maxAge == 0 || time.Since(modTime) > c.maxAge
}
+8 -6
View File
@@ -14,6 +14,7 @@
package filecache
import (
"fmt"
"path"
"path/filepath"
"strings"
@@ -25,8 +26,9 @@ import (
"github.com/gohugoio/hugo/helpers"
"errors"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -153,7 +155,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
}
if err := decoder.Decode(v); err != nil {
return nil, errors.Wrap(err, "failed to decode filecache config")
return nil, fmt.Errorf("failed to decode filecache config: %w", err)
}
if cc.Dir == "" {
@@ -162,7 +164,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
name := strings.ToLower(k)
if !valid[name] {
return nil, errors.Errorf("%q is not a valid cache name", name)
return nil, fmt.Errorf("%q is not a valid cache name", name)
}
c[name] = cc
@@ -197,12 +199,12 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
if !v.isResourceDir {
if isOsFs && !filepath.IsAbs(v.Dir) {
return c, errors.Errorf("%q must resolve to an absolute directory", v.Dir)
return c, fmt.Errorf("%q must resolve to an absolute directory", v.Dir)
}
// Avoid cache in root, e.g. / (Unix) or c:\ (Windows)
if len(strings.TrimPrefix(v.Dir, filepath.VolumeName(v.Dir))) == 1 {
return c, errors.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
return c, fmt.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
}
}
@@ -242,5 +244,5 @@ func resolveDirPlaceholder(fs afero.Fs, cfg config.Provider, placeholder string)
return filepath.Base(workingDir), false, nil
}
return "", false, errors.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
return "", false, fmt.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
}
+2 -2
View File
@@ -14,12 +14,12 @@
package filecache
import (
"fmt"
"io"
"os"
"github.com/gohugoio/hugo/hugofs"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -39,7 +39,7 @@ func (c Caches) Prune() (int, error) {
if os.IsNotExist(err) {
continue
}
return counter, errors.Wrapf(err, "failed to prune cache %q", k)
return counter, fmt.Errorf("failed to prune cache %q: %w", k, err)
}
}
+58 -41
View File
@@ -14,8 +14,8 @@
package commands
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
@@ -29,9 +29,11 @@ import (
"golang.org/x/sync/semaphore"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/paths"
"github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman"
"github.com/gohugoio/hugo/common/loggers"
@@ -42,6 +44,7 @@ import (
"github.com/gohugoio/hugo/hugolib"
"github.com/spf13/afero"
"github.com/bep/clock"
"github.com/bep/debounce"
"github.com/bep/overlayfs"
"github.com/gohugoio/hugo/common/types"
@@ -95,13 +98,12 @@ type commandeer struct {
serverPorts []serverPortListener
languagesConfigured bool
languages langs.Languages
doLiveReload bool
renderStaticToDisk bool
fastRenderMode bool
showErrorInBrowser bool
wasError bool
languages langs.Languages
doLiveReload bool
renderStaticToDisk bool
fastRenderMode bool
showErrorInBrowser bool
wasError bool
configured bool
paused bool
@@ -128,6 +130,15 @@ func (c *commandeerHugoState) hugo() *hugolib.HugoSites {
return c.hugoSites
}
func (c *commandeerHugoState) hugoTry() *hugolib.HugoSites {
select {
case <-c.created:
return c.hugoSites
case <-time.After(time.Millisecond * 100):
return nil
}
}
func (c *commandeer) errCount() int {
return int(c.logger.LogCounters().ErrorCounter.Count())
}
@@ -141,19 +152,11 @@ func (c *commandeer) getErrorWithContext() any {
m := make(map[string]any)
m["Error"] = errors.New(removeErrorPrefixFromLog(c.logger.Errors()))
//xwm["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.logger.Errors())))
m["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.logger.Errors())))
m["Version"] = hugo.BuildVersionString()
fe := herrors.UnwrapErrorWithFileContext(c.buildErr)
if fe != nil {
m["File"] = fe
}
if c.h.verbose {
var b bytes.Buffer
herrors.FprintStackTraceFromErr(&b, c.buildErr)
m["StackTrace"] = b.String()
}
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.buildErr)
m["Files"] = ferrors
return m
}
@@ -173,6 +176,21 @@ func (c *commandeer) initFs(fs *hugofs.Fs) error {
return nil
}
func (c *commandeer) initClock(loc *time.Location) error {
bt := c.Cfg.GetString("clock")
if bt == "" {
return nil
}
t, err := cast.StringToDateInDefaultLocation(bt, loc)
if err != nil {
return fmt.Errorf(`failed to parse "clock" flag: %s`, err)
}
htime.Clock = clock.Start(t)
return nil
}
func newCommandeer(mustHaveConfigFile, failOnInitErr, running bool, h *hugoBuilderCommon, f flagsToConfigHandler, cfgInit func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
var rebuildDebouncer func(f func())
if running {
@@ -350,9 +368,16 @@ func (c *commandeer) loadConfig() error {
c.configFiles = configFiles
if l, ok := c.Cfg.Get("languagesSorted").(langs.Languages); ok {
c.languagesConfigured = true
c.languages = l
var ok bool
loc := time.Local
c.languages, ok = c.Cfg.Get("languagesSorted").(langs.Languages)
if ok {
loc = langs.GetLocation(c.languages[0])
}
err = c.initClock(loc)
if err != nil {
return err
}
// Set some commonly used flags
@@ -395,23 +420,23 @@ func (c *commandeer) loadConfig() error {
}
c.fsCreate.Do(func() {
fs := hugofs.NewFrom(sourceFs, config)
// Assume both source and destination are using same filesystem.
fs := hugofs.NewFromSourceAndDestination(sourceFs, sourceFs, config)
if c.publishDirFs != nil {
// Need to reuse the destination on server rebuilds.
fs.PublishDir = c.publishDirFs
fs.PublishDirServer = c.publishDirServerFs
} else {
publishDir := config.GetString("publishDir")
publishDirStatic := config.GetString("publishDirStatic")
workingDir := config.GetString("workingDir")
absPublishDir := paths.AbsPathify(workingDir, publishDir)
absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
if c.renderStaticToDisk {
// Writes the dynamic output oton memory,
publishDirStatic := config.GetString("publishDirStatic")
workingDir := config.GetString("workingDir")
absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
fs = hugofs.NewFromSourceAndDestination(sourceFs, afero.NewMemMapFs(), config)
// Writes the dynamic output to memory,
// while serve others directly from /public on disk.
dynamicFs := afero.NewMemMapFs()
dynamicFs := fs.PublishDir
staticFs := afero.NewBasePathFs(afero.NewOsFs(), absPublishDirStatic)
// Serve from both the static and dynamic fs,
@@ -427,18 +452,10 @@ func (c *commandeer) loadConfig() error {
},
},
)
fs.PublishDir = dynamicFs
fs.PublishDirStatic = staticFs
} else if createMemFs {
// Hugo writes the output to memory instead of the disk.
fs.PublishDir = new(afero.MemMapFs)
fs.PublishDirServer = fs.PublishDir
fs.PublishDirStatic = fs.PublishDir
} else {
// Write everything to disk.
fs.PublishDir = afero.NewBasePathFs(afero.NewOsFs(), absPublishDir)
fs.PublishDirServer = fs.PublishDir
fs.PublishDirStatic = fs.PublishDir
fs = hugofs.NewFromSourceAndDestination(sourceFs, afero.NewMemMapFs(), config)
}
}
+3
View File
@@ -210,6 +210,7 @@ type hugoBuilderCommon struct {
buildWatch bool
poll string
clock string
gc bool
@@ -279,6 +280,7 @@ func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment")
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
cmd.PersistentFlags().StringVar(&cc.clock, "clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00")
}
func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
@@ -303,6 +305,7 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
cmd.Flags().BoolP("forceSyncStatic", "", false, "copy all files when static is changed.")
cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files")
cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files")
cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file")
cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations")
cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.")
cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.")
+4
View File
@@ -375,6 +375,10 @@ Single: {{ .Title }}
List: {{ .Title }}
Environment: {{ hugo.Environment }}
For issue 9788:
{{ $foo :="abc" | resources.FromString "foo.css" | minify | resources.PostProcess }}
PostProcess: {{ $foo.RelPermalink }}
`)
return dir
+1 -3
View File
@@ -31,8 +31,6 @@ import (
"github.com/gohugoio/hugo/parser"
"github.com/gohugoio/hugo/parser/metadecoders"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/hugolib"
"github.com/spf13/cobra"
@@ -193,7 +191,7 @@ func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, target
fs := hugofs.Os
if err := helpers.WriteToDisk(newFilename, &newContent, fs); err != nil {
return errors.Wrapf(err, "Failed to save file %q:", newFilename)
return fmt.Errorf("Failed to save file %q:: %w", newFilename, err)
}
return nil
+3 -3
View File
@@ -16,9 +16,9 @@ package commands
import (
"os"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters/html"
"github.com/alecthomas/chroma/styles"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/styles"
"github.com/spf13/cobra"
)
+34 -21
View File
@@ -33,15 +33,14 @@ import (
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/resources/page"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/terminal"
@@ -128,6 +127,12 @@ func initializeConfig(mustHaveConfigFile, failOnInitErr, running bool,
return nil, err
}
if h := c.hugoTry(); h != nil {
for _, s := range h.Sites {
s.RegisterMediaTypes()
}
}
return c, nil
}
@@ -190,6 +195,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"buildDrafts",
"buildFuture",
"buildExpired",
"clock",
"uglyURLs",
"canonifyURLs",
"enableRobotsTXT",
@@ -200,6 +206,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"forceSyncStatic",
"noTimes",
"noChmod",
"noBuildLock",
"ignoreVendorPaths",
"templateMetrics",
"templateMetricsHints",
@@ -277,10 +284,6 @@ func setValueFromFlag(flags *flag.FlagSet, key string, cfg config.Provider, targ
}
}
func isTerminal() bool {
return terminal.IsTerminal(os.Stdout)
}
func (c *commandeer) fullBuild(noBuildLock bool) error {
var (
g errgroup.Group
@@ -290,7 +293,7 @@ func (c *commandeer) fullBuild(noBuildLock bool) error {
if !c.h.quiet {
fmt.Println("Start building sites … ")
fmt.Println(hugo.BuildVersionString())
if isTerminal() {
if terminal.IsTerminal(os.Stdout) {
defer func() {
fmt.Print(showCursor + clearLine)
}()
@@ -300,14 +303,14 @@ func (c *commandeer) fullBuild(noBuildLock bool) error {
copyStaticFunc := func() error {
cnt, err := c.copyStatic()
if err != nil {
return errors.Wrap(err, "Error copying static files")
return fmt.Errorf("Error copying static files: %w", err)
}
langCount = cnt
return nil
}
buildSitesFunc := func() error {
if err := c.buildSites(noBuildLock); err != nil {
return errors.Wrap(err, "Error building site")
return fmt.Errorf("Error building site: %w", err)
}
return nil
}
@@ -354,10 +357,10 @@ func (c *commandeer) initCPUProfile() (func(), error) {
f, err := os.Create(c.h.cpuprofile)
if err != nil {
return nil, errors.Wrap(err, "failed to create CPU profile")
return nil, fmt.Errorf("failed to create CPU profile: %w", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
return nil, errors.Wrap(err, "failed to start CPU profile")
return nil, fmt.Errorf("failed to start CPU profile: %w", err)
}
return func() {
pprof.StopCPUProfile()
@@ -388,11 +391,11 @@ func (c *commandeer) initTraceProfile() (func(), error) {
f, err := os.Create(c.h.traceprofile)
if err != nil {
return nil, errors.Wrap(err, "failed to create trace file")
return nil, fmt.Errorf("failed to create trace file: %w", err)
}
if err := trace.Start(f); err != nil {
return nil, errors.Wrap(err, "failed to start trace")
return nil, fmt.Errorf("failed to start trace: %w", err)
}
return func() {
@@ -682,6 +685,10 @@ func (c *commandeer) firstPathSpec() *helpers.PathSpec {
}
func (c *commandeer) timeTrack(start time.Time, name string) {
// Note the use of time.Since here and time.Now in the callers.
// We have a htime.Sinnce, but that may be adjusted to the future,
// and that does not make sense here, esp. when used before the
// global Clock is initialized.
elapsed := time.Since(start)
c.logger.Printf("%s in %v ms", name, int(1000*elapsed.Seconds()))
}
@@ -732,15 +739,16 @@ func (c *commandeer) buildSites(noBuildLock bool) (err error) {
func (c *commandeer) handleBuildErr(err error, msg string) {
c.buildErr = err
c.logger.Errorln(msg + ":\n")
c.logger.Errorln(helpers.FirstUpper(err.Error()))
if !c.h.quiet && c.h.verbose {
herrors.PrintStackTraceFromErr(err)
}
c.logger.Errorln(msg + ": " + cleanErrorLog(err.Error()))
}
func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
if c.buildErr != nil {
ferrs := herrors.UnwrapFileErrorsWithErrorContext(c.buildErr)
for _, err := range ferrs {
events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write})
}
}
c.buildErr = nil
visited := c.visitedURLs.PeekAllSet()
if c.fastRenderMode {
@@ -855,8 +863,13 @@ func (c *commandeer) newWatcher(pollIntervalStr string, dirList ...string) (*wat
return nil, err
}
spec := c.hugo().Deps.SourceSpec
for _, d := range dirList {
if d != "" {
if spec.IgnoreFile(d) {
continue
}
_ = watcher.Add(d)
}
}
@@ -905,7 +918,7 @@ func (c *commandeer) printChangeDetected(typ string) {
c.logger.Println(msg)
const layout = "2006-01-02 15:04:05.000 -0700"
c.logger.Println(time.Now().Format(layout))
c.logger.Println(htime.Now().Format(layout))
}
const (
+176 -15
View File
@@ -14,32 +14,193 @@
package commands
import (
"bytes"
"fmt"
"math/rand"
"path/filepath"
"strings"
"testing"
"github.com/bep/clock"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"
"golang.org/x/tools/txtar"
)
// Issue #5662
func TestHugoWithContentDirOverride(t *testing.T) {
t.Parallel()
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
title = "Hugo Commands"
-- mycontent/p1.md --
---
title: "P1"
---
-- layouts/_default/single.html --
Page: {{ .Title }}|
`
s := newTestHugoCmdBuilder(c, files, []string{"-c", "mycontent"}).Build()
s.AssertFileContent("public/p1/index.html", `Page: P1|`)
}
// Issue #9794
func TestHugoStaticFilesMultipleStaticAndManyFolders(t *testing.T) {
t.Parallel()
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
theme = "mytheme"
-- layouts/index.html --
Home.
`
const (
numDirs = 33
numFilesMax = 12
)
r := rand.New(rand.NewSource(32))
for i := 0; i < numDirs; i++ {
for j := 0; j < r.Intn(numFilesMax); j++ {
if j%3 == 0 {
files += fmt.Sprintf("-- themes/mytheme/static/d%d/f%d.txt --\nHellot%d-%d\n", i, j, i, j)
files += fmt.Sprintf("-- themes/mytheme/static/d%d/ft%d.txt --\nHellot%d-%d\n", i, j, i, j)
}
files += fmt.Sprintf("-- static/d%d/f%d.txt --\nHello%d-%d\n", i, j, i, j)
}
}
r = rand.New(rand.NewSource(32))
s := newTestHugoCmdBuilder(c, files, []string{"-c", "mycontent"}).Build()
for i := 0; i < numDirs; i++ {
for j := 0; j < r.Intn(numFilesMax); j++ {
if j%3 == 0 {
if j%3 == 0 {
s.AssertFileContent(fmt.Sprintf("public/d%d/ft%d.txt", i, j), fmt.Sprintf("Hellot%d-%d", i, j))
}
s.AssertFileContent(fmt.Sprintf("public/d%d/f%d.txt", i, j), fmt.Sprintf("Hello%d-%d", i, j))
}
}
}
}
// Issue #8787
func TestHugoListCommandsWithClockFlag(t *testing.T) {
t.Cleanup(func() { htime.Clock = clock.System() })
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
title = "Hugo Commands"
timeZone = "UTC"
-- content/past.md --
---
title: "Past"
date: 2000-11-06
---
-- content/future.md --
---
title: "Future"
date: 2200-11-06
---
-- layouts/_default/single.html --
Page: {{ .Title }}|
`
s := newTestHugoCmdBuilder(c, files, []string{"list", "future"})
s.captureOut = true
s.Build()
p := filepath.Join("content", "future.md")
s.AssertStdout(p + ",2200-11-06T00:00:00Z")
s = newTestHugoCmdBuilder(c, files, []string{"list", "future", "--clock", "2300-11-06"}).Build()
s.AssertStdout("")
}
type testHugoCmdBuilder struct {
*qt.C
fs afero.Fs
dir string
files string
args []string
captureOut bool
out string
}
func newTestHugoCmdBuilder(c *qt.C, files string, args []string) *testHugoCmdBuilder {
s := &testHugoCmdBuilder{C: c, files: files, args: args}
s.dir = s.TempDir()
s.fs = afero.NewBasePathFs(hugofs.Os, s.dir)
return s
}
func (s *testHugoCmdBuilder) Build() *testHugoCmdBuilder {
data := txtar.Parse([]byte(s.files))
for _, f := range data.Files {
filename := filepath.Clean(f.Name)
data := bytes.TrimSuffix(f.Data, []byte("\n"))
s.Assert(s.fs.MkdirAll(filepath.Dir(filename), 0777), qt.IsNil)
s.Assert(afero.WriteFile(s.fs, filename, data, 0666), qt.IsNil)
}
hugoCmd := newCommandsBuilder().addAll().build()
cmd := hugoCmd.getCommand()
args := append(s.args, "-s="+s.dir, "--quiet")
cmd.SetArgs(args)
contentDir := "contentOverride"
if s.captureOut {
out, err := captureStdout(func() error {
_, err := cmd.ExecuteC()
return err
})
s.Assert(err, qt.IsNil)
s.out = out
} else {
_, err := cmd.ExecuteC()
s.Assert(err, qt.IsNil)
}
cfgStr := `
baseURL = "https://example.org"
title = "Hugo Commands"
contentDir = "thisdoesnotexist"
`
dir := createSimpleTestSite(t, testSiteConfig{configTOML: cfgStr, contentDir: contentDir})
cmd.SetArgs([]string{"-s=" + dir, "-c=" + contentDir})
_, err := cmd.ExecuteC()
c.Assert(err, qt.IsNil)
return s
}
func (s *testHugoCmdBuilder) AssertFileContent(filename string, matches ...string) {
s.Helper()
data, err := afero.ReadFile(s.fs, filename)
s.Assert(err, qt.IsNil)
content := strings.TrimSpace(string(data))
for _, m := range matches {
lines := strings.Split(m, "\n")
for _, match := range lines {
match = strings.TrimSpace(match)
if match == "" || strings.HasPrefix(match, "#") {
continue
}
s.Assert(content, qt.Contains, match, qt.Commentf(m))
}
}
}
func (s *testHugoCmdBuilder) AssertStdout(match string) {
s.Helper()
content := strings.TrimSpace(s.out)
s.Assert(content, qt.Contains, strings.TrimSpace(match))
}
+4 -9
View File
@@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/parser/pageparser"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/parser/metadecoders"
@@ -35,7 +36,6 @@ import (
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/parser"
"github.com/spf13/afero"
"github.com/spf13/cobra"
@@ -201,12 +201,7 @@ func (i *importCmd) retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool)
}
func (i *importCmd) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool, force bool) error {
s, err := hugolib.NewSiteDefaultLang()
if err != nil {
return err
}
fs := s.Fs.Source
fs := &afero.OsFs{}
if exists, _ := helpers.Exists(targetDir, fs); exists {
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
return errors.New("target path \"" + targetDir + "\" exists but is not a directory")
@@ -362,12 +357,12 @@ func parseJekyllFilename(filename string) (time.Time, string, error) {
re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`)
r := re.FindAllStringSubmatch(filename, -1)
if len(r) == 0 {
return time.Now(), "", errors.New("filename not match")
return htime.Now(), "", errors.New("filename not match")
}
postDate, err := time.Parse("2006-1-2", r[0][1])
if err != nil {
return time.Now(), "", err
return htime.Now(), "", err
}
postName := r[0][2]
+8
View File
@@ -99,6 +99,10 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
return newSystemError("Error building sites", err)
}
if err != nil {
return newSystemError("Error building sites", err)
}
writer := csv.NewWriter(os.Stdout)
defer writer.Flush()
@@ -127,6 +131,10 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
return newSystemError("Error building sites", err)
}
if err != nil {
return newSystemError("Error building sites", err)
}
writer := csv.NewWriter(os.Stdout)
defer writer.Flush()
+2 -2
View File
@@ -34,9 +34,9 @@ func TestListAll(t *testing.T) {
hugoCmd := newCommandsBuilder().addAll().build()
cmd := hugoCmd.getCommand()
defer func() {
t.Cleanup(func() {
os.RemoveAll(dir)
}()
})
cmd.SetArgs([]string{"-s=" + dir, "list", "all"})
+2 -3
View File
@@ -16,14 +16,13 @@ package commands
import (
"bytes"
"errors"
"fmt"
"path/filepath"
"strings"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/parser/metadecoders"
_errors "github.com/pkg/errors"
"github.com/gohugoio/hugo/create"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
@@ -94,7 +93,7 @@ func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error
for _, dir := range dirs {
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
return _errors.Wrap(err, "Failed to create dir")
return fmt.Errorf("Failed to create dir: %w", err)
}
}
+2 -2
View File
@@ -18,8 +18,8 @@ import (
"errors"
"path/filepath"
"strings"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/cobra"
@@ -113,7 +113,7 @@ func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
by := []byte(`The MIT License (MIT)
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
Copyright (c) ` + htime.Now().Format("2006") + ` YOUR_NAME_HERE
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
+91 -24
View File
@@ -33,11 +33,12 @@ import (
"syscall"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/tpl"
"golang.org/x/sync/errgroup"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/livereload"
"github.com/gohugoio/hugo/config"
@@ -167,8 +168,11 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
c.Set("watch", true)
}
// TODO(bep) yes, we should fix.
if !c.languagesConfigured {
// TODO(bep) see issue 9901
// cfgInit is called twice, before and after the languages have been initialized.
// The servers (below) can not be initialized before we
// know if we're configured in a multihost setup.
if len(c.languages) == 0 {
return nil
}
@@ -268,10 +272,6 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
return err
}
for _, s := range c.hugo().Sites {
s.RegisterMediaTypes()
}
// Watch runs its own server as part of the routine
if sc.serverWatch {
@@ -366,7 +366,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
// We're only interested in the path
u, err := url.Parse(baseURL)
if err != nil {
return nil, nil, "", "", errors.Wrap(err, "Invalid baseURL")
return nil, nil, "", "", fmt.Errorf("Invalid baseURL: %w", err)
}
decorate := func(h http.Handler) http.Handler {
@@ -475,12 +475,40 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
return mu, listener, u.String(), endpoint, nil
}
var logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
var (
logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
logDuplicateTemplateExecuteRe = regexp.MustCompile(`: template: .*?:\d+:\d+: executing ".*?"`)
logDuplicateTemplateParseRe = regexp.MustCompile(`: template: .*?:\d+:\d*`)
)
func removeErrorPrefixFromLog(content string) string {
return logErrorRe.ReplaceAllLiteralString(content, "")
}
var logReplacer = strings.NewReplacer(
"can't", "cant", // Chroma lexer does'nt do well with "can't"
"*hugolib.pageState", "page.Page", // Page is the public interface.
"Rebuild failed:", "",
)
func cleanErrorLog(content string) string {
content = strings.ReplaceAll(content, "\n", " ")
content = logReplacer.Replace(content)
content = logDuplicateTemplateExecuteRe.ReplaceAllString(content, "")
content = logDuplicateTemplateParseRe.ReplaceAllString(content, "")
seen := make(map[string]bool)
parts := strings.Split(content, ": ")
keep := make([]string, 0, len(parts))
for _, part := range parts {
if seen[part] {
continue
}
seen[part] = true
keep = append(keep, part)
}
return strings.Join(keep, ": ")
}
func (c *commandeer) serve(s *serverCmd) error {
isMultiHost := c.hugo().IsMultihost()
@@ -500,10 +528,25 @@ func (c *commandeer) serve(s *serverCmd) error {
roots = []string{""}
}
templ, err := c.hugo().TextTmpl().Parse("__default_server_error", buildErrorTemplate)
if err != nil {
return err
// Cache it here. The HugoSites object may be unavaialble later on due to intermitent configuration errors.
// To allow the en user to change the error template while the server is running, we use
// the freshest template we can provide.
var (
errTempl tpl.Template
templHandler tpl.TemplateHandler
)
getErrorTemplateAndHandler := func(h *hugolib.HugoSites) (tpl.Template, tpl.TemplateHandler) {
if h == nil {
return errTempl, templHandler
}
templHandler := h.Tmpl()
errTempl, found := templHandler.Lookup("_server/error.html")
if !found {
panic("template server/error.html not found")
}
return errTempl, templHandler
}
errTempl, templHandler = getErrorTemplateAndHandler(c.hugo())
srv := &fileServer{
baseURLs: baseURLs,
@@ -511,8 +554,11 @@ func (c *commandeer) serve(s *serverCmd) error {
c: c,
s: s,
errorTemplate: func(ctx any) (io.Reader, error) {
// hugoTry does not block, getErrorTemplateAndHandler will fall back
// to cached values if nil.
templ, handler := getErrorTemplateAndHandler(c.hugoTry())
b := &bytes.Buffer{}
err := c.hugo().Tmpl().Execute(templ, b, ctx)
err := handler.Execute(templ, b, ctx)
return b, err
},
}
@@ -558,16 +604,37 @@ func (c *commandeer) serve(s *serverCmd) error {
jww.FEEDBACK.Println("Press Ctrl+C to stop")
if s.stop != nil {
select {
case <-sigs:
case <-s.stop:
err := func() error {
if s.stop != nil {
for {
select {
case <-sigs:
return nil
case <-s.stop:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
} else {
for {
select {
case <-sigs:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
}
} else {
<-sigs
}()
if err != nil {
jww.ERROR.Println("Error:", err)
}
c.hugo().Close()
if h := c.hugoTry(); h != nil {
h.Close()
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@@ -627,7 +694,7 @@ func (sc *serverCmd) fixURL(cfg config.Provider, s string, port int) (string, er
if strings.Contains(u.Host, ":") {
u.Host, _, err = net.SplitHostPort(u.Host)
if err != nil {
return "", errors.Wrap(err, "Failed to split baseURL hostpost")
return "", fmt.Errorf("Failed to split baseURL hostpost: %w", err)
}
}
u.Host += fmt.Sprintf(":%d", port)
@@ -656,13 +723,13 @@ func memStats() error {
go func() {
var stats runtime.MemStats
start := time.Now().UnixNano()
start := htime.Now().UnixNano()
for {
runtime.ReadMemStats(&stats)
if fileMemStats != nil {
fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
(htime.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
time.Sleep(interval)
} else {
break
-61
View File
@@ -22,67 +22,6 @@ import (
"github.com/gohugoio/hugo/transform/livereloadinject"
)
var buildErrorTemplate = `<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<title>Hugo Server: Error</title>
<style type="text/css">
body {
font-family: "Muli",avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
background-color: #2f1e2e;
}
main {
margin: auto;
width: 95%;
padding: 1rem;
}
.version {
color: #ccc;
padding: 1rem 0;
}
.stack {
margin-top: 4rem;
}
pre {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
.highlight {
overflow-x: auto;
margin-bottom: 1rem;
}
a {
color: #0594cb;
text-decoration: none;
}
a:hover {
color: #ccc;
}
</style>
</head>
<body>
<main>
{{ highlight .Error "apl" "linenos=false,noclasses=true,style=paraiso-dark" }}
{{ with .File }}
{{ $params := printf "noclasses=true,style=paraiso-dark,linenos=table,hl_lines=%d,linenostart=%d" (add .LinesPos 1) (sub .Position.LineNumber .LinesPos) }}
{{ $lexer := .ChromaLexer | default "go-html-template" }}
{{ highlight (delimit .Lines "\n") $lexer $params }}
{{ end }}
{{ with .StackTrace }}
{{ highlight . "apl" "noclasses=true,style=paraiso-dark" }}
{{ end }}
<p class="version">{{ .Version }}</p>
<a href="">Reload Page</a>
</main>
</body>
</html>
`
func injectLiveReloadScript(src io.Reader, baseURL url.URL) string {
var b bytes.Buffer
chain := transform.Chain{livereloadinject.New(baseURL)}
+75 -22
View File
@@ -31,16 +31,6 @@ import (
qt "github.com/frankban/quicktest"
)
func TestServer(t *testing.T) {
c := qt.New(t)
r := runServerTest(c, true, "")
c.Assert(r.err, qt.IsNil)
c.Assert(r.homeContent, qt.Contains, "List: Hugo Commands")
c.Assert(r.homeContent, qt.Contains, "Environment: development")
}
// Issue 9518
func TestServerPanicOnConfigError(t *testing.T) {
c := qt.New(t)
@@ -51,7 +41,7 @@ func TestServerPanicOnConfigError(t *testing.T) {
linenos='table'
`
r := runServerTest(c, false, config)
r := runServerTest(c, 0, config)
c.Assert(r.err, qt.IsNotNil)
c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
@@ -62,7 +52,7 @@ func TestServerFlags(t *testing.T) {
assertPublic := func(c *qt.C, r serverTestResult, renderStaticToDisk bool) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homeContent, qt.Contains, "Environment: development")
c.Assert(r.homesContent[0], qt.Contains, "Environment: development")
c.Assert(r.publicDirnames["myfile.txt"], qt.Equals, renderStaticToDisk)
}
@@ -91,7 +81,7 @@ baseURL="https://example.org"
args = strings.Split(test.flag, "=")
}
r := runServerTest(c, true, config, args...)
r := runServerTest(c, 1, config, args...)
test.assert(c, r)
@@ -101,13 +91,71 @@ baseURL="https://example.org"
}
func TestServerBugs(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
name string
config string
flag string
numservers int
assert func(c *qt.C, r serverTestResult)
}{
// Issue 9788
{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
}},
{"PostProcess, disk", "", "--renderToDisk", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
}},
// Isue 9901
{"Multihost", `
defaultContentLanguage = 'en'
[languages]
[languages.en]
baseURL = 'https://example.com'
title = 'My blog'
weight = 1
[languages.fr]
baseURL = 'https://example.fr'
title = 'Mon blogue'
weight = 2
`, "", 2, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
for i, s := range []string{"My blog", "Mon blogue"} {
c.Assert(r.homesContent[i], qt.Contains, s)
}
}},
} {
c.Run(test.name, func(c *qt.C) {
if test.config == "" {
test.config = `
baseURL="https://example.org"
`
}
var args []string
if test.flag != "" {
args = strings.Split(test.flag, "=")
}
r := runServerTest(c, test.numservers, test.config, args...)
test.assert(c, r)
})
}
}
type serverTestResult struct {
err error
homeContent string
homesContent []string
publicDirnames map[string]bool
}
func runServerTest(c *qt.C, getHome bool, config string, args ...string) (result serverTestResult) {
func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (result serverTestResult) {
dir := createSimpleTestSite(c, testSiteConfig{configTOML: config})
sp, err := helpers.FindAvailablePort()
@@ -136,16 +184,21 @@ func runServerTest(c *qt.C, getHome bool, config string, args ...string) (result
return err
})
if getHome {
if getNumHomes > 0 {
// Esp. on slow CI machines, we need to wait a little before the web
// server is ready.
time.Sleep(567 * time.Millisecond)
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port))
c.Check(err, qt.IsNil)
c.Check(resp.StatusCode, qt.Equals, http.StatusOK)
if err == nil {
defer resp.Body.Close()
result.homeContent = helpers.ReaderToString(resp.Body)
result.homesContent = make([]string, getNumHomes)
for i := 0; i < getNumHomes; i++ {
func() {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
c.Check(err, qt.IsNil)
c.Check(resp.StatusCode, qt.Equals, http.StatusOK)
if err == nil {
defer resp.Body.Close()
result.homesContent[i] = helpers.ReaderToString(resp.Body)
}
}()
}
}
+43 -141
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2022 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.
@@ -21,8 +21,6 @@ import (
"strings"
"github.com/gohugoio/hugo/common/text"
"github.com/spf13/afero"
)
// LineMatcher contains the elements used to match an error to a line
@@ -36,14 +34,33 @@ type LineMatcher struct {
}
// LineMatcherFn is used to match a line with an error.
type LineMatcherFn func(m LineMatcher) bool
// It returns the column number or 0 if the line was found, but column could not be determinde. Returns -1 if no line match.
type LineMatcherFn func(m LineMatcher) int
// SimpleLineMatcher simply matches by line number.
var SimpleLineMatcher = func(m LineMatcher) bool {
return m.Position.LineNumber == m.LineNumber
var SimpleLineMatcher = func(m LineMatcher) int {
if m.Position.LineNumber == m.LineNumber {
// We found the line, but don't know the column.
return 0
}
return -1
}
var _ text.Positioner = ErrorContext{}
// NopLineMatcher is a matcher that always returns 1.
// This will effectively give line 1, column 1.
var NopLineMatcher = func(m LineMatcher) int {
return 1
}
// OffsetMatcher is a line matcher that matches by offset.
var OffsetMatcher = func(m LineMatcher) int {
if m.Offset+len(m.Line) >= m.Position.Offset {
// We found the line, but return 0 to signal that we want to determine
// the column from the error.
return 0
}
return -1
}
// ErrorContext contains contextual information about an error. This will
// typically be the lines surrounding some problem in a file.
@@ -56,125 +73,15 @@ type ErrorContext struct {
// The position of the error in the Lines above. 0 based.
LinesPos int
position text.Position
// The position of the content in the file. Note that this may be different from the error's position set
// in FileError.
Position text.Position
// The lexer to use for syntax highlighting.
// https://gohugo.io/content-management/syntax-highlighting/#list-of-chroma-highlighting-languages
ChromaLexer string
}
// Position returns the text position of this error.
func (e ErrorContext) Position() text.Position {
return e.position
}
var _ causer = (*ErrorWithFileContext)(nil)
// ErrorWithFileContext is an error with some additional file context related
// to that error.
type ErrorWithFileContext struct {
cause error
ErrorContext
}
func (e *ErrorWithFileContext) Error() string {
pos := e.Position()
if pos.IsValid() {
return pos.String() + ": " + e.cause.Error()
}
return e.cause.Error()
}
func (e *ErrorWithFileContext) Cause() error {
return e.cause
}
// WithFileContextForFile will try to add a file context with lines matching the given matcher.
// If no match could be found, the original error is returned with false as the second return value.
func WithFileContextForFile(e error, realFilename, filename string, fs afero.Fs, matcher LineMatcherFn) (error, bool) {
f, err := fs.Open(filename)
if err != nil {
return e, false
}
defer f.Close()
return WithFileContext(e, realFilename, f, matcher)
}
// WithFileContextForFileDefault tries to add file context using the default line matcher.
func WithFileContextForFileDefault(err error, filename string, fs afero.Fs) error {
err, _ = WithFileContextForFile(
err,
filename,
filename,
fs,
SimpleLineMatcher)
return err
}
// WithFileContextForFile will try to add a file context with lines matching the given matcher.
// If no match could be found, the original error is returned with false as the second return value.
func WithFileContext(e error, realFilename string, r io.Reader, matcher LineMatcherFn) (error, bool) {
if e == nil {
panic("error missing")
}
le := UnwrapFileError(e)
if le == nil {
var ok bool
if le, ok = ToFileError("", e).(FileError); !ok {
return e, false
}
}
var errCtx ErrorContext
posle := le.Position()
if posle.Offset != -1 {
errCtx = locateError(r, le, func(m LineMatcher) bool {
if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) {
lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber
m.Position = text.Position{LineNumber: lno}
}
return matcher(m)
})
} else {
errCtx = locateError(r, le, matcher)
}
pos := &errCtx.position
if pos.LineNumber == -1 {
return e, false
}
pos.Filename = realFilename
if le.Type() != "" {
errCtx.ChromaLexer = chromaLexerFromType(le.Type())
} else {
errCtx.ChromaLexer = chromaLexerFromFilename(realFilename)
}
return &ErrorWithFileContext{cause: e, ErrorContext: errCtx}, true
}
// UnwrapErrorWithFileContext tries to unwrap an ErrorWithFileContext from err.
// It returns nil if this is not possible.
func UnwrapErrorWithFileContext(err error) *ErrorWithFileContext {
for err != nil {
switch v := err.(type) {
case *ErrorWithFileContext:
return v
case causer:
err = v.Cause()
default:
return nil
}
}
return nil
}
func chromaLexerFromType(fileType string) string {
switch fileType {
case "html", "htm":
@@ -196,31 +103,24 @@ func chromaLexerFromFilename(filename string) string {
return chromaLexerFromType(ext)
}
func locateErrorInString(src string, matcher LineMatcherFn) ErrorContext {
func locateErrorInString(src string, matcher LineMatcherFn) *ErrorContext {
return locateError(strings.NewReader(src), &fileError{}, matcher)
}
func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext {
func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext {
if le == nil {
panic("must provide an error")
}
errCtx := ErrorContext{position: text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1}, LinesPos: -1}
ectx := &ErrorContext{LinesPos: -1, Position: text.Position{Offset: -1}}
b, err := ioutil.ReadAll(r)
if err != nil {
return errCtx
return ectx
}
pos := &errCtx.position
lepos := le.Position()
lines := strings.Split(string(b), "\n")
if lepos.ColumnNumber >= 0 {
pos.ColumnNumber = lepos.ColumnNumber
}
lineNo := 0
posBytes := 0
@@ -233,34 +133,36 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext
Offset: posBytes,
Line: line,
}
if errCtx.LinesPos == -1 && matches(m) {
pos.LineNumber = lineNo
v := matches(m)
if ectx.LinesPos == -1 && v != -1 {
ectx.Position.LineNumber = lineNo
ectx.Position.ColumnNumber = v
break
}
posBytes += len(line)
}
if pos.LineNumber != -1 {
low := pos.LineNumber - 3
if ectx.Position.LineNumber > 0 {
low := ectx.Position.LineNumber - 3
if low < 0 {
low = 0
}
if pos.LineNumber > 2 {
errCtx.LinesPos = 2
if ectx.Position.LineNumber > 2 {
ectx.LinesPos = 2
} else {
errCtx.LinesPos = pos.LineNumber - 1
ectx.LinesPos = ectx.Position.LineNumber - 1
}
high := pos.LineNumber + 2
high := ectx.Position.LineNumber + 2
if high > len(lines) {
high = len(lines)
}
errCtx.Lines = lines[low:high]
ectx.Lines = lines[low:high]
}
return errCtx
return ectx
}
+43 -19
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2022 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,8 +24,11 @@ import (
func TestErrorLocator(t *testing.T) {
c := qt.New(t)
lineMatcher := func(m LineMatcher) bool {
return strings.Contains(m.Line, "THEONE")
lineMatcher := func(m LineMatcher) int {
if strings.Contains(m.Line, "THEONE") {
return 1
}
return -1
}
lines := `LINE 1
@@ -39,35 +42,41 @@ LINE 8
`
location := locateErrorInString(lines, lineMatcher)
pos := location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"LINE 3", "LINE 4", "This is THEONE", "LINE 6", "LINE 7"})
pos := location.Position()
c.Assert(pos.LineNumber, qt.Equals, 5)
c.Assert(location.LinesPos, qt.Equals, 2)
c.Assert(locateErrorInString(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
locate := func(s string, m LineMatcherFn) *ErrorContext {
ctx := locateErrorInString(s, m)
return ctx
}
c.Assert(locate(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
location = locateErrorInString(`L1
This is THEONE
L2
`, lineMatcher)
c.Assert(location.Position().LineNumber, qt.Equals, 2)
pos = location.Position
c.Assert(pos.LineNumber, qt.Equals, 2)
c.Assert(location.LinesPos, qt.Equals, 1)
c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This is THEONE", "L2", ""})
location = locateErrorInString(`This is THEONE
location = locate(`This is THEONE
L2
`, lineMatcher)
c.Assert(location.LinesPos, qt.Equals, 0)
c.Assert(location.Lines, qt.DeepEquals, []string{"This is THEONE", "L2", ""})
location = locateErrorInString(`L1
location = locate(`L1
This THEONE
`, lineMatcher)
c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This THEONE", ""})
c.Assert(location.LinesPos, qt.Equals, 1)
location = locateErrorInString(`L1
location = locate(`L1
L2
This THEONE
`, lineMatcher)
@@ -75,12 +84,16 @@ This THEONE
c.Assert(location.LinesPos, qt.Equals, 2)
location = locateErrorInString("NO MATCH", lineMatcher)
c.Assert(location.Position().LineNumber, qt.Equals, -1)
pos = location.Position
c.Assert(pos.LineNumber, qt.Equals, 0)
c.Assert(location.LinesPos, qt.Equals, -1)
c.Assert(len(location.Lines), qt.Equals, 0)
lineMatcher = func(m LineMatcher) bool {
return m.LineNumber == 6
lineMatcher = func(m LineMatcher) int {
if m.LineNumber == 6 {
return 1
}
return -1
}
location = locateErrorInString(`A
@@ -93,14 +106,18 @@ G
H
I
J`, lineMatcher)
pos = location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"D", "E", "F", "G", "H"})
c.Assert(location.Position().LineNumber, qt.Equals, 6)
c.Assert(pos.LineNumber, qt.Equals, 6)
c.Assert(location.LinesPos, qt.Equals, 2)
// Test match EOF
lineMatcher = func(m LineMatcher) bool {
return m.LineNumber == 4
lineMatcher = func(m LineMatcher) int {
if m.LineNumber == 4 {
return 1
}
return -1
}
location = locateErrorInString(`A
@@ -108,12 +125,17 @@ B
C
`, lineMatcher)
pos = location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"B", "C", ""})
c.Assert(location.Position().LineNumber, qt.Equals, 4)
c.Assert(pos.LineNumber, qt.Equals, 4)
c.Assert(location.LinesPos, qt.Equals, 2)
offsetMatcher := func(m LineMatcher) bool {
return m.Offset == 1
offsetMatcher := func(m LineMatcher) int {
if m.Offset == 1 {
return 1
}
return -1
}
location = locateErrorInString(`A
@@ -122,7 +144,9 @@ C
D
E`, offsetMatcher)
pos = location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"A", "B", "C", "D"})
c.Assert(location.Position().LineNumber, qt.Equals, 2)
c.Assert(pos.LineNumber, qt.Equals, 2)
c.Assert(location.LinesPos, qt.Equals, 1)
}
-26
View File
@@ -19,37 +19,11 @@ import (
"errors"
"fmt"
"io"
"os"
"runtime"
"runtime/debug"
"strconv"
_errors "github.com/pkg/errors"
)
// As defined in https://godoc.org/github.com/pkg/errors
type causer interface {
Cause() error
}
type stackTracer interface {
StackTrace() _errors.StackTrace
}
// PrintStackTraceFromErr prints the error's stack trace to stdoud.
func PrintStackTraceFromErr(err error) {
FprintStackTraceFromErr(os.Stdout, err)
}
// FprintStackTraceFromErr prints the error's stack trace to w.
func FprintStackTraceFromErr(w io.Writer, err error) {
if err, ok := err.(stackTracer); ok {
for _, f := range err.StackTrace() {
fmt.Fprintf(w, "%+s:%d\n", f, f)
}
}
}
// PrintStackTrace prints the current stacktrace to w.
func PrintStackTrace(w io.Writer) {
buf := make([]byte, 1<<16)
+313 -50
View File
@@ -1,11 +1,11 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2022 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.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// Unless required by applicable lfmtaw or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
@@ -15,61 +15,306 @@ package herrors
import (
"encoding/json"
"fmt"
"io"
"path/filepath"
"github.com/bep/godartsass"
"github.com/bep/golibsass/libsass/libsasserrors"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/text"
"github.com/pelletier/go-toml/v2"
"github.com/spf13/afero"
"github.com/tdewolff/parse/v2"
"github.com/pkg/errors"
"errors"
)
var _ causer = (*fileError)(nil)
// FileError represents an error when handling a file: Parsing a config file,
// execute a template etc.
type FileError interface {
error
// ErroContext holds some context information about the error.
ErrorContext() *ErrorContext
text.Positioner
// A string identifying the type of file, e.g. JSON, TOML, markdown etc.
Type() string
// UpdatePosition updates the position of the error.
UpdatePosition(pos text.Position) FileError
// UpdateContent updates the error with a new ErrorContext from the content of the file.
UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError
}
var _ FileError = (*fileError)(nil)
// Unwrapper can unwrap errors created with fmt.Errorf.
type Unwrapper interface {
Unwrap() error
}
var (
_ FileError = (*fileError)(nil)
_ Unwrapper = (*fileError)(nil)
)
func (fe *fileError) UpdatePosition(pos text.Position) FileError {
oldFilename := fe.Position().Filename
if pos.Filename != "" && fe.fileType == "" {
_, fe.fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename))
}
if pos.Filename == "" {
pos.Filename = oldFilename
}
fe.position = pos
return fe
}
func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError {
if linematcher == nil {
linematcher = SimpleLineMatcher
}
var (
posle = fe.position
ectx *ErrorContext
)
if posle.LineNumber <= 1 && posle.Offset > 0 {
// Try to locate the line number from the content if offset is set.
ectx = locateError(r, fe, func(m LineMatcher) int {
if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) {
lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber
m.Position = text.Position{LineNumber: lno}
return linematcher(m)
}
return -1
})
} else {
ectx = locateError(r, fe, linematcher)
}
if ectx.ChromaLexer == "" {
if fe.fileType != "" {
ectx.ChromaLexer = chromaLexerFromType(fe.fileType)
} else {
ectx.ChromaLexer = chromaLexerFromFilename(fe.Position().Filename)
}
}
fe.errorContext = ectx
if ectx.Position.LineNumber > 0 {
fe.position.LineNumber = ectx.Position.LineNumber
}
if ectx.Position.ColumnNumber > 0 {
fe.position.ColumnNumber = ectx.Position.ColumnNumber
}
return fe
}
type fileError struct {
position text.Position
position text.Position
errorContext *ErrorContext
fileType string
cause error
}
func (e *fileError) ErrorContext() *ErrorContext {
return e.errorContext
}
// Position returns the text position of this error.
func (e fileError) Position() text.Position {
return e.position
}
func (e *fileError) Type() string {
return e.fileType
func (e *fileError) Error() string {
return fmt.Sprintf("%s: %s", e.position, e.causeString())
}
func (e *fileError) Error() string {
func (e *fileError) causeString() string {
if e.cause == nil {
return ""
}
return e.cause.Error()
switch v := e.cause.(type) {
// Avoid repeating the file info in the error message.
case godartsass.SassError:
return v.Message
case libsasserrors.Error:
return v.Message
default:
return v.Error()
}
}
func (f *fileError) Cause() error {
return f.cause
func (e *fileError) Unwrap() error {
return e.cause
}
// NewFileError creates a new FileError.
func NewFileError(fileType string, offset, lineNumber, columnNumber int, err error) FileError {
pos := text.Position{Offset: offset, LineNumber: lineNumber, ColumnNumber: columnNumber}
// NewFileError creates a new FileError that wraps err.
// It will try to extract the filename and line number from err.
func NewFileError(err error) FileError {
// Filetype is used to determine the Chroma lexer to use.
fileType, pos := extractFileTypePos(err)
return &fileError{cause: err, fileType: fileType, position: pos}
}
// NewFileErrorFromName creates a new FileError that wraps err.
// The value for name should identify the file, the best
// being the full filename to the file on disk.
func NewFileErrorFromName(err error, name string) FileError {
// Filetype is used to determine the Chroma lexer to use.
fileType, pos := extractFileTypePos(err)
pos.Filename = name
if fileType == "" {
_, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name))
}
return &fileError{cause: err, fileType: fileType, position: pos}
}
// NewFileErrorFromPos will use the filename and line number from pos to create a new FileError, wrapping err.
func NewFileErrorFromPos(err error, pos text.Position) FileError {
// Filetype is used to determine the Chroma lexer to use.
fileType, _ := extractFileTypePos(err)
if fileType == "" {
_, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename))
}
return &fileError{cause: err, fileType: fileType, position: pos}
}
func NewFileErrorFromFileInErr(err error, fs afero.Fs, linematcher LineMatcherFn) FileError {
fe := NewFileError(err)
pos := fe.Position()
if pos.Filename == "" {
return fe
}
f, realFilename, err2 := openFile(pos.Filename, fs)
if err2 != nil {
return fe
}
pos.Filename = realFilename
defer f.Close()
return fe.UpdateContent(f, linematcher)
}
func NewFileErrorFromFileInPos(err error, pos text.Position, fs afero.Fs, linematcher LineMatcherFn) FileError {
if err == nil {
panic("err is nil")
}
f, realFilename, err2 := openFile(pos.Filename, fs)
if err2 != nil {
return NewFileErrorFromPos(err, pos)
}
pos.Filename = realFilename
defer f.Close()
return NewFileErrorFromPos(err, pos).UpdateContent(f, linematcher)
}
// NewFileErrorFromFile is a convenience method to create a new FileError from a file.
func NewFileErrorFromFile(err error, filename string, fs afero.Fs, linematcher LineMatcherFn) FileError {
if err == nil {
panic("err is nil")
}
f, realFilename, err2 := openFile(filename, fs)
if err2 != nil {
return NewFileErrorFromName(err, realFilename)
}
defer f.Close()
return NewFileErrorFromName(err, realFilename).UpdateContent(f, linematcher)
}
func openFile(filename string, fs afero.Fs) (afero.File, string, error) {
realFilename := filename
// We want the most specific filename possible in the error message.
fi, err2 := fs.Stat(filename)
if err2 == nil {
if s, ok := fi.(interface {
Filename() string
}); ok {
realFilename = s.Filename()
}
}
f, err2 := fs.Open(filename)
if err2 != nil {
return nil, realFilename, err2
}
return f, realFilename, nil
}
// Cause returns the underlying error or itself if it does not implement Unwrap.
func Cause(err error) error {
if u := errors.Unwrap(err); u != nil {
return u
}
return err
}
func extractFileTypePos(err error) (string, text.Position) {
err = Cause(err)
var fileType string
// LibSass, DartSass
if pos := extractPosition(err); pos.LineNumber > 0 || pos.Offset > 0 {
_, fileType = paths.FileAndExtNoDelimiter(pos.Filename)
return fileType, pos
}
// Default to line 1 col 1 if we don't find any better.
pos := text.Position{
Offset: -1,
LineNumber: 1,
ColumnNumber: 1,
}
// JSON errors.
offset, typ := extractOffsetAndType(err)
if fileType == "" {
fileType = typ
}
if offset >= 0 {
pos.Offset = offset
}
// The error type from the minifier contains line number and column number.
if line, col := exctractLineNumberAndColumnNumber(err); line >= 0 {
pos.LineNumber = line
pos.ColumnNumber = col
return fileType, pos
}
// Look in the error message for the line number.
for _, handle := range lineNumberExtractors {
lno, col := handle(err)
if lno > 0 {
pos.ColumnNumber = col
pos.LineNumber = lno
break
}
}
if fileType == "" && pos.Filename != "" {
_, fileType = paths.FileAndExtNoDelimiter(pos.Filename)
}
return fileType, pos
}
// UnwrapFileError tries to unwrap a FileError from err.
// It returns nil if this is not possible.
func UnwrapFileError(err error) FileError {
@@ -77,49 +322,38 @@ func UnwrapFileError(err error) FileError {
switch v := err.(type) {
case FileError:
return v
case causer:
err = v.Cause()
default:
return nil
err = errors.Unwrap(err)
}
}
return nil
}
// ToFileErrorWithOffset will return a new FileError with a line number
// with the given offset from the original.
func ToFileErrorWithOffset(fe FileError, offset int) FileError {
pos := fe.Position()
return ToFileErrorWithLineNumber(fe, pos.LineNumber+offset)
}
// ToFileErrorWithOffset will return a new FileError with the given line number.
func ToFileErrorWithLineNumber(fe FileError, lineNumber int) FileError {
pos := fe.Position()
pos.LineNumber = lineNumber
return &fileError{cause: fe, fileType: fe.Type(), position: pos}
}
// ToFileError will convert the given error to an error supporting
// the FileError interface.
func ToFileError(fileType string, err error) FileError {
for _, handle := range lineNumberExtractors {
lno, col := handle(err)
offset, typ := extractOffsetAndType(err)
if fileType == "" {
fileType = typ
}
if lno > 0 || offset != -1 {
return NewFileError(fileType, offset, lno, col, err)
// UnwrapFileErrors tries to unwrap all FileError.
func UnwrapFileErrors(err error) []FileError {
var errs []FileError
for err != nil {
if v, ok := err.(FileError); ok {
errs = append(errs, v)
}
err = errors.Unwrap(err)
}
// Fall back to the pointing to line number 1.
return NewFileError(fileType, -1, 1, 1, err)
return errs
}
// UnwrapFileErrorsWithErrorContext tries to unwrap all FileError in err that has an ErrorContext.
func UnwrapFileErrorsWithErrorContext(err error) []FileError {
var errs []FileError
for err != nil {
if v, ok := err.(FileError); ok && v.ErrorContext() != nil {
errs = append(errs, v)
}
err = errors.Unwrap(err)
}
return errs
}
func extractOffsetAndType(e error) (int, string) {
e = errors.Cause(e)
switch v := e.(type) {
case *json.UnmarshalTypeError:
return int(v.Offset), "json"
@@ -129,3 +363,32 @@ func extractOffsetAndType(e error) (int, string) {
return -1, ""
}
}
func exctractLineNumberAndColumnNumber(e error) (int, int) {
switch v := e.(type) {
case *parse.Error:
return v.Line, v.Column
case *toml.DecodeError:
return v.Position()
}
return -1, -1
}
func extractPosition(e error) (pos text.Position) {
switch v := e.(type) {
case godartsass.SassError:
span := v.Span
start := span.Start
filename, _ := paths.UrlToFilename(span.Url)
pos.Filename = filename
pos.Offset = start.Offset
pos.ColumnNumber = start.Column
case libsasserrors.Error:
pos.Filename = v.File
pos.LineNumber = v.Line
pos.ColumnNumber = v.Column
}
return
}
+37 -9
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2022 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,14 +14,44 @@
package herrors
import (
"fmt"
"strings"
"testing"
"github.com/pkg/errors"
"errors"
"github.com/gohugoio/hugo/common/text"
qt "github.com/frankban/quicktest"
)
func TestToLineNumberError(t *testing.T) {
func TestNewFileError(t *testing.T) {
t.Parallel()
c := qt.New(t)
fe := NewFileErrorFromName(errors.New("bar"), "foo.html")
c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`)
lines := ""
for i := 1; i <= 100; i++ {
lines += fmt.Sprintf("line %d\n", i)
}
fe.UpdatePosition(text.Position{LineNumber: 32, ColumnNumber: 2})
c.Assert(fe.Error(), qt.Equals, `"foo.html:32:2": bar`)
fe.UpdatePosition(text.Position{LineNumber: 0, ColumnNumber: 0, Offset: 212})
fe.UpdateContent(strings.NewReader(lines), nil)
c.Assert(fe.Error(), qt.Equals, `"foo.html:32:0": bar`)
errorContext := fe.ErrorContext()
c.Assert(errorContext, qt.IsNotNil)
c.Assert(errorContext.Lines, qt.DeepEquals, []string{"line 30", "line 31", "line 32", "line 33", "line 34"})
c.Assert(errorContext.LinesPos, qt.Equals, 2)
c.Assert(errorContext.ChromaLexer, qt.Equals, "go-html-template")
}
func TestNewFileErrorExtractFromMessage(t *testing.T) {
t.Parallel()
c := qt.New(t)
@@ -37,18 +67,16 @@ func TestToLineNumberError(t *testing.T) {
{errors.New("parse failed: template: _default/bundle-resource-meta.html:11: unexpected in operand"), 0, 11, 1},
{errors.New(`failed:: template: _default/bundle-resource-meta.html:2:7: executing "main" at <.Titles>`), 0, 2, 7},
{errors.New(`failed to load translations: (6, 7): was expecting token =, but got "g" instead`), 0, 6, 7},
{errors.New(`execute of template failed: template: index.html:2:5: executing "index.html" at <partial "foo.html" .>: error calling partial: "/layouts/partials/foo.html:3:6": execute of template failed: template: partials/foo.html:3:6: executing "partials/foo.html" at <.ThisDoesNotExist>: can't evaluate field ThisDoesNotExist in type *hugolib.pageStat`), 0, 2, 5},
} {
got := ToFileError("template", test.in)
got := NewFileErrorFromName(test.in, "test.txt")
errMsg := qt.Commentf("[%d][%T]", i, got)
le, ok := got.(FileError)
c.Assert(ok, qt.Equals, true)
c.Assert(ok, qt.Equals, true, errMsg)
pos := le.Position()
pos := got.Position()
c.Assert(pos.LineNumber, qt.Equals, test.lineNumber, errMsg)
c.Assert(pos.ColumnNumber, qt.Equals, test.columnNumber, errMsg)
c.Assert(errors.Cause(got), qt.Not(qt.IsNil))
c.Assert(errors.Unwrap(got), qt.Not(qt.IsNil))
}
}
+5 -19
View File
@@ -16,36 +16,22 @@ package herrors
import (
"regexp"
"strconv"
"github.com/pkg/errors"
"github.com/pelletier/go-toml/v2"
)
var lineNumberExtractors = []lineNumberExtractor{
// Template/shortcode parse errors
newLineNumberErrHandlerFromRegexp(".*:(\\d+):(\\d*):"),
newLineNumberErrHandlerFromRegexp(".*:(\\d+):"),
newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`),
newLineNumberErrHandlerFromRegexp(`:(\d+):`),
// TOML parse errors
tomlLineNumberExtractor,
// YAML parse errors
newLineNumberErrHandlerFromRegexp("line (\\d+):"),
newLineNumberErrHandlerFromRegexp(`line (\d+):`),
// i18n bundle errors
newLineNumberErrHandlerFromRegexp("\\((\\d+),\\s(\\d*)"),
newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`),
}
type lineNumberExtractor func(e error) (int, int)
var tomlLineNumberExtractor = func(e error) (int, int) {
e = errors.Cause(e)
if terr, ok := e.(*toml.DecodeError); ok {
return terr.Position()
}
return -1, -1
}
func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor {
re := regexp.MustCompile(expression)
return extractLineNo(re)
@@ -72,6 +58,6 @@ func extractLineNo(re *regexp.Regexp) lineNumberExtractor {
return lno, col
}
return -1, col
return 0, col
}
}
+40
View File
@@ -20,7 +20,9 @@ import (
"context"
"reflect"
"sync"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types"
)
@@ -168,6 +170,44 @@ func GetMethodIndexByName(tp reflect.Type, name string) int {
return m.Index
}
var (
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
asTimeProviderType = reflect.TypeOf((*htime.AsTimeProvider)(nil)).Elem()
)
// IsTime returns whether tp is a time.Time type or if it can be converted into one
// in ToTime.
func IsTime(tp reflect.Type) bool {
if tp == timeType {
return true
}
if tp.Implements(asTimeProviderType) {
return true
}
return false
}
// AsTime returns v as a time.Time if possible.
// The given location is only used if the value implements AsTimeProvider (e.g. go-toml local).
// A zero Time and false is returned if this isn't possible.
// Note that this function does not accept string dates.
func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
if v.Kind() == reflect.Interface {
return AsTime(v.Elem(), loc)
}
if v.Type() == timeType {
return v.Interface().(time.Time), true
}
if v.Type().Implements(asTimeProviderType) {
return v.Interface().(htime.AsTimeProvider).AsTime(loc), true
}
return time.Time{}, false
}
// Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
func indirectInterface(v reflect.Value) reflect.Value {
if v.Kind() != reflect.Interface {
+20 -5
View File
@@ -17,10 +17,9 @@ import (
"strings"
"time"
"github.com/bep/clock"
"github.com/spf13/cast"
toml "github.com/pelletier/go-toml/v2"
"github.com/gohugoio/locales"
)
@@ -74,6 +73,8 @@ var (
"November",
"December",
}
Clock = clock.System()
)
func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
@@ -136,15 +137,29 @@ func (f TimeFormatter) Format(t time.Time, layout string) string {
func ToTimeInDefaultLocationE(i any, location *time.Location) (tim time.Time, err error) {
switch vv := i.(type) {
case toml.LocalDate:
return vv.AsTime(location), nil
case toml.LocalDateTime:
case AsTimeProvider:
return vv.AsTime(location), nil
// issue #8895
// datetimes parsed by `go-toml` have empty zone name
// convert back them into string and use `cast`
// TODO(bep) add tests, make sure we really need this.
case time.Time:
i = vv.Format(time.RFC3339)
}
return cast.ToTimeInDefaultLocationE(i, location)
}
// Now returns time.Now() or time value based on the `clock` flag.
// Use this function to fake time inside hugo.
func Now() time.Time {
return Clock.Now()
}
func Since(t time.Time) time.Duration {
return Clock.Since(t)
}
// AsTimeProvider is implemented by go-toml's LocalDate and LocalDateTime.
type AsTimeProvider interface {
AsTime(zone *time.Location) time.Time
}
+2 -3
View File
@@ -14,13 +14,12 @@
package hugio
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -60,7 +59,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
}
if !fi.IsDir() {
return errors.Errorf("%q is not a directory", from)
return fmt.Errorf("%q is not a directory", from)
}
err = fs.MkdirAll(to, 0777) // before umask
+8
View File
@@ -38,6 +38,8 @@ const (
)
var (
// buildDate allows vendor-specified build date when .git/ is unavailable.
buildDate string
// vendorInfo contains vendor notes about the current build.
vendorInfo string
)
@@ -53,6 +55,9 @@ type Info struct {
// It can be any string, but it will be all lower case.
Environment string
// version of go that the Hugo binary was built with
GoVersion string
deps []*Dependency
}
@@ -87,12 +92,14 @@ func NewInfo(environment string, deps []*Dependency) Info {
var (
commitHash string
buildDate string
goVersion string
)
bi := getBuildInfo()
if bi != nil {
commitHash = bi.Revision
buildDate = bi.RevisionTime
goVersion = bi.GoVersion
}
return Info{
@@ -100,6 +107,7 @@ func NewInfo(environment string, deps []*Dependency) Info {
BuildDate: buildDate,
Environment: environment,
deps: deps,
GoVersion: goVersion,
}
}
+1
View File
@@ -32,6 +32,7 @@ func TestHugoInfo(t *testing.T) {
if bi != nil {
c.Assert(hugoInfo.CommitHash, qt.Equals, bi.Revision)
c.Assert(hugoInfo.BuildDate, qt.Equals, bi.RevisionTime)
c.Assert(hugoInfo.GoVersion, qt.Equals, bi.GoVersion)
}
c.Assert(hugoInfo.Environment, qt.Equals, "production")
c.Assert(string(hugoInfo.Generator()), qt.Contains, fmt.Sprintf("Hugo %s", hugoInfo.Version()))
+87 -56
View File
@@ -16,7 +16,9 @@ package hugo
import (
"fmt"
"io"
"math"
"runtime"
"strconv"
"strings"
"github.com/gohugoio/hugo/compare"
@@ -25,8 +27,9 @@ import (
// Version represents the Hugo build version.
type Version struct {
// Major and minor version.
Number float32
Major int
Minor int
// Increment this for bug releases
PatchLevel int
@@ -42,7 +45,7 @@ var (
)
func (v Version) String() string {
return version(v.Number, v.PatchLevel, v.Suffix)
return version(v.Major, v.Minor, v.PatchLevel, v.Suffix)
}
// Version returns the Hugo version.
@@ -50,6 +53,11 @@ func (v Version) Version() VersionString {
return VersionString(v.String())
}
// Compare implements the compare.Comparer interface.
func (h Version) Compare(other any) int {
return compareVersions(h, other)
}
// VersionString represents a Hugo version string.
type VersionString string
@@ -60,7 +68,7 @@ func (h VersionString) String() string {
// Compare implements the compare.Comparer interface.
func (h VersionString) Compare(other any) int {
v := MustParseVersion(h.String())
return compareVersionsWithSuffix(v.Number, v.PatchLevel, v.Suffix, other)
return compareVersions(v, other)
}
// Eq implements the compare.Eqer interface.
@@ -84,10 +92,7 @@ func ParseVersion(s string) (Version, error) {
}
}
v, p := parseVersion(s)
vv.Number = v
vv.PatchLevel = p
vv.Major, vv.Minor, vv.PatchLevel = parseVersion(s)
return vv, nil
}
@@ -110,18 +115,20 @@ func (v Version) ReleaseVersion() Version {
// Next returns the next Hugo release version.
func (v Version) Next() Version {
return Version{Number: v.Number + 0.01}
return Version{Major: v.Major, Minor: v.Minor + 1}
}
// Prev returns the previous Hugo release version.
func (v Version) Prev() Version {
return Version{Number: v.Number - 0.01}
return Version{Major: v.Major, Minor: v.Minor - 1}
}
// NextPatchLevel returns the next patch/bugfix Hugo version.
// This will be a patch increment on the previous Hugo version.
func (v Version) NextPatchLevel(level int) Version {
return Version{Number: v.Number - 0.01, PatchLevel: level}
prev := v.Prev()
prev.PatchLevel = level
return prev
}
// BuildVersionString creates a version string. This is what you see when
@@ -146,6 +153,10 @@ func BuildVersionString() string {
osArch := bi.GoOS + "/" + bi.GoArch
date := bi.RevisionTime
if date == "" {
// Accept vendor-specified build date if .git/ is unavailable.
date = buildDate
}
if date == "" {
date = "unknown"
}
@@ -160,11 +171,11 @@ func BuildVersionString() string {
return versionString
}
func version(version float32, patchVersion int, suffix string) string {
if patchVersion > 0 || version > 0.53 {
return fmt.Sprintf("%.2f.%d%s", version, patchVersion, suffix)
func version(major, minor, patch int, suffix string) string {
if patch > 0 || minor > 53 {
return fmt.Sprintf("%d.%d.%d%s", major, minor, patch, suffix)
}
return fmt.Sprintf("%.2f%s", version, suffix)
return fmt.Sprintf("%d.%d%s", major, minor, suffix)
}
// CompareVersion compares the given version string or number against the
@@ -172,26 +183,41 @@ func version(version float32, patchVersion int, suffix string) string {
// It returns -1 if the given version is less than, 0 if equal and 1 if greater than
// the running version.
func CompareVersion(version any) int {
return compareVersionsWithSuffix(CurrentVersion.Number, CurrentVersion.PatchLevel, CurrentVersion.Suffix, version)
return compareVersions(CurrentVersion, version)
}
func compareVersions(inVersion float32, inPatchVersion int, in any) int {
return compareVersionsWithSuffix(inVersion, inPatchVersion, "", in)
}
func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix string, in any) int {
func compareVersions(inVersion Version, in any) int {
var c int
switch d := in.(type) {
case float64:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(d, inVersion)
case float32:
c = compareFloatVersions(inVersion, d)
c = compareFloatWithVersion(float64(d), inVersion)
case int:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(float64(d), inVersion)
case int32:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(float64(d), inVersion)
case int64:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(float64(d), inVersion)
case Version:
if d.Major == inVersion.Major && d.Minor == inVersion.Minor && d.PatchLevel == inVersion.PatchLevel {
return strings.Compare(inVersion.Suffix, d.Suffix)
}
if d.Major > inVersion.Major {
return 1
} else if d.Major < inVersion.Major {
return -1
}
if d.Minor > inVersion.Minor {
return 1
} else if d.Minor < inVersion.Minor {
return -1
}
if d.PatchLevel > inVersion.PatchLevel {
return 1
} else if d.PatchLevel < inVersion.PatchLevel {
return -1
}
default:
s, err := cast.ToStringE(in)
if err != nil {
@@ -202,50 +228,55 @@ func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix str
if err != nil {
return -1
}
return inVersion.Compare(v)
if v.Number == inVersion && v.PatchLevel == inPatchVersion {
return strings.Compare(suffix, v.Suffix)
}
if v.Number < inVersion || (v.Number == inVersion && v.PatchLevel < inPatchVersion) {
return -1
}
return 1
}
if c == 0 && suffix != "" {
return 1
}
return c
}
func parseVersion(s string) (float32, int) {
var (
v float32
p int
)
if strings.Count(s, ".") == 2 {
li := strings.LastIndex(s, ".")
p = cast.ToInt(s[li+1:])
s = s[:li]
func parseVersion(s string) (int, int, int) {
var major, minor, patch int
parts := strings.Split(s, ".")
if len(parts) > 0 {
major, _ = strconv.Atoi(parts[0])
}
if len(parts) > 1 {
minor, _ = strconv.Atoi(parts[1])
}
if len(parts) > 2 {
patch, _ = strconv.Atoi(parts[2])
}
v = float32(cast.ToFloat64(s))
return v, p
return major, minor, patch
}
func compareFloatVersions(version float32, v float32) int {
if v == version {
// compareFloatWithVersion compares v1 with v2.
// It returns -1 if v1 is less than v2, 0 if v1 is equal to v2 and 1 if v1 is greater than v2.
func compareFloatWithVersion(v1 float64, v2 Version) int {
mf, minf := math.Modf(v1)
v1maj := int(mf)
v1min := int(minf * 100)
if v2.Major == v1maj && v2.Minor == v1min {
return 0
}
if v < version {
if v1maj > v2.Major {
return 1
}
if v1maj < v2.Major {
return -1
}
return 1
if v1min > v2.Minor {
return 1
}
return -1
}
func GoMinorVersion() int {
+4 -3
View File
@@ -16,7 +16,8 @@ package hugo
// CurrentVersion represents the current build version.
// This should be the only one.
var CurrentVersion = Version{
Number: 0.97,
PatchLevel: 2,
Suffix: "",
Major: 0,
Minor: 102,
PatchLevel: 0,
Suffix: "-DEV",
}
+24 -25
View File
@@ -22,10 +22,10 @@ import (
func TestHugoVersion(t *testing.T) {
c := qt.New(t)
c.Assert(version(0.15, 0, "-DEV"), qt.Equals, "0.15-DEV")
c.Assert(version(0.15, 2, "-DEV"), qt.Equals, "0.15.2-DEV")
c.Assert(version(0, 15, 0, "-DEV"), qt.Equals, "0.15-DEV")
c.Assert(version(0, 15, 2, "-DEV"), qt.Equals, "0.15.2-DEV")
v := Version{Number: 0.21, PatchLevel: 0, Suffix: "-DEV"}
v := Version{Minor: 21, Suffix: "-DEV"}
c.Assert(v.ReleaseVersion().String(), qt.Equals, "0.21")
c.Assert(v.String(), qt.Equals, "0.21-DEV")
@@ -39,37 +39,36 @@ func TestHugoVersion(t *testing.T) {
// We started to use full semver versions even for main
// releases in v0.54.0
v = Version{Number: 0.53, PatchLevel: 0}
v = Version{Minor: 53, PatchLevel: 0}
c.Assert(v.String(), qt.Equals, "0.53")
c.Assert(v.Next().String(), qt.Equals, "0.54.0")
c.Assert(v.Next().Next().String(), qt.Equals, "0.55.0")
v = Version{Number: 0.54, PatchLevel: 0, Suffix: "-DEV"}
v = Version{Minor: 54, PatchLevel: 0, Suffix: "-DEV"}
c.Assert(v.String(), qt.Equals, "0.54.0-DEV")
}
func TestCompareVersions(t *testing.T) {
c := qt.New(t)
c.Assert(compareVersions(0.20, 0, 0.20), qt.Equals, 0)
c.Assert(compareVersions(0.20, 0, float32(0.20)), qt.Equals, 0)
c.Assert(compareVersions(0.20, 0, float64(0.20)), qt.Equals, 0)
c.Assert(compareVersions(0.19, 1, 0.20), qt.Equals, 1)
c.Assert(compareVersions(0.19, 3, "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(0.19, 1, 0.01), qt.Equals, -1)
c.Assert(compareVersions(0, 1, 3), qt.Equals, 1)
c.Assert(compareVersions(0, 1, int32(3)), qt.Equals, 1)
c.Assert(compareVersions(0, 1, int64(3)), qt.Equals, 1)
c.Assert(compareVersions(0.20, 0, "0.20"), qt.Equals, 0)
c.Assert(compareVersions(0.20, 1, "0.20.1"), qt.Equals, 0)
c.Assert(compareVersions(0.20, 1, "0.20"), qt.Equals, -1)
c.Assert(compareVersions(0.20, 0, "0.20.1"), qt.Equals, 1)
c.Assert(compareVersions(0.20, 1, "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(0.21, 1, "0.22.1"), qt.Equals, 1)
c.Assert(compareVersions(0.22, 0, "0.22-DEV"), qt.Equals, -1)
c.Assert(compareVersions(0.22, 0, "0.22.1-DEV"), qt.Equals, 1)
c.Assert(compareVersionsWithSuffix(0.22, 0, "-DEV", "0.22"), qt.Equals, 1)
c.Assert(compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22"), qt.Equals, -1)
c.Assert(compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22.1-DEV"), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.0"), 0.20), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.0"), float32(0.20)), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.0"), float64(0.20)), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.19.1"), 0.20), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.19.3"), "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.1"), 3), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.1"), int32(3)), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.1"), int64(3)), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.20"), "0.20"), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.1"), "0.20.1"), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.1"), "0.20"), qt.Equals, -1)
c.Assert(compareVersions(MustParseVersion("0.20.0"), "0.20.1"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.20.1"), "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.21.1"), "0.22.1"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.22.0"), "0.22-DEV"), qt.Equals, -1)
c.Assert(compareVersions(MustParseVersion("0.22.0"), "0.22.1-DEV"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.22.0-DEV"), "0.22"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.22.1-DEV"), "0.22"), qt.Equals, -1)
c.Assert(compareVersions(MustParseVersion("0.22.1-DEV"), "0.22.1-DEV"), qt.Equals, 0)
}
func TestParseHugoVersion(t *testing.T) {
+6 -1
View File
@@ -240,6 +240,11 @@ func NewBasicLoggerForWriter(t jww.Threshold, w io.Writer) Logger {
return newLogger(t, jww.LevelError, w, ioutil.Discard, false)
}
// RemoveANSIColours removes all ANSI colours from the given string.
func RemoveANSIColours(s string) string {
return ansiColorRe.ReplaceAllString(s, "")
}
var (
ansiColorRe = regexp.MustCompile("(?s)\\033\\[\\d*(;\\d*)*m")
errorRe = regexp.MustCompile("^(ERROR|FATAL|WARN)")
@@ -285,7 +290,7 @@ func InitGlobalLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, lo
}
func getLogWriters(outHandle, logHandle io.Writer) (io.Writer, io.Writer) {
isTerm := terminal.IsTerminal(os.Stdout)
isTerm := terminal.PrintANSIColors(os.Stdout)
if logHandle != ioutil.Discard && isTerm {
// Remove any Ansi coloring from log output
logHandle = ansiCleaner{w: logHandle}
+12
View File
@@ -46,3 +46,15 @@ func TestLoggerToWriterWithPrefix(t *testing.T) {
c.Assert(b.String(), qt.Equals, "myprefix: Hello Hugo!\n")
}
func TestRemoveANSIColours(t *testing.T) {
c := qt.New(t)
c.Assert(RemoveANSIColours(""), qt.Equals, "")
c.Assert(RemoveANSIColours("\033[31m"), qt.Equals, "")
c.Assert(RemoveANSIColours("\033[31mHello"), qt.Equals, "Hello")
c.Assert(RemoveANSIColours("\033[31mHello\033[0m"), qt.Equals, "Hello")
c.Assert(RemoveANSIColours("\033[31mHello\033[0m World"), qt.Equals, "Hello World")
c.Assert(RemoveANSIColours("\033[31mHello\033[0m World\033[31m!"), qt.Equals, "Hello World!")
c.Assert(RemoveANSIColours("\x1b[90m 5 |"), qt.Equals, " 5 |")
}
+27
View File
@@ -17,6 +17,7 @@ import (
"fmt"
"net/url"
"path"
"path/filepath"
"strings"
)
@@ -152,3 +153,29 @@ func Uglify(in string) string {
// /section/name.html -> /section/name.html
return path.Clean(in)
}
// UrlToFilename converts the URL s to a filename.
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
func UrlToFilename(s string) (string, bool) {
u, err := url.ParseRequestURI(s)
if err != nil {
return filepath.FromSlash(s), false
}
p := u.Path
if p == "" {
p, _ = url.QueryUnescape(u.Opaque)
return filepath.FromSlash(p), true
}
p = filepath.FromSlash(p)
if u.Host != "" {
// C:\data\file.txt
p = strings.ToUpper(u.Host) + ":" + p
}
return p, true
}
+10 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2022 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.
@@ -29,6 +29,15 @@ const (
noticeColor = "\033[1;36m%s\033[0m"
)
// PrintANSIColors returns false if NO_COLOR env variable is set,
// else IsTerminal(f).
func PrintANSIColors(f *os.File) bool {
if os.Getenv("NO_COLOR") != "" {
return false
}
return IsTerminal(f)
}
// IsTerminal return true if the file descriptor is terminal and the TERM
// environment variable isn't a dumb one.
func IsTerminal(f *os.File) bool {
+1 -1
View File
@@ -83,7 +83,7 @@ func createPositionStringFormatter(formatStr string) func(p Position) string {
msg := fmt.Sprintf(format, args...)
if terminal.IsTerminal(os.Stdout) {
if terminal.PrintANSIColors(os.Stdout) {
return terminal.Notice(msg)
}
+15
View File
@@ -61,3 +61,18 @@ func Puts(s string) string {
}
return s + "\n"
}
// VisitLinesAfter calls the given function for each line, including newlines, in the given string.
func VisitLinesAfter(s string, fn func(line string)) {
high := strings.IndexRune(s, '\n')
for high != -1 {
fn(s[:high+1])
s = s[high+1:]
high = strings.IndexRune(s, '\n')
}
if s != "" {
fn(s)
}
}
+33
View File
@@ -41,3 +41,36 @@ func TestPuts(t *testing.T) {
c.Assert(Puts("\nA\n"), qt.Equals, "\nA\n")
c.Assert(Puts(""), qt.Equals, "")
}
func TestVisitLinesAfter(t *testing.T) {
const lines = `line 1
line 2
line 3`
var collected []string
VisitLinesAfter(lines, func(s string) {
collected = append(collected, s)
})
c := qt.New(t)
c.Assert(collected, qt.DeepEquals, []string{"line 1\n", "line 2\n", "\n", "line 3"})
}
func BenchmarkVisitLinesAfter(b *testing.B) {
const lines = `line 1
line 2
line 3`
for i := 0; i < b.N; i++ {
VisitLinesAfter(lines, func(s string) {
})
}
}
+3
View File
@@ -17,12 +17,15 @@ package compare
// The semantics of equals is that the two value are interchangeable
// in the Hugo templates.
type Eqer interface {
// Eq returns whether this value is equal to the other.
// This is for internal use.
Eq(other any) bool
}
// ProbablyEqer is an equal check that may return false positives, but never
// a false negative.
type ProbablyEqer interface {
// For internal use.
ProbablyEq(other any) bool
}
+2 -3
View File
@@ -14,12 +14,11 @@
package config
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/types"
"github.com/gobwas/glob"
@@ -207,7 +206,7 @@ func DecodeServer(cfg Provider) (*Server, error) {
// There are some tricky infinite loop situations when dealing
// when the target does not have a trailing slash.
// This can certainly be handled better, but not time for that now.
return nil, errors.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
return nil, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
}
s.Redirects[i] = redir
}
+10 -4
View File
@@ -14,14 +14,13 @@
package config
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/gohugoio/hugo/common/herrors"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/maps"
@@ -60,7 +59,14 @@ func FromConfigString(config, configType string) (Provider, error) {
func FromFile(fs afero.Fs, filename string) (Provider, error) {
m, err := loadConfigFromFile(fs, filename)
if err != nil {
return nil, herrors.WithFileContextForFileDefault(err, filename, fs)
fe := herrors.UnwrapFileError(err)
if fe != nil {
pos := fe.Position()
pos.Filename = filename
fe.UpdatePosition(pos)
return nil, err
}
return nil, herrors.NewFileErrorFromFile(err, filename, fs, nil)
}
return NewFrom(m), nil
}
@@ -132,7 +138,7 @@ func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provid
if err != nil {
// This will be used in error reporting, use the most specific value.
dirnames = []string{path}
return errors.Wrapf(err, "failed to unmarshl config for path %q", path)
return fmt.Errorf("failed to unmarshl config for path %q: %w", path, err)
}
var keyPath []string
+7 -7
View File
@@ -27,7 +27,7 @@ import (
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/paths"
"github.com/pkg/errors"
"errors"
"github.com/gohugoio/hugo/hugofs/files"
@@ -94,11 +94,11 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string) error {
}
if ext == "" {
return "", errors.Errorf("failed to resolve %q to a archetype template", targetPath)
return "", fmt.Errorf("failed to resolve %q to a archetype template", targetPath)
}
if !files.IsContentFile(b.targetPath) {
return "", errors.Errorf("target path %q is not a known content format", b.targetPath)
return "", fmt.Errorf("target path %q is not a known content format", b.targetPath)
}
return b.buildFile()
@@ -188,14 +188,14 @@ func (b *contentBuilder) buildDir() error {
in, err := meta.Open()
if err != nil {
return errors.Wrap(err, "failed to open non-content file")
return fmt.Errorf("failed to open non-content file: %w", err)
}
targetFilename := filepath.Join(baseDir, b.targetPath, strings.TrimPrefix(filename, b.archetypeFilename))
targetDir := filepath.Dir(targetFilename)
if err := b.sourceFs.MkdirAll(targetDir, 0o777); err != nil && !os.IsExist(err) {
return errors.Wrapf(err, "failed to create target directory for %q", targetDir)
return fmt.Errorf("failed to create target directory for %q: %w", targetDir, err)
}
out, err := b.sourceFs.Create(targetFilename)
@@ -329,7 +329,7 @@ func (b *contentBuilder) mapArcheTypeDir() error {
w := hugofs.NewWalkway(walkCfg)
if err := w.Walk(); err != nil {
return errors.Wrapf(err, "failed to walk archetype dir %q", b.archetypeFilename)
return fmt.Errorf("failed to walk archetype dir %q: %w", b.archetypeFilename, err)
}
b.dirMap = m
@@ -374,7 +374,7 @@ func (b *contentBuilder) usesSiteVar(filename string) (bool, error) {
}
bb, err := afero.ReadFile(b.archeTypeFs, filename)
if err != nil {
return false, errors.Wrap(err, "failed to open archetype file")
return false, fmt.Errorf("failed to open archetype file: %w", err)
}
return bytes.Contains(bb, []byte(".Site")) || bytes.Contains(bb, []byte("site.")), nil
+2 -1
View File
@@ -34,11 +34,12 @@ import (
"strings"
"sync"
"errors"
"github.com/dustin/go-humanize"
"github.com/gobwas/glob"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/media"
"github.com/pkg/errors"
"github.com/spf13/afero"
jww "github.com/spf13/jwalterweatherman"
"golang.org/x/text/unicode/norm"
+4 -3
View File
@@ -20,12 +20,13 @@ import (
"fmt"
"regexp"
"errors"
"github.com/gobwas/glob"
"github.com/gohugoio/hugo/config"
hglob "github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/media"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
const deploymentConfigKey = "deployment"
@@ -126,7 +127,7 @@ func decodeConfig(cfg config.Provider) (deployConfig, error) {
return dcfg, err
}
for _, tgt := range dcfg.Targets {
if tgt == nil {
if *tgt == (target{}) {
return dcfg, errors.New("empty deployment target")
}
if err := tgt.parseIncludeExclude(); err != nil {
@@ -135,7 +136,7 @@ func decodeConfig(cfg config.Provider) (deployConfig, error) {
}
var err error
for _, m := range dcfg.Matchers {
if m == nil {
if *m == (matcher{}) {
return dcfg, errors.New("empty deployment matcher")
}
m.re, err = regexp.Compile(m.Pattern)
+6 -7
View File
@@ -1,12 +1,11 @@
package deps
import (
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
@@ -186,11 +185,11 @@ func (d *Deps) SetTextTmpl(tmpl tpl.TemplateParseFinder) {
func (d *Deps) LoadResources() error {
// Note that the translations need to be loaded before the templates.
if err := d.translationProvider.Update(d); err != nil {
return errors.Wrap(err, "loading translations")
return fmt.Errorf("loading translations: %w", err)
}
if err := d.templateProvider.Update(d); err != nil {
return errors.Wrap(err, "loading templates")
return fmt.Errorf("loading templates: %w", err)
}
return nil
@@ -236,18 +235,18 @@ func New(cfg DepsCfg) (*Deps, error) {
securityConfig, err := security.DecodeConfig(cfg.Cfg)
if err != nil {
return nil, errors.WithMessage(err, "failed to create security config from configuration")
return nil, fmt.Errorf("failed to create security config from configuration: %w", err)
}
execHelper := hexec.New(securityConfig)
ps, err := helpers.NewPathSpec(fs, cfg.Language, logger)
if err != nil {
return nil, errors.Wrap(err, "create PathSpec")
return nil, fmt.Errorf("create PathSpec: %w", err)
}
fileCaches, err := filecache.NewCaches(ps)
if err != nil {
return nil, errors.WithMessage(err, "failed to create file caches from configuration")
return nil, fmt.Errorf("failed to create file caches from configuration: %w", err)
}
errorHandler := &globalErrHandler{}
-1
View File
@@ -163,7 +163,6 @@
"Milli",
"Mittwoch",
"mkdir",
"mmark",
"modh",
"monokai",
"Morling",
+5 -2
View File
@@ -5,12 +5,15 @@ on: # rebuild any PRs and main branch changes
- "dependabot/**"
pull_request:
permissions:
contents: read
jobs:
spellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: streetsidesoftware/cspell-action@3bc52c39528d0214a947ff7f8b5c23ad3273435b
- uses: actions/checkout@v3
- uses: streetsidesoftware/cspell-action@v2
with:
inline: warning
strict: false
+3 -3
View File
@@ -60,7 +60,7 @@ toc: true
[content summaries]: /content-management/summaries/
[content types]: /content-management/types/
[Disqus]: https://disqus.com/
[Dynamic menu]: /templates/menus/
[Dynamic menu]: /templates/menu-templates/
[Extremely fast]: https://github.com/bep/hugo-benchmark
[front matter]: /content-management/front-matter/
[functions]: /functions/
@@ -73,13 +73,13 @@ toc: true
[organization for your projects]: /getting-started/directory-structure/
[pagevars]: /variables/page/
[Permalink]: /content-management/urls/#permalinks
[Powerful theming]: /themes/
[Powerful theming]: /hugo-modules/theme-components/
[Pretty URLs]: /content-management/urls/
[RSS]: /templates/rss/
[Shortcodes]: /content-management/shortcodes/
[sort content]: /templates/
[supported formats]: /content-management/formats/
[Syntax highlighting]: /tools/syntax-highlighting/
[Syntax highlighting]: /content-management/syntax-highlighting/
[table of contents]: /content-management/toc/
[taxonomies]: /content-management/taxonomies/
[URLs]: /content-management/urls/
+1 -2
View File
@@ -19,7 +19,7 @@ toc: true
Hugo is a general-purpose website framework. Technically speaking, Hugo is a [static site generator][]. Unlike systems that dynamically build a page with each visitor request, Hugo builds pages when you create or update your content. Since websites are viewed far more often than they are edited, Hugo is designed to provide an optimal viewing experience for your website's end users and an ideal writing experience for website authors.
Websites built with Hugo are extremely fast and secure. Hugo sites can be hosted anywhere, including [Netlify][], [Heroku][], [GoDaddy][], [DreamHost][], [GitHub Pages][], [GitLab Pages][], [Surge][], [Aerobatic][], [Firebase][], [Google Cloud Storage][], [Amazon S3][], [Rackspace][], [Azure][], and [CloudFront][] and work well with CDNs. Hugo sites run without the need for a database or dependencies on expensive runtimes like Ruby, Python, or PHP.
Websites built with Hugo are extremely fast and secure. Hugo sites can be hosted anywhere, including [Netlify][], [Heroku][], [GoDaddy][], [DreamHost][], [GitHub Pages][], [GitLab Pages][], [Surge][], [Firebase][], [Google Cloud Storage][], [Amazon S3][], [Rackspace][], [Azure][], and [CloudFront][] and work well with CDNs. Hugo sites run without the need for a database or dependencies on expensive runtimes like Ruby, Python, or PHP.
We think of Hugo as the ideal website creation tool with nearly instant build times, able to rebuild whenever a change is made.
@@ -40,7 +40,6 @@ Hugo is for people who want to hand code their own website without worrying abou
Hugo is for people building a blog, a company site, a portfolio site, documentation, a single landing page, or a website with thousands of pages.
[@spf13]: https://twitter.com/spf13
[Aerobatic]: https://www.aerobatic.com/
[Amazon S3]: https://aws.amazon.com/s3/
[Azure]: https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website
[CloudFront]: https://aws.amazon.com/cloudfront/ "Amazon CloudFront"
+2
View File
@@ -29,6 +29,7 @@ hugo [flags]
-F, --buildFuture include content with publishdate in the future
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/
--cleanDestinationDir remove files from destination not found in static directories
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
-c, --contentDir string filesystem path to content directory
@@ -46,6 +47,7 @@ hugo [flags]
--log enable Logging
--logFile string log File path (if set, logging enabled automatically)
--minify minify any supported output format (HTML, XML etc.)
--noBuildLock don't create .hugo_build.lock file
--noChmod don't sync permission mode of files
--noTimes don't sync modification time of files
--panicOnWarning panic on first WARNING log
@@ -22,6 +22,7 @@ See each sub-command's help for details on how to use the generated script.
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -45,6 +45,7 @@ hugo completion bash
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -36,6 +36,7 @@ hugo completion fish [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -33,6 +33,7 @@ hugo completion powershell [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -43,6 +43,7 @@ hugo completion zsh [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -24,6 +24,7 @@ hugo config [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -20,6 +20,7 @@ hugo config mounts [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -24,6 +24,7 @@ See convert's subcommands toJSON, toTOML and toYAML for more information.
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -25,6 +25,7 @@ hugo convert toJSON [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -25,6 +25,7 @@ hugo convert toTOML [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -25,6 +25,7 @@ hugo convert toYAML [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -34,6 +34,7 @@ hugo deploy [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -27,6 +27,7 @@ hugo env [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -16,6 +16,7 @@ A collection of several useful generators.
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -29,6 +29,7 @@ hugo gen chromastyles [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -31,6 +31,7 @@ hugo gen doc [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -27,6 +27,7 @@ hugo gen man [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -22,6 +22,7 @@ Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_p
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -27,6 +27,7 @@ hugo import jekyll [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -22,6 +22,7 @@ List requires a subcommand, e.g. `hugo list drafts`.
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -24,6 +24,7 @@ hugo list all [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -24,6 +24,7 @@ hugo list drafts [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -24,6 +24,7 @@ hugo list expired [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -24,6 +24,7 @@ hugo list future [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+2
View File
@@ -42,6 +42,7 @@ See https://gohugo.io/hugo-modules/ for more information.
--ignoreCache ignores the cache directory
-l, --layoutDir string filesystem path to layout directory
--minify minify any supported output format (HTML, XML etc.)
--noBuildLock don't create .hugo_build.lock file
--noChmod don't sync permission mode of files
--noTimes don't sync modification time of files
--panicOnWarning panic on first WARNING log
@@ -59,6 +60,7 @@ See https://gohugo.io/hugo-modules/ for more information.
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -32,6 +32,7 @@ hugo mod clean [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -50,6 +50,7 @@ hugo mod get [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -26,6 +26,7 @@ hugo mod graph [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -31,6 +31,7 @@ hugo mod init [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -24,6 +24,7 @@ hugo mod npm [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -34,6 +34,7 @@ hugo mod npm pack [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -20,6 +20,7 @@ hugo mod tidy [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -27,6 +27,7 @@ hugo mod vendor [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -26,6 +26,7 @@ hugo mod verify [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+2
View File
@@ -43,6 +43,7 @@ hugo new [path] [flags]
-k, --kind string content type to create
-l, --layoutDir string filesystem path to layout directory
--minify minify any supported output format (HTML, XML etc.)
--noBuildLock don't create .hugo_build.lock file
--noChmod don't sync permission mode of files
--noTimes don't sync modification time of files
--panicOnWarning panic on first WARNING log
@@ -60,6 +61,7 @@ hugo new [path] [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -28,6 +28,7 @@ hugo new site [path] [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -27,6 +27,7 @@ hugo new theme [name] [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+2
View File
@@ -54,6 +54,7 @@ hugo server [flags]
--memstats string log memory usage to this file
--minify minify any supported output format (HTML, XML etc.)
--navigateToChanged navigate to changed content file on live browser reload
--noBuildLock don't create .hugo_build.lock file
--noChmod don't sync permission mode of files
--noHTTPCache prevent HTTP caching
--noTimes don't sync modification time of files
@@ -76,6 +77,7 @@ hugo server [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
+1
View File
@@ -24,6 +24,7 @@ hugo version [flags]
### Options inherited from parent commands
```
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
--config string config file (default is path/config.yaml|json|toml)
--configDir string config dir (default "config")
--debug debug output
@@ -95,18 +95,15 @@ Website needs to showcase a few of the hundred "testimonials" available as conte
To avoid setting the build options on every testimonials, one can use [`cascade`]({{< relref "/content-management/front-matter#front-matter-cascade" >}}) on the testimonial section's content file.
```yaml
#content/testimonials/_index.md
{{< code-toggle >}}
title: Testimonials
# section build options:
_build:
render: true
# children build options with cascade
cascade:
_build:
render: false
list: true # default
```
{{< /code-toggle >}}
```go-html-template
{{/* layouts/_defaults/testimonials.html */}}
@@ -64,7 +64,7 @@ These are some alternatives to Disqus:
* [Utterances](https://utteranc.es/) (Open source, GitHub comments widget built on GitHub issues)
[configuration]: /getting-started/configuration/
[disquspartial]: /templates/partials/#disqus
[disquspartial]: /templates/internal/#disqus
[disqussetup]: https://disqus.com/profile/signup/
[forum]: https://discourse.gohugo.io
[front matter]: /content-management/front-matter/
@@ -126,4 +126,3 @@ refLinksNotFoundURL
[lists]: /templates/lists/
[output formats]: /templates/output-formats/
[shortcode]: /content-management/shortcodes/
[bfext]: /content-management/formats/#blackfriday-extensions
@@ -5,7 +5,7 @@ description: Both HTML and Markdown are supported content formats.
date: 2017-01-10
publishdate: 2017-01-10
categories: [content management]
keywords: [markdown,asciidoc,mmark,pandoc,content format]
keywords: [markdown,asciidoc,pandoc,content format]
menu:
docs:
parent: "content-management"
@@ -29,8 +29,6 @@ The current list of content formats in Hugo:
| Name | Markup identifiers | Comment |
| ------------- | ------------- |-------------|
| Goldmark | md, markdown, goldmark |Note that you can set the default handler of `md` and `markdown` to something else, see [Configure Markup](/getting-started/configuration-markup/).{{< new-in "0.60.0" >}} |
| Blackfriday | blackfriday |Blackfriday will eventually be deprecated.|
|MMark|mmark|Mmark is deprecated and will be removed in a future release.|
|Emacs Org-Mode|org|See [go-org](https://github.com/niklasfasching/go-org).|
|AsciiDoc|asciidocext, adoc, ad|Needs [Asciidoctor][ascii] installed.|
|RST|rst|Needs [RST](https://docutils.sourceforge.io/rst.html) installed.|
@@ -129,9 +127,6 @@ Markdown syntax is simple enough to learn in a single sitting. The following are
[`emojify` function]: /functions/emojify/
[ascii]: https://asciidoctor.org/
[bfconfig]: /getting-started/configuration/#configuring-blackfriday-rendering
[blackfriday]: https://github.com/russross/blackfriday
[mmark]: https://github.com/miekg/mmark
[config]: /getting-started/configuration/
[developer tools]: /tools/
[emojis]: https://www.webpagefx.com/tools/emoji-cheat-sheet/
@@ -146,7 +141,6 @@ Markdown syntax is simple enough to learn in a single sitting. The following are
[mdcheatsheet]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
[mdguide]: https://www.markdownguide.org/
[mdtutorial]: https://www.markdowntutorial.com/
[Miek Gieben's website]: https://miek.nl/2016/march/05/mmark-syntax-document/
[org]: https://orgmode.org/
[pandoc]: https://www.pandoc.org/
[rest]: https://docutils.sourceforge.io/rst.html
@@ -92,7 +92,7 @@ keywords
: the meta keywords for the content.
layout
: the layout Hugo should select from the [lookup order][lookup] when rendering the content. If a `type` is not specified in the front matter, Hugo will look for the layout of the same name in the layout directory that corresponds with a content's section. See ["Defining a Content Type"][definetype]
: the layout Hugo should select from the [lookup order][lookup] when rendering the content. If a `type` is not specified in the front matter, Hugo will look for the layout of the same name in the layout directory that corresponds with a content's section. See [Content Types][content type].
lastmod
: the datetime at which the content was last modified.
@@ -217,7 +217,7 @@ You can assign content-specific `weight` in the front matter of your content. Th
## Override Global Markdown Configuration
It's possible to set some options for Markdown rendering in a content's front matter as an override to the [BlackFriday rendering options set in your project configuration][config].
It's possible to set some options for Markdown rendering in a content's front matter as an override to the [Rendering options set in your project configuration][config].
## Front Matter Format Specs
@@ -232,10 +232,9 @@ It's possible to set some options for Markdown rendering in a content's front ma
[config]: /getting-started/configuration/ "Hugo documentation for site configuration"
[content type]: /content-management/types/
[contentorg]: /content-management/organization/
[definetype]: /content-management/types/#defining-a-content-type "Learn how to specify a type and a layout in a content's front matter"
[headless-bundle]: /content-management/page-bundles/#headless-bundle
[json]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf "Specification for JSON, JavaScript Object Notation"
[lists]: /templates/lists/#ordering-content "See how to order content in list pages; for example, templates that look to specific _index.md for content and front matter."
[lists]: /templates/lists/#order-content "See how to order content in list pages; for example, templates that look to specific _index.md for content and front matter."
[lookup]: /templates/lookup-order/ "Hugo traverses your templates in a specific order when rendering content to allow for DRYer templating."
[ordering]: /templates/lists/ "Hugo provides multiple ways to sort and order your content in list templates"
[outputs]: /templates/output-formats/ "With the release of v22, you can output your content to any text format using Hugo's familiar templating"

Some files were not shown because too many files have changed in this diff Show More