Compare commits

..

87 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
189 changed files with 3446 additions and 3324 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.
+4 -2
View File
@@ -50,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
@@ -72,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
+1 -7
View File
@@ -83,7 +83,7 @@ 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:
*"js: Return error when option x is not set"*, **NOT** *"returning some error."*
@@ -99,12 +99,6 @@ Most title/subjects should have a lower-cased prefix with a colon and one whites
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"
```
+4 -3
View File
@@ -369,12 +369,13 @@ func (c *commandeer) loadConfig() error {
c.configFiles = configFiles
var ok bool
loc := time.Local
c.languages, ok = c.Cfg.Get("languagesSorted").(langs.Languages)
if !ok {
panic("languages not configured")
if ok {
loc = langs.GetLocation(c.languages[0])
}
err = c.initClock(langs.GetLocation(c.languages[0]))
err = c.initClock(loc)
if err != nil {
return err
}
+1
View File
@@ -305,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.")
+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"
)
+13 -5
View File
@@ -127,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
}
@@ -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)
}()
@@ -860,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)
}
}
+1 -7
View File
@@ -36,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"
@@ -202,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")
+8 -4
View File
@@ -168,6 +168,14 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
c.Set("watch", true)
}
// 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
}
// We can only do this once.
serverCfgInit.Do(func() {
c.serverPorts = make([]serverPortListener, 1)
@@ -264,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 {
+48 -21
View File
@@ -41,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:")
@@ -52,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)
}
@@ -81,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)
@@ -95,30 +95,52 @@ func TestServerBugs(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
name string
flag string
assert func(c *qt.C, r serverTestResult)
name string
config string
flag string
numservers int
assert func(c *qt.C, r serverTestResult)
}{
// Issue 9788
{"PostProcess, memory", "", func(c *qt.C, r serverTestResult) {
{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homeContent, qt.Contains, "PostProcess: /foo.min.css")
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
}},
{"PostProcess, disk", "--renderToDisk", func(c *qt.C, r serverTestResult) {
{"PostProcess, disk", "", "--renderToDisk", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homeContent, qt.Contains, "PostProcess: /foo.min.css")
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) {
config := `
if test.config == "" {
test.config = `
baseURL="https://example.org"
`
}
var args []string
if test.flag != "" {
args = strings.Split(test.flag, "=")
}
r := runServerTest(c, true, config, args...)
r := runServerTest(c, test.numservers, test.config, args...)
test.assert(c, r)
})
@@ -129,11 +151,11 @@ baseURL="https://example.org"
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()
@@ -162,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)
}
}()
}
}
+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 {
+7 -5
View File
@@ -20,8 +20,6 @@ import (
"github.com/bep/clock"
"github.com/spf13/cast"
toml "github.com/pelletier/go-toml/v2"
"github.com/gohugoio/locales"
)
@@ -139,13 +137,12 @@ 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)
}
@@ -161,3 +158,8 @@ func Now() time.Time {
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
}
+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 {
+3 -2
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.99,
Major: 0,
Minor: 102,
PatchLevel: 0,
Suffix: "",
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) {
+1 -1
View File
@@ -290,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}
+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) {
})
}
}
+2 -2
View File
@@ -127,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 {
@@ -136,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)
+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
+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"
+1
View File
@@ -47,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
+1
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
+1
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
+1
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
@@ -126,4 +126,3 @@ refLinksNotFoundURL
[lists]: /templates/lists/
[output formats]: /templates/output-formats/
[shortcode]: /content-management/shortcodes/
[bfext]: /content-management/formats/#blackfriday-extensions
@@ -29,7 +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.|
|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.|
@@ -128,8 +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
[config]: /getting-started/configuration/
[developer tools]: /tools/
[emojis]: https://www.webpagefx.com/tools/emoji-cheat-sheet/
@@ -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
@@ -140,31 +140,6 @@ Press Ctrl+C to stop
Live reload and `--navigateToChanged` between the servers work as expected.
### Taxonomies and Blackfriday
Taxonomies and [Blackfriday configuration][config] can also be set per language:
{{< code-toggle file="config" >}}
[Taxonomies]
tag = "tags"
[blackfriday]
angledQuotes = true
hrefTargetBlank = true
[languages]
[languages.en]
weight = 1
title = "English"
[languages.en.blackfriday]
angledQuotes = false
[languages.fr]
weight = 2
title = "Français"
[languages.fr.Taxonomies]
plaque = "plaques"
{{</ code-toggle >}}
## Translate Your Content
@@ -544,10 +519,10 @@ Hugo will generate your website with these missing translation placeholders. It
For merging of content from other languages (i.e. missing content translations), see [lang.Merge].
To track down missing translation strings, run Hugo with the `--i18n-warnings` flag:
To track down missing translation strings, run Hugo with the `--printI18nWarnings` flag:
```bash
hugo --i18n-warnings | grep i18n
hugo --printI18nWarnings | grep i18n
i18n|MISSING_TRANSLATION|en|wordCount
```
@@ -45,6 +45,7 @@ Options:
* `linenostart=199`: starts the line number count from 199.
* `anchorlinenos`: Configure anchors on line numbers. Valid values are `true` or `false`;
* `lineanchors`: Configure a prefix for the anchors on line numbers. Will be suffixed with `-`, so linking to the line number 1 with the option `lineanchors=prefix` adds the anchor `prefix-1` to the page.
* `hl_inline` Highlight inside a `<code>` (inline HTML element) tag. Valid values are `true` or `false`. The `code` tag will get a class with name `code-inline`. {{< new-in "0.101.0" >}}
### Example: Highlight Shortcode
@@ -91,6 +91,9 @@ The following is a list of values that can be used in a `permalink` definition i
`:slug`
: the content's slug (or title if no slug is provided in the front matter)
`:slugorfilename`
: the content's slug (or filename if no slug is provided in the front matter)
`:filename`
: the content's filename (without extension)
-1
View File
@@ -19,4 +19,3 @@ layout: documentation-home
Hugo is the **world's fastest static website engine.** It's written in Go (aka Golang) and developed by [bep](https://github.com/bep), [spf13](https://github.com/spf13) and [friends](https://github.com/gohugoio/hugo/graphs/contributors).
Below you will find some of the most common and helpful pages from our documentation.
foo
+1 -1
View File
@@ -13,7 +13,7 @@ workson: []
relatedfuncs: [humanize]
---
If [Goldmark](https://gohugo.io/getting-started/configuration-markup#goldmark) is set as `defaultMarkdownHandler`, the sanitizing logic adheres to the setting [`markup.goldmark.parser.autoHeadingIDType`](https://gohugo.io/getting-started/configuration-markup#goldmark). If [Blackfriday](https://gohugo.io/getting-started/configuration-markup#blackfriday) is set as `defaultMarkdownHandler`, this template function uses the [`SanitizedAnchorName` logic from Blackfriday](https://github.com/russross/blackfriday#sanitized-anchor-names) (the same applies when `markup.goldmark.parser.autoHeadingIDType` is set to `blackfriday`).
If [Goldmark](https://gohugo.io/getting-started/configuration-markup#goldmark) is set as `defaultMarkdownHandler`, the sanitizing logic adheres to the setting [`markup.goldmark.parser.autoHeadingIDType`](https://gohugo.io/getting-started/configuration-markup#goldmark).
Since the `defaultMarkdownHandler` and this template function use the same sanitizing logic, you can use the latter to determine the ID of a header for linking with anchor tags.
+4
View File
@@ -54,6 +54,10 @@ hl_Lines
: String. Default is `""`.\
A space-separated list of lines to emphasize within the highlighted code. To emphasize lines 2, 3, 4, and 7, set this value to `2-4 7`. This option is independent of the `lineNoStart` option.
hl_inline
: Boolean. Default is `false`.\
Render the highlighted code without a wrapping container.
style
: String. Default is `monokai`.\
The CSS styles to apply to the highlighted code. See the [style gallery] for examples. Case-sensitive.
+3 -4
View File
@@ -27,8 +27,8 @@ hugo.Generator
hugo.Version
: the current version of the Hugo binary you are using e.g. `0.63.2`
`hugo` returns an instance that contains the following functions:
hugo.GoVersion
: returns the version of Go that the Hugo binary was built with. {{< new-in "0.101.0" >}}
hugo.Environment
: the current running environment as defined through the `--environment` cli tag
@@ -52,7 +52,6 @@ We highly recommend using `hugo.Generator` in your website's `<head>`. `hugo.Gen
hugo.Deps
: See [hugo.Deps](#hugodeps)
## hugo.Deps
{{< new-in "0.92.0" >}}
@@ -112,4 +111,4 @@ An example table listing the dependencies:
{{ end }}
</tbody>
</table>
```
```
+1 -1
View File
@@ -12,7 +12,7 @@ keywords: [path, base]
signature: ["path.Base PATH"]
workson: []
hugoversion: "0.40"
relatedfuncs: [path.Dir, path.Ext, path.Split]
relatedfuncs: [path.BaseName, path.Clean, path.Dir, path.Ext, path.Join, path.Split]
deprecated: false
---
@@ -0,0 +1,24 @@
---
title: path.BaseName
description: BaseName returns the last element of a path, removing the extension if present.
date: 2022-06-04
categories: [functions]
menu:
docs:
parent: "functions"
keywords: [path, base]
signature: ["path.BaseName PATH"]
relatedfuncs: [path.Base, path.Clean, path.Dir, path.Ext, path.Join, path.Split]
deprecated: false
---
If `PATH` is empty, `.` is returned.
**Note:** On Windows, `PATH` is converted to slash (`/`) separators.
```go-html-template
{{ path.BaseName "a/news.html" }} → "news"
{{ path.BaseName "news.html" }} → "news"
{{ path.BaseName "a/b/c" }} → "c"
{{ path.BaseName "/x/y/z/" }} → "z"
```
+2 -1
View File
@@ -8,8 +8,9 @@ categories: [functions]
menu:
docs:
parent: "functions"
keywords: [path]
keywords: [path, clean]
signature: ["path.Clean PATH"]
relatedfuncs: [path.Base, path.BaseName, path.Dir, path.Ext, path.Join, path.Split]
---
`path.Clean` replaces path separators with slashes (`/`) and removes extraneous separators, including trailing separators.
+1 -1
View File
@@ -12,7 +12,7 @@ keywords: [path, dir]
signature: ["path.Dir PATH"]
workson: []
hugoversion: "0.40"
relatedfuncs: [path.Base, path.Ext, path.Split]
relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Ext, path.Join, path.Split]
deprecated: false
---
+1 -1
View File
@@ -12,7 +12,7 @@ keywords: [path, ext, extension]
signature: ["path.Ext PATH"]
workson: []
hugoversion: "0.40"
relatedfuncs: [path.Base, path.Dir, path.Split]
relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Dir, path.Join, path.Split]
deprecated: false
---
+1 -1
View File
@@ -12,7 +12,7 @@ keywords: [path, join]
signature: ["path.Join ELEMENT..."]
workson: []
hugoversion: "0.39"
relatedfuncs: [path.Split]
relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Dir, path.Ext, path.Split]
deprecated: false
---
+1 -1
View File
@@ -12,7 +12,7 @@ keywords: [path, split]
signature: ["path.Split PATH"]
workson: []
hugoversion: "0.39"
relatedfuncs: [path.Split]
relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Dir, path.Ext, path.Join]
deprecated: false
---
@@ -24,7 +24,7 @@ Below are all markup related configuration in Hugo with their default settings:
### Goldmark
[Goldmark](https://github.com/yuin/goldmark/) is from Hugo 0.60 the default library used for Markdown. It's fast, it's [CommonMark](https://spec.commonmark.org/0.29/) compliant and it's very flexible. Note that the feature set of Goldmark vs Blackfriday isn't the same; you gain a lot but also lose some, but we will work to bridge any gap in the upcoming Hugo versions.
[Goldmark](https://github.com/yuin/goldmark/) is from Hugo 0.60 the default library used for Markdown. It's fast, it's [CommonMark](https://spec.commonmark.org/0.29/) compliant and it's very flexible.
This is the default configuration:
@@ -77,16 +77,8 @@ Note that attributes in [code fences](/content-management/syntax-highlighting/#h
````
autoHeadingIDType ("github") {{< new-in "0.62.2" >}}
: The strategy used for creating auto IDs (anchor names). Available types are `github`, `github-ascii` and `blackfriday`. `github` produces GitHub-compatible IDs, `github-ascii` will drop any non-Ascii characters after accent normalization, and `blackfriday` will make the IDs work as with [Blackfriday](#blackfriday), the default Markdown engine before Hugo 0.60. Note that if Goldmark is your default Markdown engine, this is also the strategy used in the [anchorize](/functions/anchorize/) template func.
: The strategy used for creating auto IDs (anchor names). Available types are `github`, `github-ascii` and `blackfriday`. `github` produces GitHub-compatible IDs, `github-ascii` will drop any non-Ascii characters after accent normalization, and `blackfriday` will make the IDs compatible with [Blackfriday](#blackfriday), the default Markdown engine before Hugo 0.60. Note that if Goldmark is your default Markdown engine, this is also the strategy used in the [anchorize](/functions/anchorize/) template func.
### Blackfriday
[Blackfriday](https://github.com/russross/blackfriday) was Hugo's default Markdown rendering engine, now replaced with Goldmark. But you can still use it: Just set `defaultMarkdownHandler` to `blackfriday` in your top level `markup` config.
This is the default config:
{{< code-toggle config="markup.blackFriday" />}}
### Highlight
@@ -121,9 +121,6 @@ The directory where Hugo finds asset files used in [Hugo Pipes](/hugo-pipes/). {
### baseURL
Hostname (and path) to the root, e.g. https://bep.is/
### blackfriday
See [Configure Blackfriday](/getting-started/configuration-markup#blackfriday)
### build
See [Configure Build](#configure-build)
+45 -38
View File
@@ -43,56 +43,63 @@ Usage:
hugo [command]
Available Commands:
check Contains some verification checks
completion Generate the autocompletion script for the specified shell
config Print the site configuration
convert Convert your content to different formats
deploy Deploy your site to a Cloud provider.
env Print Hugo version and environment info
gen A collection of several useful generators.
help Help about any command
import Import your site from others.
list Listing out various types of content
mod Various Hugo Modules helpers.
new Create new content for your site
server A high performance webserver
version Print the version number of Hugo
Flags:
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
-D, --buildDrafts include content marked as draft
-E, --buildExpired include expired content
-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
--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
--debug debug output
-d, --destination string filesystem path to write files to
--disableKinds strings disable different kind of pages (home, RSS etc.)
--enableGitInfo add Git revision, date and author info to the pages
-e, --environment string build environment
--forceSyncStatic copy all files when static is changed.
--gc enable to run some cleanup tasks (remove unused cache files) after the build
-h, --help help for hugo
--i18n-warnings print missing translations
--ignoreCache ignores the cache directory
-l, --layoutDir string filesystem path to layout directory
--log enable Logging
--logFile string log File path (if set, logging enabled automatically)
--minify minify any supported output format (HTML, XML etc.)
--noChmod don't sync permission mode of files
--noTimes don't sync modification time of files
--path-warnings print warnings on duplicate target paths etc.
--quiet build in quiet mode
--renderToMemory render to memory (only useful for benchmark testing)
-s, --source string filesystem path to read files relative from
--templateMetrics display metrics about template executions
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--themesDir string filesystem path to themes directory
--trace file write trace to file (not useful in general)
-v, --verbose verbose output
--verboseLog verbose logging
-w, --watch watch filesystem for changes and recreate as needed
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
-D, --buildDrafts include content marked as draft
-E, --buildExpired include expired content
-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
--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
--debug debug output
-d, --destination string filesystem path to write files to
--disableKinds strings disable different kind of pages (home, RSS etc.)
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
-e, --environment string build environment
--forceSyncStatic copy all files when static is changed.
--gc enable to run some cleanup tasks (remove unused cache files) after the build
-h, --help help for hugo
--ignoreCache ignores the cache directory
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
-l, --layoutDir string filesystem path to layout directory
--log enable Logging
--logFile string log File path (if set, logging enabled automatically)
--minify minify any supported output format (HTML, XML etc.)
--noChmod don't sync permission mode of files
--noTimes don't sync modification time of files
--panicOnWarning panic on first WARNING log
--poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes
--printI18nWarnings print missing translations
--printMemoryUsage print memory usage to screen at intervals
--printPathWarnings print warnings on duplicate target paths etc.
--printUnusedTemplates print warnings on unused templates.
--quiet build in quiet mode
--renderToMemory render to memory (only useful for benchmark testing)
-s, --source string filesystem path to read files relative from
--templateMetrics display metrics about template executions
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
-t, --theme strings themes to use (located in /themes/THEMENAME/)
--themesDir string filesystem path to themes directory
--trace file write trace to file (not useful in general)
-v, --verbose verbose output
--verboseLog verbose logging
-w, --watch watch filesystem for changes and recreate as needed
Use "hugo [command] --help" for more information about a command.
```
@@ -53,6 +53,17 @@ With `resources.GetRemote`, the first argument is a remote URL:
`resources.Get` and `resources.GetRemote` return `nil` if the resource is not found.
## Copy a Resource
{{< new-in "0.100.0" >}}
`resources.Copy` allows you to copy almost any Hugo `Resource` (the one exception is the `Page`), possibly most useful for renaming things:
```go-html-template
{{ $resized := $image.Resize "400x400" | resources.Copy "images/mynewname.jpg" }}
<img src="{{ $resized.RelPermalink }}">
```
### Caching
By default, Hugo calculates a cache key based on the `URL` and the `options` (e.g. headers) given.
+1 -1
View File
@@ -45,7 +45,7 @@ includePaths [string slice]
: Additional SCSS/SASS include paths. Paths must be relative to the project directory.
```go-html-template
{{ $options := (dict "targetPath" "style.css" "outputStyle" "compressed" "enableSourceMap" true "includePaths" (slice "node_modules/myscss")) }}
{{ $options := (dict "targetPath" "style.css" "outputStyle" "compressed" "enableSourceMap" (not hugo.IsProduction) "includePaths" (slice "node_modules/myscss")) }}
{{ $style := resources.Get "sass/main.scss" | resources.ToCSS $options }}
```
+1 -1
View File
@@ -10,7 +10,7 @@ siteURL: https://www.tomango.co.uk
siteSource: https://github.com/trys/tomango-2018
byline: "[Trys Mudford](http://www.trysmudford.com), Lead Developer, Tomango"
byline: "[Trys Mudford](https://www.trysmudford.com), Lead Developer, Tomango"
---
+1 -1
View File
@@ -17,7 +17,7 @@ aliases: []
toc: false
---
When using Hugo with [GitHub Pages](https://pages.github.com/), you can provide your own template for a [custom 404 error page](https://help.github.com/articles/custom-404-pages/) by creating a 404.html template file in your `/layouts` folder. When Hugo generates your site, the `404.html` file will be placed in the root.
When using Hugo with [GitHub Pages](https://pages.github.com/), you can provide your own template for a [custom 404 error page](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-custom-404-page-for-your-github-pages-site) by creating a 404.html template file in your `/layouts` folder. When Hugo generates your site, the `404.html` file will be placed in the root.
404 pages will have all the regular [page variables][pagevars] available to use in the templates.
+1 -1
View File
@@ -112,7 +112,7 @@ You can use the following code to render the `Short Description` in your layout:
<div>Short Description of {{.Site.Data.User0123.Name}}: <p>{{ index .Site.Data.User0123 "Short Description" | markdownify }}</p></div>
```
Note the use of the [`markdownify` template function][markdownify]. This will send the description through the Blackfriday Markdown rendering engine.
Note the use of the [`markdownify` template function][markdownify]. This will send the description through the Markdown rendering engine.
## Get Remote Data
+1 -1
View File
@@ -43,7 +43,7 @@ To use the `readFile` function in your templates, make sure the path is relative
### `readFile` Example: Add a Project File to Content
As `readFile` is a function, it is only available to you in your templates and not your content. However, we can create a simple [shortcode template][sct] that calls `readFile`, passes the first argument through the function, and then allows an optional second argument to send the file through the Blackfriday markdown processor. The pattern for adding this shortcode to your content will be as follows:
As `readFile` is a function, it is only available to you in your templates and not your content. However, we can create a simple [shortcode template][sct] that calls `readFile`, passes the first argument through the function, and then allows an optional second argument to send the file through the markdown processor. The pattern for adding this shortcode to your content will be as follows:
```
{{</* readfile file="/path/to/local/file.txt" markdown="true" */>}}
+1 -1
View File
@@ -107,7 +107,7 @@ The custom variables need to be prefixed with `$`.
{{ $address := "123 Main St." }}
{{ $address }}
```
Vriables can be re-defined using the `=` operator. The example below
Variables can be re-defined using the `=` operator. The example below
prints "Var is Hugo Home" on the home page, and "Var is Hugo Page" on
all other pages:
+2 -2
View File
@@ -45,9 +45,9 @@ Also see this Twitter thread:
Yes you can! Read [this](/hosting-and-deployment/hosting-on-netlify/#configure-hugo-version-in-netlify).
## I get "TOCSS ... this feature is not available in your current Hugo version"
## I get "... this feature is not available in your current Hugo version"
If you process `SCSS` or `SASS` to `CSS` in your Hugo project, you need the Hugo `extended` version, or else you may see this error message:
If you process `SCSS` or `SASS` to `CSS` in your Hugo project with `libsass` as the transpiler or if you convert images to the `webp` format, you need the Hugo `extended` version, or else you may see an error message similar to the below:
```bash
error: failed to transform resource: TOCSS: failed to transform "scss/main.scss" (text/x-scss): this feature is not available in your current Hugo version
+4
View File
@@ -45,4 +45,8 @@ toc: false
[markdownshortcode]: /content-management/shortcodes/#shortcodes-with-markdown
[shortcodes]: /templates/shortcode-templates/
.InnerDeindent {{< new-in "0.100.0" >}}
: Gets the `.Inner` with any indentation removed. This is what's used in the built-in `{{</* highlight */>}}` shortcode.
+26 -26
View File
@@ -1,49 +1,49 @@
---
title: "世界上最快的网站构建框架 The worlds fastest framework for building websites"
title: "世界上最快的网站构建框架"
date: 2017-03-02T12:00:00-05:00
features:
- heading: Blistering Speed
- heading: 飞快的速度
image_path: /images/icon-fast.svg
tagline: What's modern about waiting for your site to build?
copy: Hugo is the fastest tool of its kind. At <1 ms per page, the average site builds in less than a second.
tagline: 摩登发布
copy: Hugo 是同类中最快的工具。生成一页小于 1ms,生成一个站点平均不超过 1s。
- heading: Robust Content Management
- heading: 强大的内容管理
image_path: /images/icon-content-management.svg
tagline: Flexibility rules. Hugo is a content strategist's dream.
copy: Hugo supports unlimited content types, taxonomies, menus, dynamic API-driven content, and more, all without plugins.
tagline: 灵活的规则。Hugo 是一个设计师的梦想。
copy: Hugo 支持无限的内容类型、分类、菜单、动态 API 驱动的内容,以及更多,都不需要插件。
- heading: Shortcodes
- heading: 短代码
image_path: /images/icon-shortcodes.svg
tagline: Hugo's shortcodes are Markdown's hidden superpower.
copy: We love the beautiful simplicity of markdowns syntax, but there are times when we want more flexibility. Hugo shortcodes allow for both beauty and flexibility.
tagline: Hugo 短代码是 Markdown 隐藏的强大功能。
copy: 我们喜欢美丽简洁的 markdown 语法,但是有时候我们也想要更灵活一些。Hugo 短代码允许既美丽又灵活。
- heading: Built-in Templates
- heading: 内置模板
image_path: /images/icon-built-in-templates.svg
tagline: Hugo has common patterns to get your work done quickly.
copy: Hugo ships with pre-made templates to make quick work of SEO, commenting, analytics and other functions. One line of code, and you're done.
tagline: Hugo 有通用的模式让你快速的完成工作。
copy: Hugo 平台预置的模板会快速实现搜索引擎优化、评论、分析和其他的功能。一行代码,你就完成了。
- heading: Multilingual and i18n
- heading: 多语言和国际化
image_path: /images/icon-multilingual2.svg
tagline: Polyglot baked in.
copy: Hugo provides full i18n support for multi-language sites with the same straightforward development experience Hugo users love in single-language sites.
tagline: 多语言制作
copy: Hugo 为多语言站点提供了完整的国际化支持,并且具有与 Hugo 用户在单语言站点中喜爱的相同的简单开发体验。
- heading: Custom Outputs
- heading: 自定义输出
image_path: /images/icon-custom-outputs.svg
tagline: HTML not enough?
copy: Hugo allows you to output your content in multiple formats, including JSON or AMP, and makes it easy to create your own.
tagline: HTML 不够用吗?
copy: Hugo 允许您以多种格式输出内容,包括 JSON AMP,并且可以很容易地创建自己的内容。
sections:
- heading: "100s of Themes"
cta: Check out the Hugo's themes.
- heading: "100 多个主题"
cta: 看看 Hugo 的主题
link: https://themes.gohugo.io/
color_classes: bg-accent-color white
image: /images/homepage-screenshot-hugo-themes.jpg
copy: "Hugo provides a robust theming system that is easy to implement but capable of producing even the most complicated websites."
- heading: "Capable Templating"
cta: Get Started.
copy: "Hugo 提供了一个强大的主题系统,易于实现,但能够生成即使是最复杂的网站。"
- heading: "功能模板"
cta: 开始吧。
link: templates/
color_classes: bg-primary-color-light black
image: /images/home-page-templating-example.png
copy: "Hugo's Go-based templating provides just the right amount of logic to build anything from the simple to complex. If you prefer Jade/Pug-like syntax, you can also use Amber, Ace, or any combination of the three."
copy: "Hugo 基于 Go 的模板提供了恰当的方法来生成从简单到复杂的任何东西。如果你喜欢 Jade/Pug 类似的语法,你也可以使用 AmberAce 或三种任意组合。"
---
Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again.
Hugo 是最受欢迎的开源静态站点生成器之一。凭借惊人的速度和灵活性,Hugo 使建设网站的乐趣再现。
+2 -2
View File
@@ -1,7 +1,7 @@
---
title: Hugo 说明文档
linktitle: Hugo
description: Hugo is the world's fastest static website engine. It's written in Go (aka Golang) and developed by bep, spf13 and friends.
description: Hugo 是世界上最快的静态网站引擎。它是用 Go ( Golang) 编程语言所写成,由 bepspf13 和朋友们共同开发。
date: 2017-02-01
publishdate: 2017-02-01
menu:
@@ -16,6 +16,6 @@ toc: false
layout: documentation-home
isCJKLanguage: true
---
Hugo 号称**世界上最快的静态网站引擎**。它是 Go (即 Golang) 编程语言所写成,由 [bep](https://github.com/bep)、[spf13](https://github.com/spf13) 和[朋友们](https://github.com/gohugoio/hugo/graphs/contributors) 共同开发。
Hugo 世界上最快的静态网站引擎。它是 Go (即 Golang) 编程语言所写成,由 [bep](https://github.com/bep)、[spf13](https://github.com/spf13) 和[朋友们](https://github.com/gohugoio/hugo/graphs/contributors)共同开发。
下面是我们说明文档中最常用和实用的章节:
+25
View File
@@ -0,0 +1,25 @@
---
title:  开发者工具
linktitle:  开发者工具概览
description: 除了 Hugo 强大的命令外,对于 Hugo 开发者还有大量社区开发的工具链。
date: 2018-05-29
publishdate: 2018-05-29
lastmod: 2018-05-29
categories: [developer tools]
keywords: []
menu:
docs:
parent: "tools"
weight: 01
weight: 01
sections_weight: 01
draft: false
aliases: [/tools/]
---
Hugo 最强大的在于它是活跃的——并总在发展的——开发者社区。除了在[语法高亮][syntax] 中提到的 `highlight` 简码外,该章节中的这些工具和其它项目特色都是由商业服务和开源项目提供,很多就是由像你一样的 Hugo 开发者开发的。
[查看 Hugo 的流行程度与其它静态网站生成器的比较。][staticgen]
[staticgen]: https://staticgen.com
[syntax]: /tools/syntax-highlighting/
+31
View File
@@ -0,0 +1,31 @@
---
title: 搜索你的 Hugo 网站
linktitle: 搜索
description: 为你最新添加的 Hugo 网站看一些开源和商业搜索选择。
date: 2018-05-29
publishdate: 2018-05-29
lastmod: 2018-05-29
categories: [developer tools]
keywords: [search,tools]
menu:
docs:
parent: "tools"
weight: 60
weight: 60
sections_weight: 60
draft: false
aliases: []
toc: true
---
静态网站还有动态搜索功能?是的。作为可选方案,来自 Google 或者其它搜索引擎的嵌入式脚本,可以给你的访客提供一个自定义的直接基于你的内容文件索引的搜素。
* [GitHub Gist for Hugo Workflow](https://gist.github.com/sebz/efddfc8fdcb6b480f567). Gist 包含一个为你的网站创建索引的简单流程。它使用简单的 Grunt 脚本索引你所有的内容文件,并且 [lunr.js](http://lunrjs.com/) 会提供搜索结果。
* [hugo-lunr](https://www.npmjs.com/package/hugo-lunr). 一个使用 [lunr.js](http://lunrjs.com/) 为你的 Hugo 静态网站添加搜索的简单方法。Hugo-lunr 将会给你的 Hugo 项目中任意 html 和 markdown 文件创建一个索引文件。
* [hugo-lunr-zh](https://www.npmjs.com/package/hugo-lunr-zh). 有点像 Hugo-lunr,但是 Hugo-lunr-zh 能帮助你分割中文关键字。
* [Github Gist for Fuse.js integration](https://gist.github.com/eddiewebb/735feb48f50f0ddd65ae5606a1cb41ae). 该 gist 显示如何借助 Hugo 已有的构建时间在客户端通过 [Fuse.js](http://fusejs.io/) 生成可搜索的 JSON 索引。尽管 gist 使用 Fuse.js 进行模糊匹配,任何有能力读取 JSON 索引的客户端工具都可以运行。不需要 Hugo 以外, npm、grunt 或者其它构建时工具。
* [hugo-search-index](https://www.npmjs.com/package/hugo-search-index). 一个包含实现了搜索的 Gulp 任务和内置浏览器脚本的库。Gulp 从项目中的 markdown 文件生成索引。
## 商业搜索服务
* [Algolia](https://www.algolia.com/) 的搜索 API 使得在你的应用和网站中提供一个很好的搜索体验变得简单。Algolia 搜索提供托管的全文本、数字化、分面以及地理定位搜索。
+329 -509
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,6 +1,6 @@
<dl>
{{ range .Site.Data.docs.chroma.lexers }}
<dt>{{ .Name }}</dt>
<dd>{{ delimit .Aliases ", " }}</dd>
{{ end }}
</dl>
{{ range .Site.Data.docs.chroma.lexers }}
<dt>{{ .Name }}</dt>
<dd>{{ with .Aliases }}{{ delimit . ", " }}{{ end }}</dd>
{{ end }}
</dl>
+4 -4
View File
@@ -3,7 +3,7 @@ publish = "public"
command = "hugo --gc --minify"
[context.production.environment]
HUGO_VERSION = "0.98.0"
HUGO_VERSION = "0.101.0"
HUGO_ENV = "production"
HUGO_ENABLEGITINFO = "true"
@@ -11,20 +11,20 @@ HUGO_ENABLEGITINFO = "true"
command = "hugo --gc --minify --enableGitInfo"
[context.split1.environment]
HUGO_VERSION = "0.98.0"
HUGO_VERSION = "0.101.0"
HUGO_ENV = "production"
[context.deploy-preview]
command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"
[context.deploy-preview.environment]
HUGO_VERSION = "0.98.0"
HUGO_VERSION = "0.101.0"
[context.branch-deploy]
command = "hugo --gc --minify -b $DEPLOY_PRIME_URL"
[context.branch-deploy.environment]
HUGO_VERSION = "0.98.0"
HUGO_VERSION = "0.101.0"
[context.next.environment]
HUGO_ENABLEGITINFO = "true"
+14 -13
View File
@@ -3,7 +3,7 @@ module github.com/gohugoio/hugo
require (
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
github.com/PuerkitoBio/purell v1.1.1
github.com/alecthomas/chroma v0.10.0
github.com/alecthomas/chroma/v2 v2.2.0
github.com/armon/go-radix v1.0.0
github.com/aws/aws-sdk-go v1.43.5
github.com/bep/clock v0.3.0
@@ -15,15 +15,15 @@ require (
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/clbanning/mxj/v2 v2.5.6
github.com/cli/safeexec v1.0.0
github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.0
github.com/evanw/esbuild v0.14.39
github.com/evanw/esbuild v0.14.43
github.com/fortytw2/leaktest v1.3.0
github.com/frankban/quicktest v1.14.3
github.com/fsnotify/fsnotify v1.5.4
github.com/getkin/kin-openapi v0.94.0
github.com/getkin/kin-openapi v0.97.0
github.com/ghodss/yaml v1.0.0
github.com/gobuffalo/flect v0.2.5
github.com/gobwas/glob v0.2.3
@@ -42,29 +42,28 @@ require (
github.com/mitchellh/hashstructure v1.1.0
github.com/mitchellh/mapstructure v1.5.0
github.com/muesli/smartcrop v0.3.0
github.com/niklasfasching/go-org v1.6.2
github.com/niklasfasching/go-org v1.6.5
github.com/olekukonko/tablewriter v0.0.5
github.com/pelletier/go-toml/v2 v2.0.0-beta.7.0.20220408132554-2377ac4bc04c
github.com/pelletier/go-toml/v2 v2.0.2
github.com/rogpeppe/go-internal v1.8.1
github.com/russross/blackfriday v1.6.0
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/sanity-io/litter v1.5.4
github.com/sanity-io/litter v1.5.5
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.11.2
github.com/tdewolff/parse/v2 v2.5.29
github.com/tdewolff/minify/v2 v2.11.10
github.com/tdewolff/parse/v2 v2.6.0
github.com/yuin/goldmark v1.4.12
go.uber.org/atomic v1.9.0
gocloud.dev v0.24.0
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.7
golang.org/x/tools v0.1.10
golang.org/x/tools v0.1.11
google.golang.org/api v0.76.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -104,6 +103,7 @@ require (
github.com/googleapis/gax-go/v2 v2.3.0 // indirect
github.com/googleapis/go-type-adapters v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/invopop/yaml v0.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
@@ -115,12 +115,13 @@ require (
go.opencensus.io v0.23.0 // indirect
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 // indirect
google.golang.org/grpc v1.46.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
go 1.18
+34 -32
View File
@@ -131,8 +131,11 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/chroma/v2 v2.1.0 h1:ZG9L5/RsxO/xIONrBy8Cgo+5si3d9x3osweXc4VHl0o=
github.com/alecthomas/chroma/v2 v2.1.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
@@ -173,8 +176,6 @@ github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA=
github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c=
github.com/bep/godartsass v0.14.0 h1:pPb6XkpyDEppS+wK0veh7OXDQc4xzOJI9Qcjb743UeQ=
github.com/bep/godartsass v0.14.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8=
github.com/bep/golibsass v1.0.0 h1:gNguBMSDi5yZEZzVZP70YpuFQE3qogJIGUlrVILTmOw=
github.com/bep/golibsass v1.0.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
github.com/bep/golibsass v1.1.0 h1:pjtXr00IJZZaOdfryNa9wARTB3Q0BmxC3/V1KNcgyTw=
github.com/bep/golibsass v1.1.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
github.com/bep/gowebp v0.1.0 h1:4/iQpfnxHyXs3x/aTxMMdOpLEQQhFmF6G7EieWPTQyo=
@@ -193,8 +194,8 @@ github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgk
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g=
github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -238,8 +239,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanw/esbuild v0.14.39 h1:1TMZtCXOY4ctAbGY4QT9sjT203I/cQ16vXt2F9rLT58=
github.com/evanw/esbuild v0.14.39/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY=
github.com/evanw/esbuild v0.14.43 h1:4WIYkAf2fLmru3KYtKUB6mdBl4dgpoVcq7hqXhXGVG0=
github.com/evanw/esbuild v0.14.43/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
@@ -250,11 +251,10 @@ github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUork
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/getkin/kin-openapi v0.94.0 h1:bAxg2vxgnHHHoeefVdmGbR+oxtJlcv5HsJJa3qmAHuo=
github.com/getkin/kin-openapi v0.94.0/go.mod h1:LWZfzOd7PRy8GJ1dJ6mCU6tNdSfOwRac1BUPam4aw6Q=
github.com/getkin/kin-openapi v0.97.0 h1:bsvXZeuGiCW43ZKy6xOY5qfT5fCRYmnJwierblSrHCU=
github.com/getkin/kin-openapi v0.97.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
@@ -403,6 +403,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU=
github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -461,12 +463,12 @@ github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78Rwc
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niklasfasching/go-org v1.6.2 h1:kQBIZlfL4oRNApJCrBgaeNBfzxWzP6XlC7/b744Polk=
github.com/niklasfasching/go-org v1.6.2/go.mod h1:wn76Xgu4/KRe43WZhsgZjxYMaloSrl3BSweGV74SwHs=
github.com/niklasfasching/go-org v1.6.5 h1:5YAIqNTdl6lAOb7lD2AyQ1RuFGPVrAKvUexphk8PGbo=
github.com/niklasfasching/go-org v1.6.5/go.mod h1:ybv0eGDnxylFUfFE+ySaQc734j/L3+/ChKZ/h63a2wM=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pelletier/go-toml/v2 v2.0.0-beta.7.0.20220408132554-2377ac4bc04c h1:tgrA7vMkP4q9diPCg3u4Ka4/g3fvkifBu1y9DL+uy7M=
github.com/pelletier/go-toml/v2 v2.0.0-beta.7.0.20220408132554-2377ac4bc04c/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -481,14 +483,12 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sanity-io/litter v1.5.4 h1:3Fvo2hKVtmA0XFIHkFQ4cHxA0EemTqBA03WttcB3YZA=
github.com/sanity-io/litter v1.5.4/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -513,12 +513,12 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tdewolff/minify/v2 v2.11.2 h1:PpaPWhNlMVjkAKaOj0bbPv6KCVnrm8jbVwG7OtSdAqw=
github.com/tdewolff/minify/v2 v2.11.2/go.mod h1:NxozhBtgUVypPLzQdV96wkIu9J9vAiVmBcKhfC2zMfg=
github.com/tdewolff/parse/v2 v2.5.29 h1:Uf0OtZL9YaUXTuHEOitdo9lD90P0XTwCjZi+KbGChuM=
github.com/tdewolff/parse/v2 v2.5.29/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/tdewolff/minify/v2 v2.11.10 h1:2tk9nuKfc8YOTD8glZ7JF/VtE8W5HOgmepWdjcPtRro=
github.com/tdewolff/minify/v2 v2.11.10/go.mod h1:dHOS3dk+nJ0M3q3uM3VlNzTb70cou+ov0ki7C4PAFgM=
github.com/tdewolff/parse/v2 v2.6.0 h1:f2D7w32JtqjCv6SczWkfwK+m15et42qEtDnZXHoNY70=
github.com/tdewolff/parse/v2 v2.6.0/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -647,8 +647,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -746,8 +746,8 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -818,8 +818,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1017,8 +1017,10 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-40
View File
@@ -34,7 +34,6 @@ import (
"github.com/gohugoio/hugo/markup"
bp "github.com/gohugoio/hugo/bufferpool"
"github.com/gohugoio/hugo/config"
)
@@ -104,45 +103,6 @@ func NewContentSpec(cfg config.Provider, logger loggers.Logger, contentFs afero.
return spec, nil
}
var stripHTMLReplacer = strings.NewReplacer("\n", " ", "</p>", "\n", "<br>", "\n", "<br />", "\n")
// StripHTML accepts a string, strips out all HTML tags and returns it.
func StripHTML(s string) string {
// Shortcut strings with no tags in them
if !strings.ContainsAny(s, "<>") {
return s
}
s = stripHTMLReplacer.Replace(s)
// Walk through the string removing all tags
b := bp.GetBuffer()
defer bp.PutBuffer(b)
var inTag, isSpace, wasSpace bool
for _, r := range s {
if !inTag {
isSpace = false
}
switch {
case r == '<':
inTag = true
case r == '>':
inTag = false
case unicode.IsSpace(r):
isSpace = true
fallthrough
default:
if !inTag && (!isSpace || (isSpace && !wasSpace)) {
b.WriteRune(r)
}
}
wasSpace = isSpace
}
return b.String()
}
// stripEmptyNav strips out empty <nav> tags from content.
func stripEmptyNav(in []byte) []byte {
return bytes.Replace(in, []byte("<nav>\n</nav>\n\n"), []byte(``), -1)
-38
View File
@@ -52,44 +52,6 @@ func TestTrimShortHTML(t *testing.T) {
}
}
func TestStripHTML(t *testing.T) {
type test struct {
input, expected string
}
data := []test{
{"<h1>strip h1 tag <h1>", "strip h1 tag "},
{"<p> strip p tag </p>", " strip p tag "},
{"</br> strip br<br>", " strip br\n"},
{"</br> strip br2<br />", " strip br2\n"},
{"This <strong>is</strong> a\nnewline", "This is a newline"},
{"No Tags", "No Tags"},
{`<p>Summary Next Line.
<figure >
<img src="/not/real" />
</figure>
.
More text here.</p>
<p>Some more text</p>`, "Summary Next Line. . More text here.\nSome more text\n"},
}
for i, d := range data {
output := StripHTML(d.input)
if d.expected != output {
t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
}
}
}
func BenchmarkStripHTML(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
StripHTML(tstHTMLContent)
}
}
func TestStripEmptyNav(t *testing.T) {
c := qt.New(t)
cleaned := stripEmptyNav([]byte("do<nav>\n</nav>\n\nbedobedo"))
+4 -21
View File
@@ -1,11 +1,9 @@
package helpers
import (
"path/filepath"
"sort"
"strings"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/gohugoio/hugo/docshelper"
)
@@ -14,33 +12,18 @@ func init() {
docsProvider := func() docshelper.DocProvider {
var chromaLexers []any
sort.Sort(lexers.Registry.Lexers)
sort.Sort(lexers.GlobalLexerRegistry.Lexers)
for _, l := range lexers.Registry.Lexers {
for _, l := range lexers.GlobalLexerRegistry.Lexers {
config := l.Config()
var filenames []string
filenames = append(filenames, config.Filenames...)
filenames = append(filenames, config.AliasFilenames...)
aliases := config.Aliases
for _, filename := range filenames {
alias := strings.TrimSpace(strings.TrimPrefix(filepath.Ext(filename), "."))
if alias != "" {
aliases = append(aliases, alias)
}
}
aliases = UniqueStringsSorted(aliases)
lexerEntry := struct {
Name string
Aliases []string
}{
config.Name,
aliases,
config.Aliases,
}
chromaLexers = append(chromaLexers, lexerEntry)
+1 -9
View File
@@ -33,8 +33,6 @@ import (
"github.com/mitchellh/hashstructure"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/hugo"
"github.com/spf13/afero"
@@ -521,13 +519,7 @@ func PrintFs(fs afero.Fs, path string, w io.Writer) {
}
afero.Walk(fs, path, func(path string, info os.FileInfo, err error) error {
var filename string
var meta any
if fim, ok := info.(hugofs.FileMetaInfo); ok {
filename = fim.Meta().Filename
meta = fim.Meta()
}
fmt.Fprintf(w, " %q %q\t\t%v\n", path, filename, meta)
fmt.Println(path)
return nil
})
}
+5 -2
View File
@@ -32,7 +32,7 @@ func TestNewPathSpecFromConfig(t *testing.T) {
v.Set("uglyURLs", true)
v.Set("canonifyURLs", true)
v.Set("paginatePath", "side")
v.Set("baseURL", "http://base.com")
v.Set("baseURL", "http://base.com/foo")
v.Set("themesDir", "thethemes")
v.Set("layoutDir", "thelayouts")
v.Set("workingDir", "thework")
@@ -53,7 +53,10 @@ func TestNewPathSpecFromConfig(t *testing.T) {
c.Assert(p.Language.Lang, qt.Equals, "no")
c.Assert(p.PaginatePath, qt.Equals, "side")
c.Assert(p.BaseURL.String(), qt.Equals, "http://base.com")
c.Assert(p.BaseURL.String(), qt.Equals, "http://base.com/foo")
c.Assert(p.BaseURLString, qt.Equals, "http://base.com/foo")
c.Assert(p.BaseURLNoPathString, qt.Equals, "http://base.com")
c.Assert(p.ThemesDir, qt.Equals, "thethemes")
c.Assert(p.WorkingDir, qt.Equals, "thework")
}
+14 -11
View File
@@ -103,17 +103,11 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
}
if url.IsAbs() || strings.HasPrefix(in, "//") {
// It is already absolute, return it as is.
return in
}
var baseURL string
if strings.HasPrefix(in, "/") {
u := p.BaseURL.URL()
u.Path = ""
baseURL = u.String()
} else {
baseURL = p.BaseURL.String()
}
baseURL := p.getBaseURLRoot(in)
if addLanguage {
prefix := p.GetLanguagePrefix()
@@ -140,13 +134,22 @@ func (p *PathSpec) AbsURL(in string, addLanguage bool) string {
}
}
}
return paths.MakePermalink(baseURL, in).String()
}
// RelURL creates a URL relative to the BaseURL root.
// Note: The result URL will not include the context root if canonifyURLs is enabled.
func (p *PathSpec) getBaseURLRoot(path string) string {
if strings.HasPrefix(path, "/") {
// Treat it as relative to the server root.
return p.BaseURLNoPathString
} else {
// Treat it as relative to the baseURL.
return p.BaseURLString
}
}
func (p *PathSpec) RelURL(in string, addLanguage bool) string {
baseURL := p.BaseURL.String()
baseURL := p.getBaseURLRoot(in)
canonifyURLs := p.CanonifyURLs
if (!strings.HasPrefix(in, baseURL) && strings.HasPrefix(in, "http")) || strings.HasPrefix(in, "//") {
return in
+17 -7
View File
@@ -17,6 +17,7 @@ import (
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/langs"
)
@@ -59,6 +60,7 @@ func TestAbsURL(t *testing.T) {
}
func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
c := qt.New(t)
v := newTestCfg()
v.Set("multilingual", multilingual)
v.Set("defaultContentLanguage", "en")
@@ -69,6 +71,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
baseURL string
expected string
}{
// Issue 9994
{"foo/bar", "https://example.org/foo/", "https://example.org/foo/MULTIfoo/bar"},
{"/foo/bar", "https://example.org/foo/", "https://example.org/MULTIfoo/bar"},
{"/test/foo", "http://base/", "http://base/MULTItest/foo"},
{"/" + lang + "/test/foo", "http://base/", "http://base/" + lang + "/test/foo"},
{"", "http://base/ace/", "http://base/ace/MULTI"},
@@ -113,9 +119,8 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
} else {
expected = strings.Replace(expected, "MULTI", "", 1)
}
if output != expected {
t.Fatalf("Expected %#v, got %#v\n", expected, output)
}
c.Assert(output, qt.Equals, expected)
}
}
@@ -132,6 +137,7 @@ func TestRelURL(t *testing.T) {
}
func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, lang string) {
c := qt.New(t)
v := newTestCfg()
v.Set("multilingual", multilingual)
v.Set("defaultContentLanguage", "en")
@@ -143,13 +149,18 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
canonify bool
expected string
}{
// Issue 9994
{"/foo/bar", "https://example.org/foo/", false, "MULTI/foo/bar"},
{"foo/bar", "https://example.org/foo/", false, "/fooMULTI/foo/bar"},
{"/test/foo", "http://base/", false, "MULTI/test/foo"},
{"/" + lang + "/test/foo", "http://base/", false, "/" + lang + "/test/foo"},
{lang + "/test/foo", "http://base/", false, "/" + lang + "/test/foo"},
{"test.css", "http://base/sub", false, "/subMULTI/test.css"},
{"test.css", "http://base/sub", true, "MULTI/test.css"},
{"/test/", "http://base/", false, "MULTI/test/"},
{"/test/", "http://base/sub/", false, "/subMULTI/test/"},
{"test/", "http://base/sub/", false, "/subMULTI/test/"},
{"/test/", "http://base/sub/", true, "MULTI/test/"},
{"", "http://base/ace/", false, "/aceMULTI/"},
{"", "http://base/ace", false, "/aceMULTI"},
@@ -189,9 +200,8 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
expected = strings.Replace(expected, "MULTI", "", 1)
}
if output != expected {
t.Errorf("[%d][%t] Expected %#v, got %#v\n", i, test.canonify, expected, output)
}
c.Assert(output, qt.Equals, expected, qt.Commentf("[%d] %s", i, test.input))
}
}
-231
View File
@@ -1,231 +0,0 @@
// Copyright 2016 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
// 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
// limitations under the License.
package hugolib
import (
"path/filepath"
"testing"
"github.com/gohugoio/hugo/hugofs"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps"
"github.com/spf13/afero"
)
var (
caseMixingSiteConfigTOML = `
Title = "In an Insensitive Mood"
DefaultContentLanguage = "nn"
defaultContentLanguageInSubdir = true
[Blackfriday]
AngledQuotes = true
HrefTargetBlank = true
[Params]
Search = true
Color = "green"
mood = "Happy"
[Params.Colors]
Blue = "blue"
Yellow = "yellow"
[Languages]
[Languages.nn]
title = "Nynorsk title"
languageName = "Nynorsk"
weight = 1
[Languages.en]
TITLE = "English title"
LanguageName = "English"
Mood = "Thoughtful"
Weight = 2
COLOR = "Pink"
[Languages.en.blackfriday]
angledQuotes = false
hrefTargetBlank = false
[Languages.en.Colors]
BLUE = "blues"
Yellow = "golden"
`
caseMixingPage1En = `
---
TITLE: Page1 En Translation
BlackFriday:
AngledQuotes: false
Color: "black"
Search: true
mooD: "sad and lonely"
ColorS:
Blue: "bluesy"
Yellow: "sunny"
---
# "Hi"
{{< shortcode >}}
`
caseMixingPage1 = `
---
titLe: Side 1
blackFriday:
angledQuotes: true
color: "red"
search: false
MooD: "sad"
COLORS:
blue: "heavenly"
yelloW: "Sunny"
---
# "Hi"
{{< shortcode >}}
`
caseMixingPage2 = `
---
TITLE: Page2 Title
BlackFriday:
AngledQuotes: false
Color: "black"
search: true
MooD: "moody"
ColorS:
Blue: "sky"
YELLOW: "flower"
---
# Hi
{{< shortcode >}}
`
)
func caseMixingTestsWriteCommonSources(t *testing.T, fs afero.Fs) {
writeToFs(t, fs, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1)
writeToFs(t, fs, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2)
writeToFs(t, fs, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En)
writeToFs(t, fs, "layouts/shortcodes/shortcode.html", `
Shortcode Page: {{ .Page.Params.COLOR }}|{{ .Page.Params.Colors.Blue }}
Shortcode Site: {{ .Page.Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
`)
writeToFs(t, fs, "layouts/partials/partial.html", `
Partial Page: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
Partial Site: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
Partial Site Global: {{ site.Params.COLOR }}|{{ site.Params.COLORS.YELLOW }}
`)
writeToFs(t, fs, "config.toml", caseMixingSiteConfigTOML)
}
func TestCaseInsensitiveConfigurationVariations(t *testing.T) {
t.Parallel()
c := qt.New(t)
mm := afero.NewMemMapFs()
caseMixingTestsWriteCommonSources(t, mm)
cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Filename: "config.toml"})
c.Assert(err, qt.IsNil)
fs := hugofs.NewFrom(mm, cfg)
th := newTestHelper(cfg, fs, t)
writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `
Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
{{ block "main" . }}default{{end}}`)
writeSource(t, fs, filepath.Join("layouts", "sect2", "single.html"), `
{{ define "main"}}
Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }}
Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }}
{{ template "index-color" (dict "name" "Page" "params" .Params) }}
{{ template "index-color" (dict "name" "Site" "params" .Site.Params) }}
{{ .Content }}
{{ partial "partial.html" . }}
{{ end }}
{{ define "index-color" }}
{{ $yellow := index .params "COLoRS" "yELLOW" }}
{{ $colors := index .params "COLoRS" }}
{{ $yellow2 := index $colors "yEllow" }}
index1|{{ .name }}: {{ $yellow }}|
index2|{{ .name }}: {{ $yellow2 }}|
{{ end }}
`)
writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `
Page Title: {{ .Title }}
Site Title: {{ .Site.Title }}
Site Lang Mood: {{ .Site.Language.Params.MOoD }}
Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}|{{ index .Params "ColOR" }}
Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}|{{ index .Site.Params "ColOR" }}
{{ $page2 := .Site.GetPage "/sect2/page2" }}
{{ if $page2 }}
Page2: {{ $page2.Params.ColoR }}
{{ end }}
{{ .Content }}
{{ partial "partial.html" . }}
`)
sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
}
err = sites.Build(BuildCfg{})
if err != nil {
t.Fatalf("Failed to build sites: %s", err)
}
th.assertFileContent(filepath.Join("public", "nn", "sect1", "page1", "index.html"),
"Page Colors: red|heavenly|red",
"Site Colors: green|yellow|green",
"Site Lang Mood: Happy",
"Shortcode Page: red|heavenly",
"Shortcode Site: green|yellow",
"Partial Page: red|heavenly",
"Partial Site: green|yellow",
"Partial Site Global: green|yellow",
"Page Title: Side 1",
"Site Title: Nynorsk title",
"Page2: black ",
)
th.assertFileContent(filepath.Join("public", "en", "sect1", "page1", "index.html"),
"Site Colors: Pink|golden",
"Page Colors: black|bluesy",
"Site Lang Mood: Thoughtful",
"Page Title: Page1 En Translation",
"Site Title: English title",
"&ldquo;Hi&rdquo;",
)
th.assertFileContent(filepath.Join("public", "nn", "sect2", "page2", "index.html"),
"Page Colors: black|sky",
"Site Colors: green|yellow",
"Shortcode Page: black|sky",
"Block Page Colors: black|sky",
"Partial Page: black|sky",
"Partial Site: green|yellow",
"index1|Page: flower|",
"index1|Site: yellow|",
"index2|Page: flower|",
"index2|Site: yellow|",
)
}
-5
View File
@@ -108,11 +108,6 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
}
}
// Config deprecations.
if l.cfg.GetString("markup.defaultMarkdownHandler") == "blackfriday" {
helpers.Deprecated("markup.defaultMarkdownHandler=blackfriday", "See https://gohugo.io//content-management/formats/#list-of-content-formats", false)
}
// Some settings are used before we're done collecting all settings,
// so apply OS environment both before and after.
if err := l.applyOsEnvOverrides(d.Environ); err != nil {
+26
View File
@@ -756,3 +756,29 @@ theme_param="themevalue2"
})
}
func TestInvalidDefaultMarkdownHandler(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
[markup]
defaultMarkdownHandler = 'blackfriday'
-- content/_index.md --
## Foo
-- layouts/index.html --
{{ .Content }}
`
b, err := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).BuildE()
b.Assert(err, qt.IsNotNil)
b.Assert(err.Error(), qt.Contains, "Configured defaultMarkdownHandler \"blackfriday\" not found. Did you mean to use goldmark? Blackfriday was removed in Hugo v0.100.0.")
}
-2
View File
@@ -163,8 +163,6 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB
},
}
ps.shortcodeState = newShortcodeHandler(ps, ps.s, nil)
if err := ps.mapContent(parentBucket, metaProvider); err != nil {
return nil, ps.wrapError(err)
}
+4 -4
View File
@@ -383,8 +383,8 @@ Blog Section: {{ template "print-page" $blog }}
Blog Sub Section: {{ template "print-page" $blogSub }}
Page: {{ template "print-page" $page }}
Bundle: {{ template "print-page" $bundle }}
IsDescendant: true: {{ $page.IsDescendant $blog }} true: {{ $blogSub.IsDescendant $blog }} true: {{ $bundle.IsDescendant $blog }} true: {{ $page4.IsDescendant $blog }} true: {{ $blog.IsDescendant $home }} true: {{ $blog.IsDescendant $blog }} false: {{ $home.IsDescendant $blog }}
IsAncestor: true: {{ $blog.IsAncestor $page }} true: {{ $home.IsAncestor $blog }} true: {{ $blog.IsAncestor $blogSub }} true: {{ $blog.IsAncestor $bundle }} true: {{ $blog.IsAncestor $page4 }} true: {{ $home.IsAncestor $page }} true: {{ $blog.IsAncestor $blog }} false: {{ $page.IsAncestor $blog }} false: {{ $blog.IsAncestor $home }} false: {{ $blogSub.IsAncestor $blog }}
IsDescendant: true: {{ $page.IsDescendant $blog }} true: {{ $blogSub.IsDescendant $blog }} true: {{ $bundle.IsDescendant $blog }} true: {{ $page4.IsDescendant $blog }} true: {{ $blog.IsDescendant $home }} false: {{ $blog.IsDescendant $blog }} false: {{ $home.IsDescendant $blog }}
IsAncestor: true: {{ $blog.IsAncestor $page }} true: {{ $home.IsAncestor $blog }} true: {{ $blog.IsAncestor $blogSub }} true: {{ $blog.IsAncestor $bundle }} true: {{ $blog.IsAncestor $page4 }} true: {{ $home.IsAncestor $page }} false: {{ $blog.IsAncestor $blog }} false: {{ $page.IsAncestor $blog }} false: {{ $blog.IsAncestor $home }} false: {{ $blogSub.IsAncestor $blog }}
IsDescendant overlap1: false: {{ $overlap1.IsDescendant $overlap2 }}
IsDescendant overlap2: false: {{ $overlap2.IsDescendant $overlap1 }}
IsAncestor overlap1: false: {{ $overlap1.IsAncestor $overlap2 }}
@@ -426,8 +426,8 @@ Draft5: {{ if (.Site.GetPage "blog/draftsection/sub/page") }}FOUND{{ end }}|
Blog Sub Section: Page 3|/blog/subsection/|2019-06-03|Current Section: blog/subsection|Resources: application: /blog/subsection/subdata.json|
Page: Page 1|/blog/page1/|2019-06-01|Current Section: blog|Resources:
Bundle: Page 12|/blog/bundle/|0001-01-01|Current Section: blog|Resources: application: /blog/bundle/data.json|page: |
IsDescendant: true: true true: true true: true true: true true: true true: true false: false
IsAncestor: true: true true: true true: true true: true true: true true: true true: true false: false false: false false: false
IsDescendant: true: true true: true true: true true: true true: true false: false false: false
IsAncestor: true: true true: true true: true true: true true: true true: true false: false false: false false: false false: false
IsDescendant overlap1: false: false
IsDescendant overlap2: false: false
IsAncestor overlap1: false: false
-70
View File
@@ -18,7 +18,6 @@ import (
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers"
)
func TestRenderHookEditNestedPartial(t *testing.T) {
@@ -428,72 +427,3 @@ Image:
<p>html-image: image.jpg|Text: Hello<br> Goodbye|Plain: Hello GoodbyeEND</p>
`)
}
func TestRenderString(t *testing.T) {
b := newTestSitesBuilder(t)
b.WithTemplates("index.html", `
{{ $p := site.GetPage "p1.md" }}
{{ $optBlock := dict "display" "block" }}
{{ $optOrg := dict "markup" "org" }}
RSTART:{{ "**Bold Markdown**" | $p.RenderString }}:REND
RSTART:{{ "**Bold Block Markdown**" | $p.RenderString $optBlock }}:REND
RSTART:{{ "/italic org mode/" | $p.RenderString $optOrg }}:REND
RSTART:{{ "## Header2" | $p.RenderString }}:REND
`, "_default/_markup/render-heading.html", "Hook Heading: {{ .Level }}")
b.WithContent("p1.md", `---
title: "p1"
---
`,
)
b.Build(BuildCfg{})
b.AssertFileContent("public/index.html", `
RSTART:<strong>Bold Markdown</strong>:REND
RSTART:<p><strong>Bold Block Markdown</strong></p>
RSTART:<em>italic org mode</em>:REND
RSTART:Hook Heading: 2:REND
`)
}
// https://github.com/gohugoio/hugo/issues/6882
func TestRenderStringOnListPage(t *testing.T) {
renderStringTempl := `
{{ .RenderString "**Hello**" }}
`
b := newTestSitesBuilder(t)
b.WithContent("mysection/p1.md", `FOO`)
b.WithTemplates(
"index.html", renderStringTempl,
"_default/list.html", renderStringTempl,
"_default/single.html", renderStringTempl,
)
b.Build(BuildCfg{})
for _, filename := range []string{
"index.html",
"mysection/index.html",
"categories/index.html",
"tags/index.html",
"mysection/p1/index.html",
} {
b.AssertFileContent("public/"+filename, `<strong>Hello</strong>`)
}
}
// Issue 9433
func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
t.Parallel()
logger := loggers.NewWarningLogger()
b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
disableKinds = ["page", "section", "taxonomy", "term"]
`)
b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
b.BuildE(BuildCfg{})
b.Assert(int(logger.LogCounters().WarnCounter.Count()), qt.Equals, 0)
}
+59
View File
@@ -214,3 +214,62 @@ func TestTimeOnError(t *testing.T) {
b.Assert(b.BuildE(BuildCfg{}), qt.Not(qt.IsNil))
}
func TestTOMLDates(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
timeZone = "America/Los_Angeles"
-- content/_index.md --
---
date: "2020-10-20"
---
-- content/p1.md --
+++
title = "TOML Date with UTC offset"
date = 2021-08-16T06:00:00+00:00
+++
## Foo
-- data/mydata.toml --
date = 2020-10-20
talks = [
{ date = 2017-01-23, name = "Past talk 1" },
{ date = 2017-01-24, name = "Past talk 2" },
{ date = 2017-01-26, name = "Past talk 3" },
{ date = 2050-02-12, name = "Future talk 1" },
{ date = 2050-02-13, name = "Future talk 2" },
]
-- layouts/index.html --
{{ $futureTalks := where site.Data.mydata.talks "date" ">" now }}
{{ $pastTalks := where site.Data.mydata.talks "date" "<" now }}
{{ $homeDate := site.Home.Date }}
{{ $p1Date := (site.GetPage "p1").Date }}
Future talks: {{ len $futureTalks }}
Past talks: {{ len $pastTalks }}
Home's Date should be greater than past: {{ gt $homeDate (index $pastTalks 0).date }}
Home's Date should be less than future: {{ lt $homeDate (index $futureTalks 0).date }}
Home's Date should be equal mydata date: {{ eq $homeDate site.Data.mydata.date }}
Full time: {{ $p1Date | time.Format ":time_full" }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.AssertFileContent("public/index.html", `
Future talks: 2
Past talks: 3
Home's Date should be greater than past: true
Home's Date should be less than future: true
Home's Date should be equal mydata date: true
Full time: 6:00:00 am UTC
`)
}
+22 -9
View File
@@ -76,18 +76,24 @@ type BaseFs struct {
theBigFs *filesystemsCollector
// Locks.
buildMu *lockedfile.Mutex // <project>/.hugo_build.lock
buildMuTests sync.Mutex // Used in tests.
buildMu Lockable // <project>/.hugo_build.lock
}
type Lockable interface {
Lock() (unlock func(), err error)
}
type fakeLockfileMutex struct {
mu sync.Mutex
}
func (f *fakeLockfileMutex) Lock() (func(), error) {
f.mu.Lock()
return func() { f.mu.Unlock() }, nil
}
// Tries to acquire a build lock.
func (fs *BaseFs) LockBuild() (unlock func(), err error) {
if htesting.IsTest {
fs.buildMuTests.Lock()
return func() {
fs.buildMuTests.Unlock()
}, nil
}
return fs.buildMu.Lock()
}
@@ -445,12 +451,19 @@ func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) err
sourceFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Source, p.WorkingDir))
publishFsStatic := fs.PublishDirStatic
var buildMu Lockable
if p.Cfg.GetBool("noBuildLock") || htesting.IsTest {
buildMu = &fakeLockfileMutex{}
} else {
buildMu = lockedfile.MutexAt(filepath.Join(p.WorkingDir, lockFileBuild))
}
b := &BaseFs{
SourceFs: sourceFs,
WorkDir: fs.WorkingDirReadOnly,
PublishFs: publishFs,
PublishFsStatic: publishFsStatic,
buildMu: lockedfile.MutexAt(filepath.Join(p.WorkingDir, lockFileBuild)),
buildMu: buildMu,
}
for _, opt := range options {
-63
View File
@@ -365,11 +365,6 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
c.Assert(frTags["FRtag1"], qt.Not(qt.IsNil))
b.AssertFileContent("public/fr/plaques/FRtag1/index.html", "FRtag1|Bonjour|http://example.com/blog/fr/plaques/FRtag1/")
// Check Blackfriday config
c.Assert(strings.Contains(content(doc1fr), "&laquo;"), qt.Equals, true)
c.Assert(strings.Contains(content(doc1en), "&laquo;"), qt.Equals, false)
c.Assert(strings.Contains(content(doc1en), "&ldquo;"), qt.Equals, true)
// en and nn have custom site menus
c.Assert(len(frSite.Menus()), qt.Equals, 0)
c.Assert(len(enSite.Menus()), qt.Equals, 1)
@@ -796,39 +791,6 @@ categories: ["mycat"]
}
}
// https://github.com/gohugoio/hugo/issues/5777
func TestTableOfContentsInShortcodes(t *testing.T) {
t.Parallel()
b := newMultiSiteTestDefaultBuilder(t)
b.WithTemplatesAdded("layouts/shortcodes/toc.html", tocShortcode)
b.WithTemplatesAdded("layouts/shortcodes/wrapper.html", "{{ .Inner }}")
b.WithContent("post/simple.en.md", tocPageSimple)
b.WithContent("post/variants1.en.md", tocPageVariants1)
b.WithContent("post/variants2.en.md", tocPageVariants2)
b.WithContent("post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings)
b.CreateSites().Build(BuildCfg{})
b.AssertFileContent("public/en/post/simple/index.html",
tocPageSimpleExpected,
// Make sure it is inserted twice
`TOC1: <nav id="TableOfContents">`,
`TOC2: <nav id="TableOfContents">`,
)
b.AssertFileContentFn("public/en/post/variants1/index.html", func(s string) bool {
return strings.Count(s, "TableOfContents") == 4
})
b.AssertFileContentFn("public/en/post/variants2/index.html", func(s string) bool {
return strings.Count(s, "TableOfContents") == 6
})
b.AssertFileContent("public/en/post/withSCInHeading/index.html", tocPageWithShortcodesInHeadingsExpected)
}
var tocShortcode = `
TOC1: {{ .Page.TableOfContents }}
@@ -970,12 +932,6 @@ enableRobotsTXT = true
[permalinks]
other = "/somewhere/else/:filename"
# TODO(bep)
[markup]
defaultMarkdownHandler = "blackfriday"
[markup.blackfriday]
angledQuotes = true
[Taxonomies]
tag = "tags"
@@ -984,8 +940,6 @@ tag = "tags"
weight = 10
title = "In English"
languageName = "English"
[Languages.en.blackfriday]
angledQuotes = false
[[Languages.en.menu.main]]
url = "/"
name = "Home"
@@ -1031,12 +985,6 @@ enableRobotsTXT: true
permalinks:
other: "/somewhere/else/:filename"
# TODO(bep)
markup:
defaultMarkdownHandler: blackfriday
blackFriday:
angledQuotes: true
Taxonomies:
tag: "tags"
@@ -1045,8 +993,6 @@ Languages:
weight: 10
title: "In English"
languageName: "English"
blackfriday:
angledQuotes: false
menu:
main:
- url: "/"
@@ -1092,12 +1038,6 @@ var multiSiteJSONConfigTemplate = `
"permalinks": {
"other": "/somewhere/else/:filename"
},
"markup": {
"defaultMarkdownHandler": "blackfriday",
"blackfriday": {
"angledQuotes": true
}
},
"Taxonomies": {
"tag": "tags"
},
@@ -1106,9 +1046,6 @@ var multiSiteJSONConfigTemplate = `
"weight": 10,
"title": "In English",
"languageName": "English",
"blackfriday": {
"angledQuotes": false
},
"menu": {
"main": [
{
+128
View File
@@ -15,6 +15,7 @@ package hugolib
import (
"fmt"
"math/rand"
"strings"
"testing"
@@ -320,3 +321,130 @@ The content.
b.CreateSites().Build(BuildCfg{})
}
// This is just a test to verify that BenchmarkBaseline is working as intended.
func TestBenchmarkBaseline(t *testing.T) {
cfg := IntegrationTestConfig{
T: t,
TxtarString: benchmarkBaselineFiles(),
}
b := NewIntegrationTestBuilder(cfg).Build()
b.Assert(len(b.H.Sites), qt.Equals, 4)
b.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 161)
b.Assert(len(b.H.Sites[0].Pages()), qt.Equals, 197)
b.Assert(len(b.H.Sites[2].RegularPages()), qt.Equals, 158)
b.Assert(len(b.H.Sites[2].Pages()), qt.Equals, 194)
}
func BenchmarkBaseline(b *testing.B) {
cfg := IntegrationTestConfig{
T: b,
TxtarString: benchmarkBaselineFiles(),
}
builders := make([]*IntegrationTestBuilder, b.N)
for i := range builders {
builders[i] = NewIntegrationTestBuilder(cfg)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
builders[i].Build()
}
}
func benchmarkBaselineFiles() string {
rnd := rand.New(rand.NewSource(32))
files := `
-- config.toml --
baseURL = "https://example.com"
defaultContentLanguage = 'en'
[module]
[[module.mounts]]
source = 'content/en'
target = 'content/en'
lang = 'en'
[[module.mounts]]
source = 'content/nn'
target = 'content/nn'
lang = 'nn'
[[module.mounts]]
source = 'content/no'
target = 'content/no'
lang = 'no'
[[module.mounts]]
source = 'content/sv'
target = 'content/sv'
lang = 'sv'
[[module.mounts]]
source = 'layouts'
target = 'layouts'
[languages]
[languages.en]
title = "English"
weight = 1
[languages.nn]
title = "Nynorsk"
weight = 2
[languages.no]
title = "Norsk"
weight = 3
[languages.sv]
title = "Svenska"
weight = 4
-- layouts/_default/list.html --
{{ .Title }}
{{ .Content }}
-- layouts/_default/single.html --
{{ .Title }}
{{ .Content }}
-- layouts/shortcodes/myshort.html --
{{ .Inner }}
`
contentTemplate := `
---
title: "Page %d"
date: "2018-01-01"
weight: %d
---
## Heading 1
Duis nisi reprehenderit nisi cupidatat cillum aliquip ea id eu esse commodo et.
## Heading 2
Aliqua labore enim et sint anim amet excepteur ea dolore.
{{< myshort >}}
Hello, World!
{{< /myshort >}}
Aliqua labore enim et sint anim amet excepteur ea dolore.
`
for _, lang := range []string{"en", "nn", "no", "sv"} {
files += fmt.Sprintf("\n-- content/%s/_index.md --\n"+contentTemplate, lang, 1, 1, 1)
for i, root := range []string{"", "foo", "bar", "baz"} {
for j, section := range []string{"posts", "posts/funny", "posts/science", "posts/politics", "posts/world", "posts/technology", "posts/world/news", "posts/world/news/europe"} {
n := i + j + 1
files += fmt.Sprintf("\n-- content/%s/%s/%s/_index.md --\n"+contentTemplate, lang, root, section, n, n, n)
for k := 1; k < rnd.Intn(30)+1; k++ {
n := n + k
files += fmt.Sprintf("\n-- content/%s/%s/%s/p%d.md --\n"+contentTemplate, lang, root, section, n, n, n)
}
}
}
}
return files
}
+21 -10
View File
@@ -41,13 +41,15 @@ func NewIntegrationTestBuilder(conf IntegrationTestConfig) *IntegrationTestBuild
}
if conf.NeedsOsFS {
tempDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-integration-test")
c.Assert(err, qt.IsNil)
conf.WorkingDir = filepath.Join(tempDir, conf.WorkingDir)
if !conf.PrintAndKeepTempDir {
c.Cleanup(clean)
} else {
fmt.Println("\nUsing WorkingDir dir:", conf.WorkingDir)
if !filepath.IsAbs(conf.WorkingDir) {
tempDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-integration-test")
c.Assert(err, qt.IsNil)
conf.WorkingDir = filepath.Join(tempDir, conf.WorkingDir)
if !conf.PrintAndKeepTempDir {
c.Cleanup(clean)
} else {
fmt.Println("\nUsing WorkingDir dir:", conf.WorkingDir)
}
}
} else if conf.WorkingDir == "" {
conf.WorkingDir = helpers.FilePathSeparator
@@ -195,7 +197,10 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder {
func (s *IntegrationTestBuilder) BuildE() (*IntegrationTestBuilder, error) {
s.Helper()
s.initBuilder()
if err := s.initBuilder(); err != nil {
return s, err
}
err := s.build(BuildCfg{})
return s, err
}
@@ -265,7 +270,8 @@ func (s *IntegrationTestBuilder) FileContent(filename string) string {
return s.readWorkingDir(s, s.fs, filepath.FromSlash(filename))
}
func (s *IntegrationTestBuilder) initBuilder() {
func (s *IntegrationTestBuilder) initBuilder() error {
var initErr error
s.builderInit.Do(func() {
var afs afero.Fs
if s.Cfg.NeedsOsFS {
@@ -318,7 +324,10 @@ func (s *IntegrationTestBuilder) initBuilder() {
depsCfg := deps.DepsCfg{Cfg: cfg, Fs: fs, Running: s.Cfg.Running, Logger: logger}
sites, err := NewHugoSites(depsCfg)
s.Assert(err, qt.IsNil)
if err != nil {
initErr = err
return
}
s.H = sites
s.fs = fs
@@ -336,6 +345,8 @@ func (s *IntegrationTestBuilder) initBuilder() {
}
})
return initErr
}
func (s *IntegrationTestBuilder) absFilename(filename string) string {
+64
View File
@@ -524,3 +524,67 @@ Blog|HasMenuCurrent: false|Page: Page(/blog/_index.md)
Blog|IsMenuCurrent: false|Page: Page(/blog/_index.md)
`)
}
// Issue 9846
func TestMenuHasMenuCurrentSection(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
disableKinds = ['RSS','sitemap','taxonomy','term']
[[menu.main]]
name = 'Home'
pageRef = '/'
weight = 1
[[menu.main]]
name = 'Tests'
pageRef = '/tests'
weight = 2
[[menu.main]]
name = 'Test 1'
pageRef = '/tests/test-1'
parent = 'Tests'
weight = 1
-- content/tests/test-1.md --
---
title: "Test 1"
---
-- layouts/_default/list.html --
{{ range site.Menus.main }}
{{ .Name }}|{{ .URL }}|IsMenuCurrent = {{ $.IsMenuCurrent "main" . }}|HasMenuCurrent = {{ $.HasMenuCurrent "main" . }}|
{{ range .Children }}
{{ .Name }}|{{ .URL }}|IsMenuCurrent = {{ $.IsMenuCurrent "main" . }}|HasMenuCurrent = {{ $.HasMenuCurrent "main" . }}|
{{ end }}
{{ end }}
{{/* Some tests for issue 9925 */}}
{{ $page := .Site.GetPage "tests/test-1" }}
{{ $section := site.GetPage "tests" }}
Home IsAncestor Self: {{ site.Home.IsAncestor site.Home }}
Home IsDescendant Self: {{ site.Home.IsDescendant site.Home }}
Section IsAncestor Self: {{ $section.IsAncestor $section }}
Section IsDescendant Self: {{ $section.IsDescendant $section}}
Page IsAncestor Self: {{ $page.IsAncestor $page }}
Page IsDescendant Self: {{ $page.IsDescendant $page}}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.AssertFileContent("public/tests/index.html", `
Tests|/tests/|IsMenuCurrent = true|HasMenuCurrent = false
Home IsAncestor Self: false
Home IsDescendant Self: false
Section IsAncestor Self: false
Section IsDescendant Self: false
Page IsAncestor Self: false
Page IsDescendant Self: false
`)
}
+44 -27
View File
@@ -336,7 +336,7 @@ func (p *pageState) HasShortcode(name string) bool {
return false
}
return p.shortcodeState.nameSet[name]
return p.shortcodeState.hasName(name)
}
func (p *pageState) Site() page.Site {
@@ -600,7 +600,7 @@ func (p *pageState) getContentConverter() converter.Converter {
// Only used for shortcode inner content.
markup = "markdown"
}
p.m.contentConverter, err = p.m.newContentConverter(p, markup, p.m.renderingConfigOverrides)
p.m.contentConverter, err = p.m.newContentConverter(p, markup)
})
if err != nil {
@@ -610,19 +610,36 @@ func (p *pageState) getContentConverter() converter.Converter {
}
func (p *pageState) mapContent(bucket *pagesMapBucket, meta *pageMeta) error {
s := p.shortcodeState
rn := &pageContentMap{
p.cmap = &pageContentMap{
items: make([]any, 0, 20),
}
iter := p.source.parsed.Iterator()
return p.mapContentForResult(
p.source.parsed,
p.shortcodeState,
p.cmap,
meta.markup,
func(m map[string]interface{}) error {
return meta.setMetadata(bucket, p, m)
},
)
}
func (p *pageState) mapContentForResult(
result pageparser.Result,
s *shortcodeHandler,
rn *pageContentMap,
markup string,
withFrontMatter func(map[string]any) error,
) error {
iter := result.Iterator()
fail := func(err error, i pageparser.Item) error {
if fe, ok := err.(herrors.FileError); ok {
return fe
}
return p.parseError(err, iter.Input(), i.Pos)
return p.parseError(err, result.Input(), i.Pos())
}
// the parser is guaranteed to return items in proper order or fail, so …
@@ -639,14 +656,14 @@ Loop:
case it.Type == pageparser.TypeIgnore:
case it.IsFrontMatter():
f := pageparser.FormatFromFrontMatterType(it.Type)
m, err := metadecoders.Default.UnmarshalToMap(it.Val, f)
m, err := metadecoders.Default.UnmarshalToMap(it.Val(result.Input()), f)
if err != nil {
if fe, ok := err.(herrors.FileError); ok {
pos := fe.Position()
// Apply the error to the content file.
pos.Filename = p.File().Filename()
// Offset the starting position of front matter.
offset := iter.LineNumber() - 1
offset := iter.LineNumber(result.Input()) - 1
if f == metadecoders.YAML {
offset -= 1
}
@@ -660,15 +677,17 @@ Loop:
}
}
if err := meta.setMetadata(bucket, p, m); err != nil {
return err
if withFrontMatter != nil {
if err := withFrontMatter(m); err != nil {
return err
}
}
frontMatterSet = true
next := iter.Peek()
if !next.IsDone() {
p.source.posMainContent = next.Pos
p.source.posMainContent = next.Pos()
}
if !p.s.shouldBuild(p) {
@@ -680,10 +699,10 @@ Loop:
posBody := -1
f := func(item pageparser.Item) bool {
if posBody == -1 && !item.IsDone() {
posBody = item.Pos
posBody = item.Pos()
}
if item.IsNonWhitespace() {
if item.IsNonWhitespace(result.Input()) {
p.truncated = true
// Done
@@ -693,12 +712,12 @@ Loop:
}
iter.PeekWalk(f)
p.source.posSummaryEnd = it.Pos
p.source.posSummaryEnd = it.Pos()
p.source.posBodyStart = posBody
p.source.hasSummaryDivider = true
if meta.markup != "html" {
// The content will be rendered by Blackfriday or similar,
if markup != "html" {
// The content will be rendered by Goldmark or similar,
// and we need to track the summary.
rn.AddReplacement(internalSummaryDividerPre, it)
}
@@ -708,19 +727,19 @@ Loop:
// let extractShortcode handle left delim (will do so recursively)
iter.Backup()
currShortcode, err := s.extractShortcode(ordinal, 0, iter)
currShortcode, err := s.extractShortcode(ordinal, 0, result.Input(), iter)
if err != nil {
return fail(err, it)
}
currShortcode.pos = it.Pos
currShortcode.length = iter.Current().Pos - it.Pos
currShortcode.pos = it.Pos()
currShortcode.length = iter.Current().Pos() - it.Pos()
if currShortcode.placeholder == "" {
currShortcode.placeholder = createShortcodePlaceholder("s", currShortcode.ordinal)
}
if currShortcode.name != "" {
s.nameSet[currShortcode.name] = true
s.addName(currShortcode.name)
}
if currShortcode.params == nil {
@@ -735,7 +754,7 @@ Loop:
rn.AddShortcode(currShortcode)
case it.Type == pageparser.TypeEmoji:
if emoji := helpers.Emoji(it.ValStr()); emoji != nil {
if emoji := helpers.Emoji(it.ValStr(result.Input())); emoji != nil {
rn.AddReplacement(emoji, it)
} else {
rn.AddBytes(it)
@@ -743,7 +762,7 @@ Loop:
case it.IsEOF():
break Loop
case it.IsError():
err := fail(errors.New(it.ValStr()), it)
err := fail(errors.New(it.ValStr(result.Input())), it)
currShortcode.err = err
return err
@@ -752,16 +771,14 @@ Loop:
}
}
if !frontMatterSet {
if !frontMatterSet && withFrontMatter != nil {
// Page content without front matter. Assign default front matter from
// cascades etc.
if err := meta.setMetadata(bucket, p, nil); err != nil {
if err := withFrontMatter(nil); err != nil {
return err
}
}
p.cmap = rn
return nil
}
+3
View File
@@ -102,6 +102,9 @@ type pageCommon struct {
// The parsed page content.
pageContent
// Keeps track of the shortcodes on a page.
shortcodeState *shortcodeHandler
// Set if feature enabled and this is in a Git repo.
gitInfo *gitmap.GitInfo
codeowners []string
+5 -7
View File
@@ -33,21 +33,19 @@ type pageContent struct {
cmap *pageContentMap
shortcodeState *shortcodeHandler
source rawPageContent
}
// returns the content to be processed by Blackfriday or similar.
func (p pageContent) contentToRender(renderedShortcodes map[string]string) []byte {
source := p.source.parsed.Input()
// returns the content to be processed by Goldmark or similar.
func (p pageContent) contentToRender(parsed pageparser.Result, pm *pageContentMap, renderedShortcodes map[string]string) []byte {
source := parsed.Input()
c := make([]byte, 0, len(source)+(len(source)/10))
for _, it := range p.cmap.items {
for _, it := range pm.items {
switch v := it.(type) {
case pageparser.Item:
c = append(c, source[v.Pos:v.Pos+len(v.Val)]...)
c = append(c, source[v.Pos():v.Pos()+len(v.Val(source))]...)
case pageContentReplacement:
c = append(c, v.val...)
case *shortcode:
+7 -20
View File
@@ -120,9 +120,8 @@ type pageMeta struct {
s *Site
renderingConfigOverrides map[string]any
contentConverterInit sync.Once
contentConverter converter.Converter
contentConverterInit sync.Once
contentConverter converter.Converter
}
func (p *pageMeta) Aliases() []string {
@@ -744,21 +743,10 @@ func (p *pageMeta) applyDefaultValues(n *contentNode) error {
}
}
if !p.f.IsZero() {
var renderingConfigOverrides map[string]any
bfParam := getParamToLower(p, "blackfriday")
if bfParam != nil {
renderingConfigOverrides = maps.ToStringMap(bfParam)
}
p.renderingConfigOverrides = renderingConfigOverrides
}
return nil
}
func (p *pageMeta) newContentConverter(ps *pageState, markup string, renderingConfigOverrides map[string]any) (converter.Converter, error) {
func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.Converter, error) {
if ps == nil {
panic("no Page provided")
}
@@ -780,11 +768,10 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string, renderingCo
cpp, err := cp.New(
converter.DocumentContext{
Document: newPageForRenderHook(ps),
DocumentID: id,
DocumentName: path,
Filename: filename,
ConfigOverrides: renderingConfigOverrides,
Document: newPageForRenderHook(ps),
DocumentID: id,
DocumentName: path,
Filename: filename,
},
)
if err != nil {
+2
View File
@@ -66,6 +66,8 @@ func newPageBase(metaProvider *pageMeta) (*pageState, error) {
},
}
ps.shortcodeState = newShortcodeHandler(ps, ps.s)
siteAdapter := pageSiteAdapter{s: s, p: ps}
ps.pageMenus = &pageMenus{p: ps}
+75 -15
View File
@@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/common/types/hstring"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/parser/pageparser"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cast"
@@ -35,7 +36,7 @@ import (
"github.com/gohugoio/hugo/markup/converter"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/gohugoio/hugo/lazy"
bp "github.com/gohugoio/hugo/bufferpool"
@@ -117,7 +118,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
p.pageOutputTemplateVariationsState.Store(2)
}
cp.workContent = p.contentToRender(cp.contentPlaceholders)
cp.workContent = p.contentToRender(p.source.parsed, p.cmap, cp.contentPlaceholders)
isHTML := cp.p.m.markup == "html"
@@ -201,7 +202,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
})
cp.initPlain = cp.initMain.Branch(func() (any, error) {
cp.plain = helpers.StripHTML(string(cp.content))
cp.plain = tpl.StripHTML(string(cp.content))
cp.plainWords = strings.Fields(cp.plain)
cp.setWordCounts(p.m.isCJKLanguage)
@@ -336,7 +337,7 @@ func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
return "", errors.New("want 1 or 2 arguments")
}
var s string
var contentToRender string
opts := defaultRenderStringOpts
sidx := 1
@@ -353,16 +354,16 @@ func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
}
}
contentToRender := args[sidx]
contentToRenderv := args[sidx]
if _, ok := contentToRender.(hstring.RenderedString); ok {
if _, ok := contentToRenderv.(hstring.RenderedString); ok {
// This content is already rendered, this is potentially
// a infinite recursion.
return "", errors.New("text is already rendered, repeating it may cause infinite recursion")
}
var err error
s, err = cast.ToStringE(contentToRender)
contentToRender, err = cast.ToStringE(contentToRenderv)
if err != nil {
return "", err
}
@@ -375,26 +376,85 @@ func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
if opts.Markup != "" && opts.Markup != p.p.m.markup {
var err error
// TODO(bep) consider cache
conv, err = p.p.m.newContentConverter(p.p, opts.Markup, nil)
conv, err = p.p.m.newContentConverter(p.p, opts.Markup)
if err != nil {
return "", p.p.wrapError(err)
}
}
c, err := p.renderContentWithConverter(conv, []byte(s), false)
if err != nil {
return "", p.p.wrapError(err)
}
var rendered []byte
b := c.Bytes()
if strings.Contains(contentToRender, "{{") {
// Probably a shortcode.
parsed, err := pageparser.ParseMain(strings.NewReader(contentToRender), pageparser.Config{})
if err != nil {
return "", err
}
pm := &pageContentMap{
items: make([]any, 0, 20),
}
s := newShortcodeHandler(p.p, p.p.s)
if err := p.p.mapContentForResult(
parsed,
s,
pm,
opts.Markup,
nil,
); err != nil {
return "", err
}
placeholders, hasShortcodeVariants, err := s.renderShortcodesForPage(p.p, p.f)
if err != nil {
return "", err
}
if hasShortcodeVariants {
p.p.pageOutputTemplateVariationsState.Store(2)
}
b, err := p.renderContentWithConverter(conv, p.p.contentToRender(parsed, pm, placeholders), false)
if err != nil {
return "", p.p.wrapError(err)
}
rendered = b.Bytes()
if p.placeholdersEnabled {
// ToC was accessed via .Page.TableOfContents in the shortcode,
// at a time when the ToC wasn't ready.
if _, err := p.p.Content(); err != nil {
return "", err
}
placeholders[tocShortcodePlaceholder] = string(p.tableOfContents)
}
if pm.hasNonMarkdownShortcode || p.placeholdersEnabled {
rendered, err = replaceShortcodeTokens(rendered, placeholders)
if err != nil {
return "", err
}
}
// We need a consolidated view in $page.HasShortcode
p.p.shortcodeState.transferNames(s)
} else {
c, err := p.renderContentWithConverter(conv, []byte(contentToRender), false)
if err != nil {
return "", p.p.wrapError(err)
}
rendered = c.Bytes()
}
if opts.Display == "inline" {
// We may have to rethink this in the future when we get other
// renderers.
b = p.p.s.ContentSpec.TrimShortHTML(b)
rendered = p.p.s.ContentSpec.TrimShortHTML(rendered)
}
return template.HTML(string(b)), nil
return template.HTML(string(rendered)), nil
}
func (p *pageContentOutput) RenderWithTemplateInfo(info tpl.Info, layout ...string) (template.HTML, error) {
+6 -8
View File
@@ -36,6 +36,9 @@ func (pt pageTree) IsAncestor(other any) (bool, error) {
}
ref1, ref2 := pt.p.getTreeRef(), tp.getTreeRef()
if ref1 != nil && ref2 != nil && ref1.key == ref2.key {
return false, nil
}
if ref1 != nil && ref1.key == "/" {
return true, nil
@@ -50,10 +53,6 @@ func (pt pageTree) IsAncestor(other any) (bool, error) {
return ref1.n.p.IsHome(), nil
}
if ref1.key == ref2.key {
return true, nil
}
if strings.HasPrefix(ref2.key, ref1.key) {
return true, nil
}
@@ -82,6 +81,9 @@ func (pt pageTree) IsDescendant(other any) (bool, error) {
}
ref1, ref2 := pt.p.getTreeRef(), tp.getTreeRef()
if ref1 != nil && ref2 != nil && ref1.key == ref2.key {
return false, nil
}
if ref2 != nil && ref2.key == "/" {
return true, nil
@@ -96,10 +98,6 @@ func (pt pageTree) IsDescendant(other any) (bool, error) {
return ref2.n.p.IsHome(), nil
}
if ref1.key == ref2.key {
return true, nil
}
if strings.HasPrefix(ref1.key, ref2.key) {
return true, nil
}
+6 -103
View File
@@ -26,6 +26,7 @@ import (
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/markup/asciidocext"
"github.com/gohugoio/hugo/markup/rst"
"github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/config"
@@ -40,7 +41,6 @@ import (
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
)
const (
@@ -238,16 +238,6 @@ the cylinder and strike me down. ## BB
### BBB
"You're a great Granser," he cried delightedly, "always making believe them little marks mean something."
`
simplePageWithAdditionalExtension = `+++
[blackfriday]
extensions = ["hardLineBreak"]
+++
first line.
second line.
fourth line.
`
simplePageWithURL = `---
@@ -351,7 +341,7 @@ func normalizeExpected(ext, str string) string {
default:
return str
case "html":
return strings.Trim(helpers.StripHTML(str), " ")
return strings.Trim(tpl.StripHTML(str), " ")
case "ad":
paragraphs := strings.Split(str, "</p>")
expected := ""
@@ -694,26 +684,6 @@ func TestPageWithShortCodeInSummary(t *testing.T) {
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithShortcodeInSummary)
}
func TestPageWithAdditionalExtension(t *testing.T) {
t.Parallel()
cfg, fs := newTestCfg()
cfg.Set("markup", map[string]any{
"defaultMarkdownHandler": "blackfriday", // TODO(bep)
})
c := qt.New(t)
writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithAdditionalExtension)
s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
c.Assert(len(s.RegularPages()), qt.Equals, 1)
p := s.RegularPages()[0]
checkPageContent(t, p, "<p>first line.<br />\nsecond line.</p>\n\n<p>fourth line.</p>\n")
}
func TestTableOfContents(t *testing.T) {
cfg, fs := newTestCfg()
c := qt.New(t)
@@ -1373,7 +1343,7 @@ title: "HTML Content"
---
`
b.WithContent("regular.html", frontmatter+`<h1>Hugo</h1>`)
b.WithContent("noblackfridayforyou.html", frontmatter+`**Hugo!**`)
b.WithContent("nomarkdownforyou.html", frontmatter+`**Hugo!**`)
b.WithContent("manualsummary.html", frontmatter+`
<p>This is summary</p>
<!--more-->
@@ -1387,8 +1357,8 @@ title: "HTML Content"
"Summary: Hugo|Truncated: false")
b.AssertFileContent(
"public/noblackfridayforyou/index.html",
"Permalink: http://example.com/noblackfridayforyou/|**Hugo!**|",
"public/nomarkdownforyou/index.html",
"Permalink: http://example.com/nomarkdownforyou/|**Hugo!**|",
)
// https://github.com/gohugoio/hugo/issues/5723
@@ -1736,6 +1706,7 @@ Len Summary: {{ len .Summary }}
Len Content: {{ len .Content }}
SUMMARY:{{ .Summary }}:{{ len .Summary }}:END
`}
b := newTestSitesBuilder(t)
@@ -2010,74 +1981,6 @@ Link with URL as text
`)
}
func TestBlackfridayDefault(t *testing.T) {
t.Parallel()
b := newTestSitesBuilder(t).WithConfigFile("toml", `
baseURL = "https://example.org"
[markup]
defaultMarkdownHandler="blackfriday"
[markup.highlight]
noClasses=false
[markup.goldmark]
[markup.goldmark.renderer]
unsafe=true
`)
// Use the new attribute syntax to make sure it's not Goldmark.
b.WithTemplatesAdded("_default/single.html", `
Title: {{ .Title }}
Content: {{ .Content }}
`, "shortcodes/s.html", `## Code
{{ .Inner }}
`)
content := `
+++
title = "A Page!"
+++
## Code Fense in Shortcode
{{% s %}}
S:
{{% s %}}
$$$bash {hl_lines=[1]}
SHORT
$$$
{{% /s %}}
{{% /s %}}
## Code Fence
$$$bash {hl_lines=[1]}
MARKDOWN
$$$
`
content = strings.ReplaceAll(content, "$$$", "```")
for i, ext := range []string{"md", "html"} {
b.WithContent(fmt.Sprintf("page%d.%s", i+1, ext), content)
}
b.Build(BuildCfg{})
// Blackfriday does not support this extended attribute syntax.
b.AssertFileContent("public/page1/index.html",
`<pre tabindex="0"><code class="language-bash {hl_lines=[1]}" data-lang="bash {hl_lines=[1]}">SHORT</code></pre>`,
`<pre tabindex="0"><code class="language-bash {hl_lines=[1]}" data-lang="bash {hl_lines=[1]}">MARKDOWN`,
)
b.AssertFileContent("public/page2/index.html",
`<pre tabindex="0"><code class="language-bash {hl_lines=[1]}" data-lang="bash {hl_lines=[1]}">SHORT`,
)
}
func TestPageCaseIssues(t *testing.T) {
t.Parallel()
+12 -3
View File
@@ -34,6 +34,8 @@ type Paths struct {
Cfg config.Provider
BaseURL
BaseURLString string
BaseURLNoPathString string
// If the baseURL contains a base path, e.g. https://example.com/docs, then "/docs" will be the BasePath.
BasePath string
@@ -145,10 +147,17 @@ func New(fs *hugofs.Fs, cfg config.Provider) (*Paths, error) {
}
}
var baseURLString = baseURL.String()
var baseURLNoPath = baseURL.URL()
baseURLNoPath.Path = ""
var baseURLNoPathString = baseURLNoPath.String()
p := &Paths{
Fs: fs,
Cfg: cfg,
BaseURL: baseURL,
Fs: fs,
Cfg: cfg,
BaseURL: baseURL,
BaseURLString: baseURLString,
BaseURLNoPathString: baseURLNoPathString,
DisablePathToLower: cfg.GetBool("disablePathToLower"),
RemovePathAccents: cfg.GetBool("removePathAccents"),
+192
View File
@@ -0,0 +1,192 @@
// 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 requiredF by applicable law 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
// limitations under the License.
package hugolib
import (
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers"
)
func TestRenderString(t *testing.T) {
b := newTestSitesBuilder(t)
b.WithTemplates("index.html", `
{{ $p := site.GetPage "p1.md" }}
{{ $optBlock := dict "display" "block" }}
{{ $optOrg := dict "markup" "org" }}
RSTART:{{ "**Bold Markdown**" | $p.RenderString }}:REND
RSTART:{{ "**Bold Block Markdown**" | $p.RenderString $optBlock }}:REND
RSTART:{{ "/italic org mode/" | $p.RenderString $optOrg }}:REND
RSTART:{{ "## Header2" | $p.RenderString }}:REND
`, "_default/_markup/render-heading.html", "Hook Heading: {{ .Level }}")
b.WithContent("p1.md", `---
title: "p1"
---
`,
)
b.Build(BuildCfg{})
b.AssertFileContent("public/index.html", `
RSTART:<strong>Bold Markdown</strong>:REND
RSTART:<p><strong>Bold Block Markdown</strong></p>
RSTART:<em>italic org mode</em>:REND
RSTART:Hook Heading: 2:REND
`)
}
// https://github.com/gohugoio/hugo/issues/6882
func TestRenderStringOnListPage(t *testing.T) {
renderStringTempl := `
{{ .RenderString "**Hello**" }}
`
b := newTestSitesBuilder(t)
b.WithContent("mysection/p1.md", `FOO`)
b.WithTemplates(
"index.html", renderStringTempl,
"_default/list.html", renderStringTempl,
"_default/single.html", renderStringTempl,
)
b.Build(BuildCfg{})
for _, filename := range []string{
"index.html",
"mysection/index.html",
"categories/index.html",
"tags/index.html",
"mysection/p1/index.html",
} {
b.AssertFileContent("public/"+filename, `<strong>Hello</strong>`)
}
}
// Issue 9433
func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
t.Parallel()
logger := loggers.NewWarningLogger()
b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
disableKinds = ["page", "section", "taxonomy", "term"]
`)
b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
b.BuildE(BuildCfg{})
b.Assert(int(logger.LogCounters().WarnCounter.Count()), qt.Equals, 0)
}
func TestRenderStringWithShortcode(t *testing.T) {
t.Parallel()
filesTemplate := `
-- config.toml --
title = "Hugo Rocks!"
enableInlineShortcodes = true
-- content/p1/index.md --
---
title: "P1"
---
## First
-- layouts/shortcodes/mark1.md --
{{ .Inner }}
-- layouts/shortcodes/mark2.md --
1. Item Mark2 1
1. Item Mark2 2
1. Item Mark2 2-1
1. Item Mark2 3
-- layouts/shortcodes/myhthml.html --
Title: {{ .Page.Title }}
TableOfContents: {{ .Page.TableOfContents }}
Page Type: {{ printf "%T" .Page }}
-- layouts/_default/single.html --
{{ .RenderString "Markdown: {{% mark2 %}}|HTML: {{< myhthml >}}|Inline: {{< foo.inline >}}{{ site.Title }}{{< /foo.inline >}}|" }}
HasShortcode: mark2:{{ .HasShortcode "mark2" }}:true
HasShortcode: foo:{{ .HasShortcode "foo" }}:false
`
t.Run("Basic", func(t *testing.T) {
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: filesTemplate,
},
).Build()
b.AssertFileContent("public/p1/index.html",
"<p>Markdown: 1. Item Mark2 1</p>\n<ol>\n<li>Item Mark2 2\n<ol>\n<li>Item Mark2 2-1</li>\n</ol>\n</li>\n<li>Item Mark2 3|",
"<a href=\"#first\">First</a>", // ToC
`
HTML: Title: P1
Inline: Hugo Rocks!
HasShortcode: mark2:true:true
HasShortcode: foo:false:false
Page Type: *hugolib.pageForShortcode`,
)
})
t.Run("Edit shortcode", func(t *testing.T) {
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: filesTemplate,
Running: true,
},
).Build()
b.EditFiles("layouts/shortcodes/myhthml.html", "Edit shortcode").Build()
b.AssertFileContent("public/p1/index.html",
`Edit shortcode`,
)
})
}
// Issue 9959
func TestRenderStringWithShortcodeInPageWithNoContentFile(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
-- layouts/shortcodes/myshort.html --
Page Kind: {{ .Page.Kind }}
-- layouts/index.html --
Short: {{ .RenderString "{{< myshort >}}" }}
Has myshort: {{ .HasShortcode "myshort" }}
Has other: {{ .HasShortcode "other" }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.AssertFileContent("public/index.html",
`
Page Kind: home
Has myshort: true
Has other: false
`)
}
+95 -18
View File
@@ -62,6 +62,12 @@ type ShortcodeWithPage struct {
// this ordinal will represent the position of this shortcode in the page content.
Ordinal int
// Indentation before the opening shortcode in the source.
indentation string
innerDeindentInit sync.Once
innerDeindent template.HTML
// pos is the position in bytes in the source file. Used for error logging.
posInit sync.Once
posOffset int
@@ -70,6 +76,27 @@ type ShortcodeWithPage struct {
scratch *maps.Scratch
}
// InnerDeindent returns the (potentially de-indented) inner content of the shortcode.
func (scp *ShortcodeWithPage) InnerDeindent() template.HTML {
if scp.indentation == "" {
return scp.Inner
}
scp.innerDeindentInit.Do(func() {
b := bp.GetBuffer()
text.VisitLinesAfter(string(scp.Inner), func(s string) {
if strings.HasPrefix(s, scp.indentation) {
b.WriteString(strings.TrimPrefix(s, scp.indentation))
} else {
b.WriteString(s)
}
})
scp.innerDeindent = template.HTML(b.String())
bp.PutBuffer(b)
})
return scp.innerDeindent
}
// Position returns this shortcode's detailed position. Note that this information
// may be expensive to calculate, so only use this in error situations.
func (scp *ShortcodeWithPage) Position() text.Position {
@@ -170,11 +197,13 @@ type shortcode struct {
ordinal int
err error
indentation string // indentation from source.
info tpl.Info // One of the output formats (arbitrary)
templs []tpl.Template // All output formats
// If set, the rendered shortcode is sent as part of the surrounding content
// to Blackfriday and similar.
// to Goldmark and similar.
// Before Hug0 0.55 we didn't send any shortcode output to the markup
// renderer, and this flag told Hugo to process the {{ .Inner }} content
// separately.
@@ -182,7 +211,7 @@ type shortcode struct {
// {{ $_hugo_config := `{ "version": 1 }`}}
doMarkup bool
// the placeholder in the source when passed to Blackfriday etc.
// the placeholder in the source when passed to Goldmark etc.
// This also identifies the rendered shortcode.
placeholder string
@@ -248,13 +277,14 @@ type shortcodeHandler struct {
shortcodes []*shortcode
// All the shortcode names in this set.
nameSet map[string]bool
nameSet map[string]bool
nameSetMu sync.RWMutex
// Configuration
enableInlineShortcodes bool
}
func newShortcodeHandler(p *pageState, s *Site, placeholderFunc func() string) *shortcodeHandler {
func newShortcodeHandler(p *pageState, s *Site) *shortcodeHandler {
sh := &shortcodeHandler{
p: p,
s: s,
@@ -323,7 +353,7 @@ func renderShortcode(
hasVariants = hasVariants || more
}
data := &ShortcodeWithPage{Ordinal: sc.ordinal, posOffset: sc.pos, Params: sc.params, Page: newPageForShortcode(p), Parent: parent, Name: sc.name}
data := &ShortcodeWithPage{Ordinal: sc.ordinal, posOffset: sc.pos, indentation: sc.indentation, Params: sc.params, Page: newPageForShortcode(p), Parent: parent, Name: sc.name}
if sc.params != nil {
data.IsNamedParams = reflect.TypeOf(sc.params).Kind() == reflect.Map
}
@@ -398,6 +428,22 @@ func renderShortcode(
return "", false, fe
}
if len(sc.inner) == 0 && len(sc.indentation) > 0 {
b := bp.GetBuffer()
i := 0
text.VisitLinesAfter(result, func(line string) {
// The first line is correctly indented.
if i > 0 {
b.WriteString(sc.indentation)
}
i++
b.WriteString(line)
})
result = b.String()
bp.PutBuffer(b)
}
return result, hasVariants, err
}
@@ -405,6 +451,28 @@ func (s *shortcodeHandler) hasShortcodes() bool {
return s != nil && len(s.shortcodes) > 0
}
func (s *shortcodeHandler) addName(name string) {
s.nameSetMu.Lock()
defer s.nameSetMu.Unlock()
s.nameSet[name] = true
}
func (s *shortcodeHandler) transferNames(in *shortcodeHandler) {
s.nameSetMu.Lock()
defer s.nameSetMu.Unlock()
for k := range in.nameSet {
s.nameSet[k] = true
}
}
func (s *shortcodeHandler) hasName(name string) bool {
s.nameSetMu.RLock()
defer s.nameSetMu.RUnlock()
_, ok := s.nameSet[name]
return ok
}
func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format) (map[string]string, bool, error) {
rendered := make(map[string]string)
@@ -441,19 +509,28 @@ func (s *shortcodeHandler) parseError(err error, input []byte, pos int) error {
// pageTokens state:
// - before: positioned just before the shortcode start
// - after: shortcode(s) consumed (plural when they are nested)
func (s *shortcodeHandler) extractShortcode(ordinal, level int, pt *pageparser.Iterator) (*shortcode, error) {
func (s *shortcodeHandler) extractShortcode(ordinal, level int, source []byte, pt *pageparser.Iterator) (*shortcode, error) {
if s == nil {
panic("handler nil")
}
sc := &shortcode{ordinal: ordinal}
// Back up one to identify any indentation.
if pt.Pos() > 0 {
pt.Backup()
item := pt.Next()
if item.IsIndentation() {
sc.indentation = item.ValStr(source)
}
}
cnt := 0
nestedOrdinal := 0
nextLevel := level + 1
const errorPrefix = "failed to extract shortcode"
fail := func(err error, i pageparser.Item) error {
return s.parseError(fmt.Errorf("%s: %w", errorPrefix, err), pt.Input(), i.Pos)
return s.parseError(fmt.Errorf("%s: %w", errorPrefix, err), source, i.Pos())
}
Loop:
@@ -473,10 +550,10 @@ Loop:
if cnt > 0 {
// nested shortcode; append it to inner content
pt.Backup()
nested, err := s.extractShortcode(nestedOrdinal, nextLevel, pt)
nested, err := s.extractShortcode(nestedOrdinal, nextLevel, source, pt)
nestedOrdinal++
if nested != nil && nested.name != "" {
s.nameSet[nested.name] = true
s.addName(nested.name)
}
if err == nil {
@@ -512,7 +589,7 @@ Loop:
// return that error, more specific
continue
}
return sc, fail(fmt.Errorf("shortcode %q has no .Inner, yet a closing tag was provided", next.Val), next)
return sc, fail(fmt.Errorf("shortcode %q has no .Inner, yet a closing tag was provided", next.ValStr(source)), next)
}
}
if next.IsRightShortcodeDelim() {
@@ -525,11 +602,11 @@ Loop:
return sc, nil
case currItem.IsText():
sc.inner = append(sc.inner, currItem.ValStr())
sc.inner = append(sc.inner, currItem.ValStr(source))
case currItem.Type == pageparser.TypeEmoji:
// TODO(bep) avoid the duplication of these "text cases", to prevent
// more of #6504 in the future.
val := currItem.ValStr()
val := currItem.ValStr(source)
if emoji := helpers.Emoji(val); emoji != nil {
sc.inner = append(sc.inner, string(emoji))
} else {
@@ -537,7 +614,7 @@ Loop:
}
case currItem.IsShortcodeName():
sc.name = currItem.ValStr()
sc.name = currItem.ValStr(source)
// Used to check if the template expects inner content.
templs := s.s.Tmpl().LookupVariants(sc.name)
@@ -548,7 +625,7 @@ Loop:
sc.info = templs[0].(tpl.Info)
sc.templs = templs
case currItem.IsInlineShortcodeName():
sc.name = currItem.ValStr()
sc.name = currItem.ValStr(source)
sc.isInline = true
case currItem.IsShortcodeParam():
if !pt.IsValueNext() {
@@ -557,11 +634,11 @@ Loop:
// named params
if sc.params == nil {
params := make(map[string]any)
params[currItem.ValStr()] = pt.Next().ValTyped()
params[currItem.ValStr(source)] = pt.Next().ValTyped(source)
sc.params = params
} else {
if params, ok := sc.params.(map[string]any); ok {
params[currItem.ValStr()] = pt.Next().ValTyped()
params[currItem.ValStr(source)] = pt.Next().ValTyped(source)
} else {
return sc, errShortCodeIllegalState
}
@@ -570,11 +647,11 @@ Loop:
// positional params
if sc.params == nil {
var params []any
params = append(params, currItem.ValTyped())
params = append(params, currItem.ValTyped(source))
sc.params = params
} else {
if params, ok := sc.params.([]any); ok {
params = append(params, currItem.ValTyped())
params = append(params, currItem.ValTyped(source))
sc.params = params
} else {
return sc, errShortCodeIllegalState
+218 -561
View File
@@ -21,332 +21,13 @@ import (
"testing"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/markup/asciidocext"
"github.com/gohugoio/hugo/markup/rst"
"github.com/gohugoio/hugo/parser/pageparser"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/tpl"
"github.com/spf13/cast"
qt "github.com/frankban/quicktest"
)
func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.TemplateManager) error) {
t.Helper()
CheckShortCodeMatchAndError(t, input, expected, withTemplate, false)
}
func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tpl.TemplateManager) error, expectError bool) {
t.Helper()
cfg, fs := newTestCfg()
cfg.Set("markup", map[string]any{
"defaultMarkdownHandler": "blackfriday", // TODO(bep)
})
c := qt.New(t)
// Need some front matter, see https://github.com/gohugoio/hugo/issues/2337
contentFile := `---
title: "Title"
---
` + input
writeSource(t, fs, "content/simple.md", contentFile)
b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}).WithNothingAdded()
err := b.BuildE(BuildCfg{})
if err != nil && !expectError {
t.Fatalf("Shortcode rendered error %s.", err)
}
if expectError {
c.Assert(err, qt.ErrorMatches, expected)
return
}
h := b.H
c.Assert(len(h.Sites), qt.Equals, 1)
c.Assert(len(h.Sites[0].RegularPages()), qt.Equals, 1)
output := strings.TrimSpace(content(h.Sites[0].RegularPages()[0]))
output = strings.TrimPrefix(output, "<p>")
output = strings.TrimSuffix(output, "</p>")
expected = strings.TrimSpace(expected)
if output != expected {
t.Fatalf("Shortcode render didn't match. got \n%q but expected \n%q", output, expected)
}
}
func TestNonSC(t *testing.T) {
t.Parallel()
// notice the syntax diff from 0.12, now comment delims must be added
CheckShortCodeMatch(t, "{{%/* movie 47238zzb */%}}", "{{% movie 47238zzb %}}", nil)
}
// Issue #929
func TestHyphenatedSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
return nil
}
CheckShortCodeMatch(t, "{{< hyphenated-video 47238zzb >}}", "Playing Video 47238zzb", wt)
}
// Issue #1753
func TestNoTrailingNewline(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/a.html", `{{ .Get 0 }}`)
return nil
}
CheckShortCodeMatch(t, "ab{{< a c >}}d", "abcd", wt)
}
func TestPositionalParamSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/video.html", `Playing Video {{ .Get 0 }}`)
return nil
}
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", wt)
CheckShortCodeMatch(t, "{{< video 47238zzb 132 >}}", "Playing Video 47238zzb", wt)
CheckShortCodeMatch(t, "{{<video 47238zzb>}}", "Playing Video 47238zzb", wt)
CheckShortCodeMatch(t, "{{<video 47238zzb >}}", "Playing Video 47238zzb", wt)
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", wt)
}
func TestPositionalParamIndexOutOfBounds(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/video.html", `Playing Video {{ with .Get 1 }}{{ . }}{{ else }}Missing{{ end }}`)
return nil
}
CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video Missing", wt)
}
// #5071
func TestShortcodeRelated(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/a.html", `{{ len (.Site.RegularPages.Related .Page) }}`)
return nil
}
CheckShortCodeMatch(t, "{{< a >}}", "0", wt)
}
func TestShortcodeInnerMarkup(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("shortcodes/a.html", `<div>{{ .Inner }}</div>`)
tem.AddTemplate("shortcodes/b.html", `**Bold**: <div>{{ .Inner }}</div>`)
return nil
}
CheckShortCodeMatch(t,
"{{< a >}}B: <div>{{% b %}}**Bold**{{% /b %}}</div>{{< /a >}}",
// This assertion looks odd, but is correct: for inner shortcodes with
// the {{% we treats the .Inner content as markup, but not the shortcode
// itself.
"<div>B: <div>**Bold**: <div><strong>Bold</strong></div></div></div>",
wt)
CheckShortCodeMatch(t,
"{{% b %}}This is **B**: {{< b >}}This is B{{< /b>}}{{% /b %}}",
"<strong>Bold</strong>: <div>This is <strong>B</strong>: <strong>Bold</strong>: <div>This is B</div></div>",
wt)
}
// some repro issues for panics in Go Fuzz testing
func TestNamedParamSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
return nil
}
CheckShortCodeMatch(t, `{{< img src="one" >}}`, `<img src="one">`, wt)
CheckShortCodeMatch(t, `{{< img class="aspen" >}}`, `<img class="aspen">`, wt)
CheckShortCodeMatch(t, `{{< img src= "one" >}}`, `<img src="one">`, wt)
CheckShortCodeMatch(t, `{{< img src ="one" >}}`, `<img src="one">`, wt)
CheckShortCodeMatch(t, `{{< img src = "one" >}}`, `<img src="one">`, wt)
CheckShortCodeMatch(t, `{{< img src = "one" class = "aspen grove" >}}`, `<img src="one" class="aspen grove">`, wt)
}
// Issue #2294
func TestNestedNamedMissingParam(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/acc.html", `<div class="acc">{{ .Inner }}</div>`)
tem.AddTemplate("_internal/shortcodes/div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
tem.AddTemplate("_internal/shortcodes/div2.html", `<div {{with .Get 0}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
return nil
}
CheckShortCodeMatch(t,
`{{% acc %}}{{% div %}}d1{{% /div %}}{{% div2 %}}d2{{% /div2 %}}{{% /acc %}}`,
"<div class=\"acc\"><div >d1</div><div >d2</div></div>", wt)
}
func TestIsNamedParamsSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/bynameorposition.html", `{{ with .Get "id" }}Named: {{ . }}{{ else }}Pos: {{ .Get 0 }}{{ end }}`)
tem.AddTemplate("_internal/shortcodes/ifnamedparams.html", `<div id="{{ if .IsNamedParams }}{{ .Get "id" }}{{ else }}{{ .Get 0 }}{{end}}">`)
return nil
}
CheckShortCodeMatch(t, `{{< ifnamedparams id="name" >}}`, `<div id="name">`, wt)
CheckShortCodeMatch(t, `{{< ifnamedparams position >}}`, `<div id="position">`, wt)
CheckShortCodeMatch(t, `{{< bynameorposition id="name" >}}`, `Named: name`, wt)
CheckShortCodeMatch(t, `{{< bynameorposition position >}}`, `Pos: position`, wt)
}
func TestInnerSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
}
CheckShortCodeMatch(t, `{{< inside class="aspen" >}}`, `<div class="aspen"></div>`, wt)
CheckShortCodeMatch(t, `{{< inside class="aspen" >}}More Here{{< /inside >}}`, "<div class=\"aspen\">More Here</div>", wt)
CheckShortCodeMatch(t, `{{< inside >}}More Here{{< /inside >}}`, "<div>More Here</div>", wt)
}
func TestInnerSCWithMarkdown(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
// Note: In Hugo 0.55 we made it so any outer {{%'s inner content was rendered as part of the surrounding
// markup. This solved lots of problems, but it also meant that this test had to be adjusted.
tem.AddTemplate("_internal/shortcodes/wrapper.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
tem.AddTemplate("_internal/shortcodes/inside.html", `{{ .Inner }}`)
return nil
}
CheckShortCodeMatch(t, `{{< wrapper >}}{{% inside %}}
# More Here
[link](http://spf13.com) and text
{{% /inside %}}{{< /wrapper >}}`, "<div><h1 id=\"more-here\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>", wt)
}
func TestEmbeddedSC(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" %}}`, "<figure class=\"bananas orange\"><img src=\"/found/here\"/>\n</figure>", nil)
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" caption="This is a caption" %}}`, "<figure class=\"bananas orange\"><img src=\"/found/here\"\n alt=\"This is a caption\"/><figcaption>\n <p>This is a caption</p>\n </figcaption>\n</figure>", nil)
}
func TestNestedSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
tem.AddTemplate("_internal/shortcodes/scn2.html", `<div>SC2</div>`)
return nil
}
CheckShortCodeMatch(t, `{{% scn1 %}}{{% scn2 %}}{{% /scn1 %}}`, "<div>Outer, inner is <div>SC2</div></div>", wt)
CheckShortCodeMatch(t, `{{< scn1 >}}{{% scn2 %}}{{< /scn1 >}}`, "<div>Outer, inner is <div>SC2</div></div>", wt)
}
func TestNestedComplexSC(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/row.html", `-row-{{ .Inner}}-rowStop-`)
tem.AddTemplate("_internal/shortcodes/column.html", `-col-{{.Inner }}-colStop-`)
tem.AddTemplate("_internal/shortcodes/aside.html", `-aside-{{ .Inner }}-asideStop-`)
return nil
}
CheckShortCodeMatch(t, `{{< row >}}1-s{{% column %}}2-**s**{{< aside >}}3-**s**{{< /aside >}}4-s{{% /column %}}5-s{{< /row >}}6-s`,
"-row-1-s-col-2-<strong>s</strong>-aside-3-<strong>s</strong>-asideStop-4-s-colStop-5-s-rowStop-6-s", wt)
// turn around the markup flag
CheckShortCodeMatch(t, `{{% row %}}1-s{{< column >}}2-**s**{{% aside %}}3-**s**{{% /aside %}}4-s{{< /column >}}5-s{{% /row %}}6-s`,
"-row-1-s-col-2-<strong>s</strong>-aside-3-<strong>s</strong>-asideStop-4-s-colStop-5-s-rowStop-6-s", wt)
}
func TestParentShortcode(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
tem.AddTemplate("_internal/shortcodes/r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
tem.AddTemplate("_internal/shortcodes/r3.html", `3: {{ .Parent.Parent.Get "pr1" }}{{ .Parent.Get "pr2" }}{{ .Get "pr3" }} {{ .Inner }}`)
return nil
}
CheckShortCodeMatch(t, `{{< r1 pr1="p1" >}}1: {{< r2 pr2="p2" >}}2: {{< r3 pr3="p3" >}}{{< /r3 >}}{{< /r2 >}}{{< /r1 >}}`,
"1: p1 1: 2: p1p2 2: 3: p1p2p3 ", wt)
}
func TestFigureOnlySrc(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{< figure src="/found/here" >}}`, "<figure><img src=\"/found/here\"/>\n</figure>", nil)
}
func TestFigureCaptionAttrWithMarkdown(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{< figure src="/found/here" caption="Something **bold** _italic_" >}}`, "<figure><img src=\"/found/here\"\n alt=\"Something bold italic\"/><figcaption>\n <p>Something <strong>bold</strong> <em>italic</em></p>\n </figcaption>\n</figure>", nil)
CheckShortCodeMatch(t, `{{< figure src="/found/here" attr="Something **bold** _italic_" >}}`, "<figure><img src=\"/found/here\"/><figcaption>\n <p>Something <strong>bold</strong> <em>italic</em></p>\n </figcaption>\n</figure>", nil)
}
func TestFigureImgWidth(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="100px" %}}`, "<figure class=\"bananas orange\"><img src=\"/found/here\"\n alt=\"apple\" width=\"100px\"/>\n</figure>", nil)
}
func TestFigureImgHeight(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" height="100px" %}}`, "<figure class=\"bananas orange\"><img src=\"/found/here\"\n alt=\"apple\" height=\"100px\"/>\n</figure>", nil)
}
func TestFigureImgWidthAndHeight(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="50" height="100" %}}`, "<figure class=\"bananas orange\"><img src=\"/found/here\"\n alt=\"apple\" width=\"50\" height=\"100\"/>\n</figure>", nil)
}
func TestFigureLinkNoTarget(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{< figure src="/found/here" link="/jump/here/on/clicking" >}}`, "<figure><a href=\"/jump/here/on/clicking\"><img src=\"/found/here\"/></a>\n</figure>", nil)
}
func TestFigureLinkWithTarget(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{< figure src="/found/here" link="/jump/here/on/clicking" target="_self" >}}`, "<figure><a href=\"/jump/here/on/clicking\" target=\"_self\"><img src=\"/found/here\"/></a>\n</figure>", nil)
}
func TestFigureLinkWithTargetAndRel(t *testing.T) {
t.Parallel()
CheckShortCodeMatch(t, `{{< figure src="/found/here" link="/jump/here/on/clicking" target="_blank" rel="noopener" >}}`, "<figure><a href=\"/jump/here/on/clicking\" target=\"_blank\" rel=\"noopener\"><img src=\"/found/here\"/></a>\n</figure>", nil)
}
// #1642
func TestShortcodeWrappedInPIssue(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate("_internal/shortcodes/bug.html", `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`)
return nil
}
CheckShortCodeMatch(t, `
{{< bug >}}
{{< bug >}}
`, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", wt)
}
// #6866
func TestShortcodeIncomplete(t *testing.T) {
t.Parallel()
CheckShortCodeMatchAndError(t, `{{< >}}`, ".*shortcode has no name.*", nil, true)
}
func TestExtractShortcodes(t *testing.T) {
b := newTestSitesBuilder(t).WithSimpleConfigFile()
@@ -368,13 +49,6 @@ title: "Shortcodes Galore!"
s := b.H.Sites[0]
/*errCheck := func(s string) func(name string, assert *require.Assertions, shortcode *shortcode, err error) {
return func(name string, assert *require.Assertions, shortcode *shortcode, err error) {
c.Assert(err, name, qt.Not(qt.IsNil))
c.Assert(err.Error(), name, qt.Equals, s)
}
}*/
// Make it more regexp friendly
strReplacer := strings.NewReplacer("[", "{", "]", "}")
@@ -433,216 +107,18 @@ title: "Shortcodes Galore!"
t.Parallel()
c := qt.New(t)
counter := 0
placeholderFunc := func() string {
counter++
return fmt.Sprintf("HAHA%s-%dHBHB", shortcodePlaceholderPrefix, counter)
}
p, err := pageparser.ParseMain(strings.NewReader(test.input), pageparser.Config{})
c.Assert(err, qt.IsNil)
handler := newShortcodeHandler(nil, s, placeholderFunc)
handler := newShortcodeHandler(nil, s)
iter := p.Iterator()
short, err := handler.extractShortcode(0, 0, iter)
short, err := handler.extractShortcode(0, 0, p.Input(), iter)
test.check(c, short, err)
})
}
}
func TestShortcodesInSite(t *testing.T) {
baseURL := "http://foo/bar"
tests := []struct {
contentPath string
content string
outFile string
expected any
}{
{
"sect/doc1.md", `a{{< b >}}c`,
filepath.FromSlash("public/sect/doc1/index.html"), "<p>abc</p>\n",
},
// Issue #1642: Multiple shortcodes wrapped in P
// Deliberately forced to pass even if they maybe shouldn't.
{
"sect/doc2.md", `a
{{< b >}}
{{< c >}}
{{< d >}}
e`,
filepath.FromSlash("public/sect/doc2/index.html"),
"<p>a</p>\n\n<p>b<br />\nc\nd</p>\n\n<p>e</p>\n",
},
{
"sect/doc3.md", `a
{{< b >}}
{{< c >}}
{{< d >}}
e`,
filepath.FromSlash("public/sect/doc3/index.html"),
"<p>a</p>\n\n<p>b<br />\nc</p>\n\nd\n\n<p>e</p>\n",
},
{
"sect/doc4.md", `a
{{< b >}}
{{< b >}}
{{< b >}}
{{< b >}}
{{< b >}}
`,
filepath.FromSlash("public/sect/doc4/index.html"),
"<p>a\nb\nb\nb\nb\nb</p>\n",
},
// #2192 #2209: Shortcodes in markdown headers
{
"sect/doc5.md", `# {{< b >}}
## {{% c %}}`,
filepath.FromSlash("public/sect/doc5/index.html"), `-hbhb">b</h1>`,
},
// #2223 pygments
{
"sect/doc6.md", "\n```bash\nb = {{< b >}} c = {{% c %}}\n```\n",
filepath.FromSlash("public/sect/doc6/index.html"),
`<span class="nv">b</span>`,
},
// #2249
{
"sect/doc7.ad", `_Shortcodes:_ *b: {{< b >}} c: {{% c %}}*`,
filepath.FromSlash("public/sect/doc7/index.html"),
"<div class=\"paragraph\">\n<p><em>Shortcodes:</em> <strong>b: b c: c</strong></p>\n</div>\n",
},
{
"sect/doc8.rst", `**Shortcodes:** *b: {{< b >}} c: {{% c %}}*`,
filepath.FromSlash("public/sect/doc8/index.html"),
"<div class=\"document\">\n\n\n<p><strong>Shortcodes:</strong> <em>b: b c: c</em></p>\n</div>",
},
// Issue #1229: Menus not available in shortcode.
{
"sect/doc10.md", `---
menu:
main:
identifier: 'parent'
tags:
- Menu
---
**Menus:** {{< menu >}}`,
filepath.FromSlash("public/sect/doc10/index.html"),
"<p><strong>Menus:</strong> 1</p>\n",
},
// Issue #2323: Taxonomies not available in shortcode.
{
"sect/doc11.md", `---
tags:
- Bugs
menu:
main:
parent: 'parent'
---
**Tags:** {{< tags >}}`,
filepath.FromSlash("public/sect/doc11/index.html"),
"<p><strong>Tags:</strong> 2</p>\n",
},
{
"sect/doc12.md", `---
title: "Foo"
---
{{% html-indented-v1 %}}`,
"public/sect/doc12/index.html",
"<h1>Hugo!</h1>",
},
}
temp := tests[:0]
for _, test := range tests {
if strings.HasSuffix(test.contentPath, ".ad") && !asciidocext.Supports() {
t.Log("Skip Asciidoc test case as no Asciidoc present.")
continue
} else if strings.HasSuffix(test.contentPath, ".rst") && !rst.Supports() {
t.Log("Skip Rst test case as no rst2html present.")
continue
}
temp = append(temp, test)
}
tests = temp
sources := make([][2]string, len(tests))
for i, test := range tests {
sources[i] = [2]string{filepath.FromSlash(test.contentPath), test.content}
}
addTemplates := func(templ tpl.TemplateManager) error {
templ.AddTemplate("_default/single.html", "{{.Content}} Word Count: {{ .WordCount }}")
templ.AddTemplate("_internal/shortcodes/b.html", `b`)
templ.AddTemplate("_internal/shortcodes/c.html", `c`)
templ.AddTemplate("_internal/shortcodes/d.html", `d`)
templ.AddTemplate("_internal/shortcodes/html-indented-v1.html", "{{ $_hugo_config := `{ \"version\": 1 }` }}"+`
<h1>Hugo!</h1>
`)
templ.AddTemplate("_internal/shortcodes/menu.html", `{{ len (index .Page.Menus "main").Children }}`)
templ.AddTemplate("_internal/shortcodes/tags.html", `{{ len .Page.Site.Taxonomies.tags }}`)
return nil
}
cfg, fs := newTestCfg()
cfg.Set("defaultContentLanguage", "en")
cfg.Set("baseURL", baseURL)
cfg.Set("uglyURLs", false)
cfg.Set("verbose", true)
cfg.Set("security", map[string]any{
"exec": map[string]any{
"allow": []string{"^python$", "^rst2html.*", "^asciidoctor$"},
},
})
cfg.Set("markup.highlight.noClasses", false)
cfg.Set("markup.highlight.codeFences", true)
cfg.Set("markup", map[string]any{
"defaultMarkdownHandler": "blackfriday", // TODO(bep)
})
writeSourcesToSource(t, "content", fs, sources...)
s := buildSingleSite(t, deps.DepsCfg{WithTemplate: addTemplates, Fs: fs, Cfg: cfg}, BuildCfg{})
for i, test := range tests {
test := test
t.Run(fmt.Sprintf("test=%d;contentPath=%s", i, test.contentPath), func(t *testing.T) {
t.Parallel()
th := newTestHelper(s.Cfg, s.Fs, t)
expected := cast.ToStringSlice(test.expected)
th.assertFileContent(filepath.FromSlash(test.outFile), expected...)
})
}
}
func TestShortcodeMultipleOutputFormats(t *testing.T) {
t.Parallel()
@@ -815,6 +291,58 @@ func BenchmarkReplaceShortcodeTokens(b *testing.B) {
}
}
func BenchmarkShortcodesInSite(b *testing.B) {
files := `
-- config.toml --
-- layouts/shortcodes/mark1.md --
{{ .Inner }}
-- layouts/shortcodes/mark2.md --
1. Item Mark2 1
1. Item Mark2 2
1. Item Mark2 2-1
1. Item Mark2 3
-- layouts/_default/single.html --
{{ .Content }}
`
content := `
---
title: "Markdown Shortcode"
---
## List
1. List 1
{{§ mark1 §}}
1. Item Mark1 1
1. Item Mark1 2
{{§ mark2 §}}
{{§ /mark1 §}}
`
for i := 1; i < 100; i++ {
files += fmt.Sprintf("\n-- content/posts/p%d.md --\n"+content, i+1)
}
files = strings.ReplaceAll(files, "§", "%")
cfg := IntegrationTestConfig{
T: b,
TxtarString: files,
}
builders := make([]*IntegrationTestBuilder, b.N)
for i := range builders {
builders[i] = NewIntegrationTestBuilder(cfg)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
builders[i].Build()
}
}
func TestReplaceShortcodeTokens(t *testing.T) {
t.Parallel()
for i, this := range []struct {
@@ -1235,7 +763,7 @@ title: "Hugo Rocks!"
)
}
func TestShortcodeTypedParams(t *testing.T) {
func TestShortcodeParams(t *testing.T) {
t.Parallel()
c := qt.New(t)
@@ -1250,6 +778,7 @@ title: "Hugo Rocks!"
types positional: {{< hello true false 33 3.14 >}}
types named: {{< hello b1=true b2=false i1=33 f1=3.14 >}}
types string: {{< hello "true" trues "33" "3.14" >}}
escaped quoute: {{< hello "hello \"world\"." >}}
`).WithTemplatesAdded(
@@ -1268,28 +797,20 @@ Get: {{ printf "%v (%T)" $b1 $b1 | safeHTML }}
"types positional: - 0: true (bool) - 1: false (bool) - 2: 33 (int) - 3: 3.14 (float64)",
"types named: - b1: true (bool) - b2: false (bool) - f1: 3.14 (float64) - i1: 33 (int) Get: true (bool) ",
"types string: - 0: true (string) - 1: trues (string) - 2: 33 (string) - 3: 3.14 (string) ",
"hello &#34;world&#34;. (string)",
)
}
func TestShortcodeRef(t *testing.T) {
for _, plainIDAnchors := range []bool{false, true} {
plainIDAnchors := plainIDAnchors
t.Run(fmt.Sprintf("plainIDAnchors=%t", plainIDAnchors), func(t *testing.T) {
t.Parallel()
t.Parallel()
v := config.NewWithTestDefaults()
v.Set("baseURL", "https://example.org")
v.Set("blackfriday", map[string]any{
"plainIDAnchors": plainIDAnchors,
})
v.Set("markup", map[string]any{
"defaultMarkdownHandler": "blackfriday", // TODO(bep)
})
v := config.NewWithTestDefaults()
v.Set("baseURL", "https://example.org")
builder := newTestSitesBuilder(t).WithViper(v)
builder := newTestSitesBuilder(t).WithViper(v)
for i := 1; i <= 2; i++ {
builder.WithContent(fmt.Sprintf("page%d.md", i), `---
for i := 1; i <= 2; i++ {
builder.WithContent(fmt.Sprintf("page%d.md", i), `---
title: "Hugo Rocks!"
---
@@ -1305,33 +826,19 @@ title: "Hugo Rocks!"
`)
}
}
builder.Build(BuildCfg{})
builder.Build(BuildCfg{})
if plainIDAnchors {
builder.AssertFileContent("public/page2/index.html",
`
builder.AssertFileContent("public/page2/index.html", `
<a href="/page1/#doc">Page 1 with anchor</a>
<a href="https://example.org/page2/">Page 2</a>
<a href="/page2/#doc">Page 2 with anchor</a></p>
<h2 id="doc">Doc</h2>
`,
)
} else {
builder.AssertFileContent("public/page2/index.html",
`
<p><a href="https://example.org/page1/">Page 1</a>
<a href="/page1/#doc:45ca767ba77bc1445a0acab74f80812f">Page 1 with anchor</a>
<a href="https://example.org/page2/">Page 2</a>
<a href="/page2/#doc:8e3cdf52fa21e33270c99433820e46bd">Page 2 with anchor</a></p>
<h2 id="doc:8e3cdf52fa21e33270c99433820e46bd">Doc</h2>
`,
)
}
})
}
)
}
// https://github.com/gohugoio/hugo/issues/6857
@@ -1398,3 +905,153 @@ outputs: ["html", "css", "csv", "json"]
}
}
// #9821
func TestShortcodeMarkdownOutputFormat(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
-- content/p1.md --
---
title: "p1"
---
{{< foo >}}
-- layouts/shortcodes/foo.md --
§§§
<x
§§§
-- layouts/_default/single.html --
{{ .Content }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
Running: true,
},
).Build()
b.AssertFileContent("public/p1/index.html", `
<x
`)
}
func TestShortcodePreserveIndentation(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
-- content/p1.md --
---
title: "p1"
---
## List With Indented Shortcodes
1. List 1
{{% mark1 %}}
1. Item Mark1 1
1. Item Mark1 2
{{% mark2 %}}
{{% /mark1 %}}
-- layouts/shortcodes/mark1.md --
{{ .Inner }}
-- layouts/shortcodes/mark2.md --
1. Item Mark2 1
1. Item Mark2 2
1. Item Mark2 2-1
1. Item Mark2 3
-- layouts/_default/single.html --
{{ .Content }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
Running: true,
},
).Build()
b.AssertFileContent("public/p1/index.html", "<ol>\n<li>\n<p>List 1</p>\n<ol>\n<li>Item Mark1 1</li>\n<li>Item Mark1 2</li>\n<li>Item Mark2 1</li>\n<li>Item Mark2 2\n<ol>\n<li>Item Mark2 2-1</li>\n</ol>\n</li>\n<li>Item Mark2 3</li>\n</ol>\n</li>\n</ol>")
}
func TestShortcodeCodeblockIndent(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
-- content/p1.md --
---
title: "p1"
---
## Code block
{{% code %}}
-- layouts/shortcodes/code.md --
echo "foo";
-- layouts/_default/single.html --
{{ .Content }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
Running: true,
},
).Build()
b.AssertFileContent("public/p1/index.html", "<pre><code>echo &quot;foo&quot;;\n</code></pre>")
}
func TestShortcodeHighlightDeindent(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
[markup]
[markup.highlight]
codeFences = true
noClasses = false
-- content/p1.md --
---
title: "p1"
---
## Indent 5 Spaces
{{< highlight bash >}}
line 1;
line 2;
line 3;
{{< /highlight >}}
-- layouts/_default/single.html --
{{ .Content }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
Running: true,
},
).Build()
b.AssertFileContent("public/p1/index.html", `
<pre><code> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">line 1<span class="p">;</span>
</span></span><span class="line"><span class="cl">line 2<span class="p">;</span>
</span></span><span class="line"><span class="cl">line 3<span class="p">;</span></span></span></code></pre></div>
</code></pre>
`)
}

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