mirror of
https://github.com/gohugoio/hugo.git
synced 2026-09-02 03:32:38 +00:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9eb9b70a29 | |||
| 0ff4a9326f | |||
| 45e1084ff2 | |||
| c98348416c | |||
| 2de393c791 | |||
| 7efb35680f | |||
| ddbcc67122 | |||
| fd75f129b2 | |||
| 14878ca0a0 | |||
| e88873b803 | |||
| 4219993b0c | |||
| 988e1417a0 | |||
| 42529882ce | |||
| 45f1b1cfc5 | |||
| 369bdf22f8 | |||
| d1b03a093f | |||
| 941c28ab0c | |||
| ffbdcc75ab | |||
| 95d9764518 | |||
| b66f9f2665 | |||
| f7e00c039f | |||
| f5ba6fd45c | |||
| c4bbc1eeeb | |||
| 0e0fb1b648 | |||
| b017f7cb01 | |||
| 7fb28085ac | |||
| 0cd1929b9c | |||
| cbdaff2135 | |||
| 3fefea06b8 | |||
| 5c48ba9343 | |||
| 21562e3aad | |||
| 9c24b86e4b | |||
| 223bf28004 | |||
| 72b0ccdb01 | |||
| 65e52a7f5c | |||
| bdf935d66c | |||
| 8ebcaa5394 | |||
| 241481931f | |||
| 5caed8a71f | |||
| d1278f696a | |||
| 92f31ae63b | |||
| fd3953c18c | |||
| 15463f835b | |||
| 466fa43c16 | |||
| 6072ce0bcd | |||
| 2c5943ddc6 | |||
| 0cb459a20a | |||
| 475f87f685 | |||
| 604cfffc5b | |||
| d863dde6c6 | |||
| 580b214a4c | |||
| ddb9547085 | |||
| 288b0fb15a | |||
| 3e1344632d | |||
| 7a9ce0eca5 | |||
| f2ba0cc8c5 | |||
| 62ceaabdce | |||
| 35fa192838 | |||
| 09ac733381 | |||
| 66da1b7b2f | |||
| 5a9ecb82a3 | |||
| cbc35c48d2 | |||
| 44f3c07969 | |||
| a5a4422aae | |||
| 617e094482 | |||
| cf12fa6161 | |||
| 2e1c81770a | |||
| 4276075c78 | |||
| d25cb2943f | |||
| 8b9bdc403d | |||
| 4e94d1db72 | |||
| 0566bbf7c7 | |||
| 534e7155bb | |||
| 953f215f32 | |||
| 8e2fd55923 | |||
| 311b8008bf | |||
| c7d5f9f067 | |||
| bfebd8c02c | |||
| 0afb4866e3 | |||
| b1ec0c226f | |||
| 212d9e3017 | |||
| 4daac654d9 |
+96
-33
@@ -1,51 +1,114 @@
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: bepsays/ci-goreleaser:1.1800.000
|
||||
environment:
|
||||
CGO_ENABLED: "0"
|
||||
parameters:
|
||||
|
||||
# v2: 11m.
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.21900.20000
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
<<: *defaults
|
||||
prepare_release:
|
||||
<<: *defaults
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
steps:
|
||||
- checkout:
|
||||
- &remote-docker
|
||||
setup_remote_docker:
|
||||
version: 20.10.14
|
||||
- checkout:
|
||||
path: hugo
|
||||
- &git-config
|
||||
run:
|
||||
command: |
|
||||
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
|
||||
git config --global user.name "hugoreleaser"
|
||||
- run:
|
||||
command: |
|
||||
git clone git@github.com:gohugoio/hugoDocs.git
|
||||
cd hugo
|
||||
go mod download
|
||||
sleep 5
|
||||
go mod verify
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths: .
|
||||
release:
|
||||
<<: *defaults
|
||||
command: |
|
||||
cd hugo
|
||||
go mod download
|
||||
go run -tags release main.go release --step 1
|
||||
- save_cache:
|
||||
key: git-sha-{{ .Revision }}
|
||||
paths:
|
||||
- hugo
|
||||
- gomodcache
|
||||
build_container1:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /root/project
|
||||
- &restore-cache
|
||||
restore_cache:
|
||||
key: git-sha-{{ .Revision }}
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
|
||||
git config --global user.name "hugoreleaser"
|
||||
go run -tags release main.go release -r ${CIRCLE_BRANCH}
|
||||
|
||||
no_output_timeout: 20m
|
||||
command: |
|
||||
mkdir -p /tmp/files/dist1
|
||||
cd hugo
|
||||
hugoreleaser build -paths "builds/container1/**" -workers 3 -dist /tmp/files/dist1 -chunks $CIRCLE_NODE_TOTAL -chunk-index $CIRCLE_NODE_INDEX
|
||||
- &persist-workspace
|
||||
persist_to_workspace:
|
||||
root: /tmp/files
|
||||
paths:
|
||||
- dist1
|
||||
- dist2
|
||||
parallelism: 7
|
||||
build_container2:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.21900.20000
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: |
|
||||
mkdir -p /tmp/files/dist2
|
||||
cd hugo
|
||||
hugoreleaser build -paths "builds/container2/**" -workers 1 -dist /tmp/files/dist2
|
||||
- *persist-workspace
|
||||
archive_and_release:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
steps:
|
||||
- *restore-cache
|
||||
- *attach-workspace
|
||||
- *git-config
|
||||
- run:
|
||||
command: |
|
||||
cp -a /tmp/workspace/dist1/. ./hugo/dist
|
||||
cp -a /tmp/workspace/dist2/. ./hugo/dist
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
hugoreleaser archive
|
||||
hugoreleaser release
|
||||
go run -tags release main.go release --step 2
|
||||
workflows:
|
||||
version: 2
|
||||
release:
|
||||
jobs:
|
||||
- build:
|
||||
- prepare_release:
|
||||
filters:
|
||||
branches:
|
||||
only: /release-.*/
|
||||
- hold:
|
||||
type: approval
|
||||
- build_container1:
|
||||
requires:
|
||||
- build
|
||||
- release:
|
||||
- prepare_release
|
||||
- build_container2:
|
||||
requires:
|
||||
- prepare_release
|
||||
- archive_and_release:
|
||||
context: org-global
|
||||
requires:
|
||||
- hold
|
||||
- build_container1
|
||||
- build_container2
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
# Go 1.18 had some breaking changes on the source level which means Hugo cannot be built
|
||||
# with older Go versions, but the improvements in Go 1.18 were too good to pass on (e.g. break and continue).
|
||||
# Note that you don't need Go (or Go 1.18) to run a pre-built binary.
|
||||
go-version: [1.18.x]
|
||||
go-version: [1.18.x,1.19.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -49,8 +49,11 @@ jobs:
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
choco install pandoc
|
||||
Choco-Install -PackageName pandoc
|
||||
- run: pandoc -v
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Choco-Install -PackageName mingw -ArgumentList "--version","10.2.0","--allow-downgrade"
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass-embedded Linux
|
||||
run: |
|
||||
|
||||
+1
-7
@@ -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
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2022 The Hugo Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -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](https://go.dev/).
|
||||
|
||||
[Website](https://gohugo.io) |
|
||||
[Forum](https://discourse.gohugo.io) |
|
||||
@@ -13,9 +13,18 @@ A Fast and Flexible Static Site Generator built with love by [bep](https://githu
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Supported Architectures](#supported-architectures)
|
||||
* [Choose How to Install](#choose-how-to-install)
|
||||
* [Install Hugo as Your Site Generator (Binary Install)](#install-hugo-as-your-site-generator-binary-install)
|
||||
* [Build and Install the Binaries from Source (Advanced Install)](#build-and-install-the-binaries-from-source-advanced-install)
|
||||
* [The Hugo Documentation](#the-hugo-documentation)
|
||||
* [Contributing to Hugo](#contributing-code-to-hugo)
|
||||
* [Dependencies](#dependencies)
|
||||
|
||||
## Overview
|
||||
|
||||
Hugo is a static HTML and CSS website generator written in [Go][].
|
||||
Hugo is a static HTML and CSS website generator written in [Go](https://go.dev/).
|
||||
It is optimized for speed, ease of use, and configurability.
|
||||
Hugo takes a directory with content and templates and renders them into a full HTML website.
|
||||
|
||||
@@ -27,7 +36,7 @@ A good rule of thumb is that each piece of content renders in around 1 milliseco
|
||||
|
||||
Hugo is designed to work well for any kind of website including blogs, tumbles, and docs.
|
||||
|
||||
#### Supported Architectures
|
||||
## Supported Architectures
|
||||
|
||||
Currently, we provide pre-built Hugo binaries for Windows, Linux, FreeBSD, NetBSD, DragonFly BSD, OpenBSD, macOS (Darwin), and [Android](https://gist.github.com/bep/a0d8a26cf6b4f8bc992729b8e50b480b) for x64, i386 and ARM architectures.
|
||||
|
||||
@@ -38,7 +47,6 @@ Hugo may also be compiled from source wherever the Go compiler tool chain can ru
|
||||
## Choose How to Install
|
||||
|
||||
If you want to use Hugo as your site generator, simply install the Hugo binaries.
|
||||
The Hugo binaries have no external dependencies.
|
||||
|
||||
To contribute to the Hugo source code or documentation, you should [fork the Hugo GitHub project](https://github.com/gohugoio/hugo#fork-destination-box) and clone it to your local machine.
|
||||
|
||||
@@ -83,7 +91,7 @@ The Hugo documentation now lives in its own repository, see https://github.com/g
|
||||
```bash
|
||||
git clone git@github.com:gohugoio/hugo.git
|
||||
```
|
||||
## Contributing to Hugo
|
||||
## Contributing code to Hugo
|
||||
|
||||
**Note March 16th 2022:** We are currently very constrained on human resources to do code reviews, so we currently require any new Pull Requests to be limited to bug fixes closing an existing issue. Also, we have updated to Go 1.18, but we will currently not accept any generic rewrites, "interface{} to any" replacements and similar.
|
||||
|
||||
@@ -96,131 +104,137 @@ helping to manage issues, etc.
|
||||
|
||||
The Hugo community and maintainers are [very active](https://github.com/gohugoio/hugo/pulse/monthly) and helpful, and the project benefits greatly from this activity.
|
||||
|
||||
### Asking Support Questions
|
||||
## Asking Support Questions
|
||||
|
||||
We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions.
|
||||
Please don't use the GitHub issue tracker to ask questions.
|
||||
|
||||
### Reporting Issues
|
||||
## Reporting Issues
|
||||
|
||||
If you believe you have found a defect in Hugo or its documentation, use
|
||||
the GitHub issue tracker to report the problem to the Hugo maintainers.
|
||||
If you're not sure if it's a bug or not, start by asking in the [discussion forum](https://discourse.gohugo.io).
|
||||
When reporting the issue, please provide the version of Hugo in use (`hugo version`).
|
||||
|
||||
### Submitting Patches
|
||||
|
||||
The Hugo project welcomes all contributors and contributions regardless of skill or experience level.
|
||||
If you are interested in helping with the project, we will help you with your contribution.
|
||||
Hugo is a very active project with many contributions happening daily.
|
||||
|
||||
We want to create the best possible product for our users and the best contribution experience for our developers,
|
||||
we have a set of guidelines which ensure that all contributions are acceptable.
|
||||
The guidelines are not intended as a filter or barrier to participation.
|
||||
If you are unfamiliar with the contribution process, the Hugo team will help you and teach you how to bring your contribution in accordance with the guidelines.
|
||||
|
||||
For a complete guide to contributing code to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
|
||||
|
||||
[Go]: https://golang.org/
|
||||
[Hugo Documentation]: https://gohugo.io/overview/introduction/
|
||||
|
||||
## Dependencies
|
||||
|
||||
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"
|
||||
```
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
+7
-5
@@ -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
|
||||
}
|
||||
|
||||
@@ -278,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
|
||||
@@ -291,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)
|
||||
}()
|
||||
|
||||
+11
-12
@@ -17,8 +17,6 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/releaser"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -29,10 +27,9 @@ var _ cmder = (*releaseCommandeer)(nil)
|
||||
type releaseCommandeer struct {
|
||||
cmd *cobra.Command
|
||||
|
||||
version string
|
||||
|
||||
skipPublish bool
|
||||
try bool
|
||||
step int
|
||||
skipPush bool
|
||||
try bool
|
||||
}
|
||||
|
||||
func createReleaser() cmder {
|
||||
@@ -50,9 +47,9 @@ func createReleaser() cmder {
|
||||
return r.release()
|
||||
}
|
||||
|
||||
r.cmd.PersistentFlags().StringVarP(&r.version, "rel", "r", "", "new release version, i.e. 0.25.1")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.skipPublish, "skip-publish", "", false, "skip all publishing pipes of the release")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "simulate a release, i.e. no changes")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.skipPush, "skip-push", "", false, "skip pushing to remote")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "no changes")
|
||||
r.cmd.PersistentFlags().IntVarP(&r.step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)")
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -65,8 +62,10 @@ func (c *releaseCommandeer) flagsToConfig(cfg config.Provider) {
|
||||
}
|
||||
|
||||
func (r *releaseCommandeer) release() error {
|
||||
if r.version == "" {
|
||||
return errors.New("must set the --rel flag to the relevant version number")
|
||||
rel, err := releaser.New(r.skipPush, r.try, r.step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return releaser.New(r.version, r.skipPublish, r.try).Run()
|
||||
|
||||
return rel.Run()
|
||||
}
|
||||
|
||||
@@ -272,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 {
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/htime"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
)
|
||||
|
||||
@@ -168,6 +170,44 @@ func GetMethodIndexByName(tp reflect.Type, name string) int {
|
||||
return m.Index
|
||||
}
|
||||
|
||||
var (
|
||||
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||
asTimeProviderType = reflect.TypeOf((*htime.AsTimeProvider)(nil)).Elem()
|
||||
)
|
||||
|
||||
// IsTime returns whether tp is a time.Time type or if it can be converted into one
|
||||
// in ToTime.
|
||||
func IsTime(tp reflect.Type) bool {
|
||||
if tp == timeType {
|
||||
return true
|
||||
}
|
||||
|
||||
if tp.Implements(asTimeProviderType) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AsTime returns v as a time.Time if possible.
|
||||
// The given location is only used if the value implements AsTimeProvider (e.g. go-toml local).
|
||||
// A zero Time and false is returned if this isn't possible.
|
||||
// Note that this function does not accept string dates.
|
||||
func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
|
||||
if v.Kind() == reflect.Interface {
|
||||
return AsTime(v.Elem(), loc)
|
||||
}
|
||||
|
||||
if v.Type() == timeType {
|
||||
return v.Interface().(time.Time), true
|
||||
}
|
||||
|
||||
if v.Type().Implements(asTimeProviderType) {
|
||||
return v.Interface().(htime.AsTimeProvider).AsTime(loc), true
|
||||
}
|
||||
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
|
||||
func indirectInterface(v reflect.Value) reflect.Value {
|
||||
if v.Kind() != reflect.Interface {
|
||||
|
||||
@@ -20,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
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ package hugio
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
@@ -53,7 +51,7 @@ func CopyFile(fs afero.Fs, from, to string) error {
|
||||
|
||||
// CopyDir copies a directory.
|
||||
func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool) error {
|
||||
fi, err := os.Stat(from)
|
||||
fi, err := fs.Stat(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,7 +65,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
|
||||
return err
|
||||
}
|
||||
|
||||
entries, _ := ioutil.ReadDir(from)
|
||||
entries, _ := afero.ReadDir(fs, from)
|
||||
for _, entry := range entries {
|
||||
fromFilename := filepath.Join(from, entry.Name())
|
||||
toFilename := filepath.Join(to, entry.Name())
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -153,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"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 100,
|
||||
Minor: 103,
|
||||
PatchLevel: 0,
|
||||
Suffix: "",
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n enableInlineShortcodes = false\n [security.exec]\n allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']\n\n [security.funcs]\n getenv = ['^HUGO_']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']\n\n [security.funcs]\n getenv = ['^HUGO_']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 }}
|
||||
```
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" */>}}
|
||||
|
||||
@@ -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
|
||||
|
||||
+26
-26
@@ -1,49 +1,49 @@
|
||||
---
|
||||
title: "世界上最快的网站构建框架 The world’s 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 markdown’s 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 类似的语法,你也可以使用 Amber、Ace 或三种任意组合。"
|
||||
---
|
||||
|
||||
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 使建设网站的乐趣再现。
|
||||
|
||||
@@ -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) 编程语言所写成,由 bep、spf13 和朋友们共同开发。
|
||||
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)共同开发。
|
||||
|
||||
下面是我们说明文档中最常用和实用的章节:
|
||||
|
||||
@@ -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/
|
||||
@@ -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 搜索提供托管的全文本、数字化、分面以及地理定位搜索。
|
||||
+195
-399
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -3,7 +3,7 @@ publish = "public"
|
||||
command = "hugo --gc --minify"
|
||||
|
||||
[context.production.environment]
|
||||
HUGO_VERSION = "0.99.1"
|
||||
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.99.1"
|
||||
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.99.1"
|
||||
HUGO_VERSION = "0.101.0"
|
||||
|
||||
[context.branch-deploy]
|
||||
command = "hugo --gc --minify -b $DEPLOY_PRIME_URL"
|
||||
|
||||
[context.branch-deploy.environment]
|
||||
HUGO_VERSION = "0.99.1"
|
||||
HUGO_VERSION = "0.101.0"
|
||||
|
||||
[context.next.environment]
|
||||
HUGO_ENABLEGITINFO = "true"
|
||||
|
||||
@@ -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.42
|
||||
github.com/evanw/esbuild v0.15.5
|
||||
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.98.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gobuffalo/flect v0.2.5
|
||||
github.com/gobwas/glob v0.2.3
|
||||
@@ -36,34 +36,34 @@ require (
|
||||
github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690
|
||||
github.com/jdkato/prose v1.2.1
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/kyokomi/emoji/v2 v2.2.9
|
||||
github.com/kyokomi/emoji/v2 v2.2.10
|
||||
github.com/magefile/mage v1.13.0
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-isatty v0.0.16
|
||||
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/rogpeppe/go-internal v1.8.1
|
||||
github.com/pelletier/go-toml/v2 v2.0.4
|
||||
github.com/rogpeppe/go-internal v1.9.0
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
github.com/sanity-io/litter v1.5.5
|
||||
github.com/spf13/afero v1.8.2
|
||||
github.com/spf13/afero v1.9.2
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/cobra v1.5.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.5
|
||||
github.com/tdewolff/parse/v2 v2.5.31
|
||||
github.com/yuin/goldmark v1.4.12
|
||||
go.uber.org/atomic v1.9.0
|
||||
github.com/tdewolff/minify/v2 v2.12.0
|
||||
github.com/tdewolff/parse/v2 v2.6.2
|
||||
github.com/yuin/goldmark v1.4.13
|
||||
go.uber.org/atomic v1.10.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
|
||||
)
|
||||
@@ -91,7 +91,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect
|
||||
github.com/aws/smithy-go v1.8.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.5 // indirect
|
||||
@@ -103,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
|
||||
@@ -114,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-20220811171246-fbc7d0a398ab // 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
|
||||
|
||||
@@ -131,8 +131,9 @@ 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.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=
|
||||
@@ -191,8 +192,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=
|
||||
@@ -206,8 +207,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -236,8 +237,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.42 h1:QnzHQLVCTDbzjrBfB5/MNveeEwIhjgYfAG5GZxjXCVc=
|
||||
github.com/evanw/esbuild v0.14.42/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY=
|
||||
github.com/evanw/esbuild v0.15.5 h1:5nAef2JVECs/dYSdtTQTDlXrM97lOfMRtvMBpOT1L8c=
|
||||
github.com/evanw/esbuild v0.15.5/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk=
|
||||
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=
|
||||
@@ -248,11 +249,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.98.0 h1:lIACvCG9cxmFsEywz+LCoVhcZHFLUy+Nv5QSkb43eAE=
|
||||
github.com/getkin/kin-openapi v0.98.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=
|
||||
@@ -401,6 +401,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=
|
||||
@@ -426,8 +428,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.9 h1:UWYkjplPZ4rMPvLxc+/e12/xTqoRcn55oUySkpZ554g=
|
||||
github.com/kyokomi/emoji/v2 v2.2.9/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.10 h1:1z5eMVcxFifsmEoNpdeq4UahbcicgQ4FEHuzrCVwmiI=
|
||||
github.com/kyokomi/emoji/v2 v2.2.10/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
|
||||
@@ -439,8 +441,8 @@ github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O
|
||||
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
|
||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -459,13 +461,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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pelletier/go-toml/v2 v2.0.4 h1:MHHO+ZUPwPZQ6BmnnT81iQg5cuurp78CRH7rNsguSMk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.4/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -477,8 +478,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
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=
|
||||
@@ -488,12 +489,12 @@ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF
|
||||
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=
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/fsync v0.9.0 h1:f9CEt3DOB2mnHxZaftmEOFWjABEvKM/xpf3cUwJrGOY=
|
||||
github.com/spf13/fsync v0.9.0/go.mod h1:fNtJEfG3HiltN3y4cPOz6MLjos9+2pIEqLIgszqhp/0=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
@@ -502,6 +503,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -509,14 +511,17 @@ 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.5 h1:wfRJ6JMmu87Azx+Lj7NcCTg8D/L/92SNuYF5fv31fjg=
|
||||
github.com/tdewolff/minify/v2 v2.11.5/go.mod h1:ZR6VK7tbgF0XXBVwDHXrbqM4tGYchBpdwhsbKeDmAww=
|
||||
github.com/tdewolff/parse/v2 v2.5.31 h1:PrJN/trWTUYaYxg7jRpKnKXpBK4kBjT9rAFhFAh2Lus=
|
||||
github.com/tdewolff/parse/v2 v2.5.31/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tdewolff/minify/v2 v2.12.0 h1:ZyvMKeciyR3vzJrK/oHyBcSmpttQ/V+ah7qOqTZclaU=
|
||||
github.com/tdewolff/minify/v2 v2.12.0/go.mod h1:8mvf+KglD7XurfvvFZDUYvVURy6bA/r0oTvmakXMnyg=
|
||||
github.com/tdewolff/parse/v2 v2.6.1/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
||||
github.com/tdewolff/parse/v2 v2.6.2 h1:RDExtQ7ThRGSucatK0nri3p+HD/tSreYh1IDtDDAS/I=
|
||||
github.com/tdewolff/parse/v2 v2.6.2/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM=
|
||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -524,8 +529,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
|
||||
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
@@ -538,8 +543,9 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
@@ -643,8 +649,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=
|
||||
@@ -742,8 +748,9 @@ 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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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=
|
||||
@@ -814,8 +821,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=
|
||||
@@ -1013,8 +1020,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=
|
||||
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
project_name: hugo
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GOPROXY=https://proxy.golang.org
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo_unix
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
goos:
|
||||
- freebsd
|
||||
- netbsd
|
||||
- openbsd
|
||||
- dragonfly
|
||||
goarch:
|
||||
- amd64
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo_extended_windows
|
||||
ldflags:
|
||||
- -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
- "-extldflags '-static'"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
- CXX=x86_64-w64-mingw32-g++
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- binary: hugo
|
||||
id: hugo_extended_darwin
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- binary: hugo
|
||||
id: hugo_extended_linux
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
hooks:
|
||||
post: ./goreleaser-hook-post-linux.sh
|
||||
release:
|
||||
draft: true
|
||||
|
||||
archives:
|
||||
-
|
||||
id: "hugo"
|
||||
builds: ['hugo', 'hugo_unix']
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
-
|
||||
id: "hugo_extended"
|
||||
builds: ['hugo_extended_windows', 'hugo_extended_linux', 'hugo_extended_darwin']
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "{{.ProjectName}}_extended_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
nfpms:
|
||||
-
|
||||
id: "hugo"
|
||||
builds: ['hugo']
|
||||
formats:
|
||||
- deb
|
||||
vendor: "gohugo.io"
|
||||
homepage: "https://gohugo.io/"
|
||||
maintainer: "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description: "A Fast and Flexible Static Site Generator built with love in GoLang."
|
||||
license: "Apache-2.0"
|
||||
file_name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
-
|
||||
id: "hugo_extended"
|
||||
builds: ['hugo_extended_linux']
|
||||
formats:
|
||||
- deb
|
||||
vendor: "gohugo.io"
|
||||
homepage: "https://gohugo.io/"
|
||||
maintainer: "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description: "A Fast and Flexible Static Site Generator built with love in GoLang."
|
||||
license: "Apache-2.0"
|
||||
file_name_template: "{{.ProjectName}}_extended_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
+4
-21
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
}
|
||||
|
||||
@@ -163,8 +163,6 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB
|
||||
},
|
||||
}
|
||||
|
||||
ps.shortcodeState = newShortcodeHandler(ps, ps.s)
|
||||
|
||||
if err := ps.mapContent(parentBucket, metaProvider); err != nil {
|
||||
return nil, ps.wrapError(err)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -197,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
|
||||
}
|
||||
@@ -267,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 {
|
||||
@@ -320,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
|
||||
@@ -338,6 +345,8 @@ func (s *IntegrationTestBuilder) initBuilder() {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
return initErr
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) absFilename(filename string) string {
|
||||
|
||||
+12
-12
@@ -639,7 +639,7 @@ func (p *pageState) mapContentForResult(
|
||||
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 …
|
||||
@@ -656,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
|
||||
}
|
||||
@@ -687,7 +687,7 @@ Loop:
|
||||
|
||||
next := iter.Peek()
|
||||
if !next.IsDone() {
|
||||
p.source.posMainContent = next.Pos
|
||||
p.source.posMainContent = next.Pos()
|
||||
}
|
||||
|
||||
if !p.s.shouldBuild(p) {
|
||||
@@ -699,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
|
||||
@@ -712,7 +712,7 @@ Loop:
|
||||
}
|
||||
iter.PeekWalk(f)
|
||||
|
||||
p.source.posSummaryEnd = it.Pos
|
||||
p.source.posSummaryEnd = it.Pos()
|
||||
p.source.posBodyStart = posBody
|
||||
p.source.hasSummaryDivider = true
|
||||
|
||||
@@ -727,13 +727,13 @@ 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)
|
||||
}
|
||||
@@ -754,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)
|
||||
@@ -762,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,8 +33,6 @@ type pageContent struct {
|
||||
|
||||
cmap *pageContentMap
|
||||
|
||||
shortcodeState *shortcodeHandler
|
||||
|
||||
source rawPageContent
|
||||
}
|
||||
|
||||
@@ -47,7 +45,7 @@ func (p pageContent) contentToRender(parsed pageparser.Result, pm *pageContentMa
|
||||
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:
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/text"
|
||||
"github.com/gohugoio/hugo/common/types/hstring"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
@@ -37,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"
|
||||
@@ -334,7 +333,6 @@ func (p *pageContentOutput) WordCount() int {
|
||||
}
|
||||
|
||||
func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
|
||||
defer herrors.Recover()
|
||||
if len(args) < 1 || len(args) > 2 {
|
||||
return "", errors.New("want 1 or 2 arguments")
|
||||
}
|
||||
|
||||
+12
-3
@@ -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"),
|
||||
|
||||
@@ -158,5 +158,35 @@ Page Type: *hugolib.pageForShortcode`,
|
||||
)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
`)
|
||||
|
||||
}
|
||||
|
||||
+13
-13
@@ -509,7 +509,7 @@ 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")
|
||||
}
|
||||
@@ -520,7 +520,7 @@ func (s *shortcodeHandler) extractShortcode(ordinal, level int, pt *pageparser.I
|
||||
pt.Backup()
|
||||
item := pt.Next()
|
||||
if item.IsIndentation() {
|
||||
sc.indentation = string(item.Val)
|
||||
sc.indentation = item.ValStr(source)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ func (s *shortcodeHandler) extractShortcode(ordinal, level int, pt *pageparser.I
|
||||
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:
|
||||
@@ -550,7 +550,7 @@ 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.addName(nested.name)
|
||||
@@ -589,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() {
|
||||
@@ -602,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 {
|
||||
@@ -614,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)
|
||||
@@ -625,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() {
|
||||
@@ -634,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
|
||||
}
|
||||
@@ -647,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
|
||||
|
||||
@@ -112,7 +112,7 @@ title: "Shortcodes Galore!"
|
||||
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)
|
||||
})
|
||||
@@ -763,7 +763,7 @@ title: "Hugo Rocks!"
|
||||
)
|
||||
}
|
||||
|
||||
func TestShortcodeTypedParams(t *testing.T) {
|
||||
func TestShortcodeParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
@@ -778,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(
|
||||
@@ -796,6 +797,7 @@ 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 "world". (string)",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -853,7 +853,7 @@ func (th testHelper) assertFileContentRegexp(filename string, matches ...string)
|
||||
r := regexp.MustCompile(match)
|
||||
matches := r.MatchString(content)
|
||||
if !matches {
|
||||
fmt.Println(match+":\n", content)
|
||||
fmt.Println("Expected to match regexp:\n"+match+"\nGot:\n", content)
|
||||
}
|
||||
th.Assert(matches, qt.Equals, true)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
HUGO_RELEASE_NAME=New release setup
|
||||
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.102.0
|
||||
HUGORELEASER_COMMITISH=0ff4a9326fd8ac1deb7245461bc4a56d94d0c40b
|
||||
@@ -0,0 +1,232 @@
|
||||
project = "hugo"
|
||||
|
||||
[go_settings]
|
||||
go_proxy = "https://proxy.golang.org"
|
||||
go_exe = "go"
|
||||
|
||||
[build_settings]
|
||||
binary = "hugo"
|
||||
flags = ["-buildmode", "exe"]
|
||||
env = ["CGO_ENABLED=0"]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"
|
||||
|
||||
[archive_settings]
|
||||
name_template = "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
extra_files = [
|
||||
{ source_path = "README.md", target_path = "README.md" },
|
||||
{ source_path = "LICENSE", target_path = "LICENSE" },
|
||||
]
|
||||
[archive_settings.type]
|
||||
format = "tar.gz"
|
||||
extension = ".tar.gz"
|
||||
[archive_settings.replacements]
|
||||
amd64 = "64bit"
|
||||
386 = "32bit"
|
||||
arm = "ARM"
|
||||
arm64 = "ARM64"
|
||||
darwin = "macOS"
|
||||
linux = "Linux"
|
||||
windows = "Windows"
|
||||
openbsd = "OpenBSD"
|
||||
netbsd = "NetBSD"
|
||||
freebsd = "FreeBSD"
|
||||
dragonfly = "DragonFlyBSD"
|
||||
|
||||
[release_settings]
|
||||
name = "${HUGO_RELEASE_NAME}"
|
||||
type = "github"
|
||||
repository = "hugo"
|
||||
repository_owner = "gohugoio"
|
||||
draft = true
|
||||
prerelease = false
|
||||
|
||||
[release_settings.release_notes_settings]
|
||||
# Use Hugoreleaser's autogenerated release notes.
|
||||
generate = true
|
||||
|
||||
# Collapse relases with < 10 changes below one title.
|
||||
short_threshold = 10
|
||||
short_title = "What's Changed"
|
||||
|
||||
groups = [
|
||||
# Group the changes in the release notes by title.
|
||||
# You need at least one.
|
||||
# The groups will be tested in order until a match is found.
|
||||
# The titles will so be listed in the given order in the release note.
|
||||
# Any match with ignore=true title will be dropped.
|
||||
{ regexp = "Merge commit|Squashed", ignore = true },
|
||||
{ title = "Bug fixes", regexp = "fix", ordinal = 10 },
|
||||
{ title = "Dependency Updates", regexp = "deps", ordinal = 30 },
|
||||
{ title = "Build Setup", regexp = "(snap|release|update to)", ordinal = 40 },
|
||||
{ title = "Documentation", regexp = "(doc|readme)", ordinal = 40 },
|
||||
{ title = "Improvements", regexp = ".*", ordinal = 20 },
|
||||
]
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/regular"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm"
|
||||
[builds.os.archs.build_settings]
|
||||
env = ["CGO_ENABLED=0", "GOARM=7"]
|
||||
|
||||
# Unix BSD variants
|
||||
[[builds.os]]
|
||||
goos = "dragonfly"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "freebsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "netbsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "openbsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
env = ["CGO_ENABLED=1"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[builds.os.build_settings]
|
||||
env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"]
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container2/linux/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[builds.os.build_settings]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=aarch64-linux-gnu-gcc",
|
||||
"CXX=aarch64-linux-gnu-g++",
|
||||
]
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/regular"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=x86_64-w64-mingw32-gcc",
|
||||
"CXX=x86_64-w64-mingw32-g++",
|
||||
]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/regular/**"]
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
# Only extended builds in container2.
|
||||
paths = ["builds/container2/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/regular/**"]
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/regular/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
[archives.archive_settings.type]
|
||||
format = "_plugin"
|
||||
extension = ".deb"
|
||||
[archives.archive_settings.plugin]
|
||||
id = "deb"
|
||||
type = "gorun"
|
||||
command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@v0.5.0"
|
||||
[archives.archive_settings.custom_settings]
|
||||
vendor = "gohugo.io"
|
||||
homepage = "https://github.com/gohugoio/hugoreleaser"
|
||||
maintainer = "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description = "Build, archive and release Go programs."
|
||||
license = "Apache-2.0"
|
||||
[[archives]]
|
||||
paths = ["builds/**/extended/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "_plugin"
|
||||
extension = ".deb"
|
||||
[archives.archive_settings.plugin]
|
||||
id = "deb"
|
||||
type = "gorun"
|
||||
command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest"
|
||||
[archives.archive_settings.custom_settings]
|
||||
vendor = "gohugo.io"
|
||||
homepage = "https://github.com/gohugoio/hugoreleaser"
|
||||
maintainer = "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description = "Build, archive and release Go programs."
|
||||
license = "Apache-2.0"
|
||||
|
||||
[[releases]]
|
||||
paths = ["archives/**"]
|
||||
path = "r1"
|
||||
|
||||
# The above should allow the following build commands:
|
||||
# hugoreleaser build -paths "builds/container1/**"
|
||||
# hugoreleaser build -paths "builds/container2/**"
|
||||
# hugoreleaser archive
|
||||
# hugoreleaser release
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -265,6 +265,39 @@ Position: {{ .Position | safeHTML }}
|
||||
b.AssertFileContent("public/p1/index.html", filepath.FromSlash("Position: \"/content/p1.md:7:1\""))
|
||||
}
|
||||
|
||||
// Issue 10118
|
||||
func TestAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
|
||||
## Issue 10118
|
||||
|
||||
§§§ {foo="bar"}
|
||||
Hello, World!
|
||||
§§§
|
||||
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
-- layouts/_default/_markup/render-codeblock.html --
|
||||
Attributes: {{ .Attributes }}|Type: {{ .Type }}|
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "<h2 id=\"issue-10118\">Issue 10118</h2>\nAttributes: map[foo:bar]|Type: |")
|
||||
}
|
||||
|
||||
// Issue 9571
|
||||
func TestAttributesChroma(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -16,9 +16,10 @@ package codeblocks
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
htext "github.com/gohugoio/hugo/common/text"
|
||||
"github.com/gohugoio/hugo/markup/converter/hooks"
|
||||
@@ -69,7 +70,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
}
|
||||
|
||||
n := node.(*codeBlock)
|
||||
lang := string(n.b.Language(src))
|
||||
lang := getLang(n.b, src)
|
||||
renderer := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang)
|
||||
if renderer == nil {
|
||||
return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang)
|
||||
@@ -174,6 +175,12 @@ func (c *codeBlockContext) Position() htext.Position {
|
||||
return c.pos
|
||||
}
|
||||
|
||||
func getLang(node *ast.FencedCodeBlock, src []byte) string {
|
||||
langWithAttributes := string(node.Language(src))
|
||||
lang, _, _ := strings.Cut(langWithAttributes, "{")
|
||||
return lang
|
||||
}
|
||||
|
||||
func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute {
|
||||
if node.Attributes() != nil {
|
||||
return node.Attributes()
|
||||
@@ -188,7 +195,7 @@ func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute {
|
||||
}
|
||||
}
|
||||
|
||||
if attrStartIdx > 0 {
|
||||
if attrStartIdx != -1 {
|
||||
n := ast.NewTextBlock() // dummy node for storing attributes
|
||||
attrStr := infostr[attrStartIdx:]
|
||||
if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -72,6 +72,9 @@ type Config struct {
|
||||
// A space separated list of line numbers, e.g. “3-8 10-20”.
|
||||
Hl_Lines string
|
||||
|
||||
// If set, the markup will not be wrapped in any container.
|
||||
Hl_inline bool
|
||||
|
||||
// A parsed and ready to use list of line ranges.
|
||||
HL_lines_parsed [][2]int `json:"-"`
|
||||
|
||||
@@ -93,6 +96,7 @@ func (cfg Config) ToHTMLOptions() []html.Option {
|
||||
html.LineNumbersInTable(cfg.LineNumbersInTable),
|
||||
html.WithClasses(!cfg.NoClasses),
|
||||
html.LinkableLineNumbers(cfg.AnchorLineNos, lineAnchors),
|
||||
html.InlineCode(cfg.Hl_inline),
|
||||
}
|
||||
|
||||
if cfg.Hl_Lines != "" || cfg.HL_lines_parsed != nil {
|
||||
|
||||
@@ -20,10 +20,10 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma"
|
||||
"github.com/alecthomas/chroma/formatters/html"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
"github.com/gohugoio/hugo/common/text"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
@@ -88,6 +88,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
|
||||
var b strings.Builder
|
||||
|
||||
attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice()
|
||||
|
||||
options := ctx.Options()
|
||||
|
||||
if err := applyOptionsFromMap(options, &cfg); err != nil {
|
||||
@@ -108,8 +109,13 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
|
||||
return HightlightResult{}, err
|
||||
}
|
||||
|
||||
highlighted := b.String()
|
||||
if high == 0 {
|
||||
high = len(highlighted)
|
||||
}
|
||||
|
||||
return HightlightResult{
|
||||
highlighted: template.HTML(b.String()),
|
||||
highlighted: template.HTML(highlighted),
|
||||
innerLow: low,
|
||||
innerHigh: high,
|
||||
}, nil
|
||||
@@ -117,6 +123,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a
|
||||
|
||||
func (h chromaHighlighter) RenderCodeblock(w hugio.FlexiWriter, ctx hooks.CodeblockContext) error {
|
||||
cfg := h.cfg
|
||||
|
||||
attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice()
|
||||
|
||||
if err := applyOptionsFromMap(ctx.Options(), &cfg); err != nil {
|
||||
@@ -158,8 +165,6 @@ func (h HightlightResult) Inner() template.HTML {
|
||||
}
|
||||
|
||||
func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.Attribute, cfg Config) (int, int, error) {
|
||||
var low, high int
|
||||
|
||||
var lexer chroma.Lexer
|
||||
if lang != "" {
|
||||
lexer = lexers.Get(lang)
|
||||
@@ -176,11 +181,15 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
|
||||
w := &byteCountFlexiWriter{delegate: fw}
|
||||
|
||||
if lexer == nil {
|
||||
wrapper := getPreWrapper(lang, w)
|
||||
fmt.Fprint(w, wrapper.Start(true, ""))
|
||||
fmt.Fprint(w, gohtml.EscapeString(code))
|
||||
fmt.Fprint(w, wrapper.End(true))
|
||||
return low, high, nil
|
||||
if cfg.Hl_inline {
|
||||
fmt.Fprint(w, fmt.Sprintf("<code%s>%s</code>", inlineCodeAttrs(lang), gohtml.EscapeString(code)))
|
||||
} else {
|
||||
preWrapper := getPreWrapper(lang, w)
|
||||
fmt.Fprint(w, preWrapper.Start(true, ""))
|
||||
fmt.Fprint(w, gohtml.EscapeString(code))
|
||||
fmt.Fprint(w, preWrapper.End(true))
|
||||
}
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
style := styles.Get(cfg.Style)
|
||||
@@ -194,20 +203,51 @@ func highlight(fw hugio.FlexiWriter, code, lang string, attributes []attributes.
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if !cfg.Hl_inline {
|
||||
writeDivStart(w, attributes)
|
||||
}
|
||||
|
||||
options := cfg.ToHTMLOptions()
|
||||
preWrapper := getPreWrapper(lang, w)
|
||||
options = append(options, html.WithPreWrapper(preWrapper))
|
||||
var wrapper html.PreWrapper
|
||||
|
||||
if cfg.Hl_inline {
|
||||
wrapper = startEnd{
|
||||
start: func(code bool, styleAttr string) string {
|
||||
if code {
|
||||
return fmt.Sprintf(`<code%s>`, inlineCodeAttrs(lang))
|
||||
}
|
||||
return ``
|
||||
},
|
||||
end: func(code bool) string {
|
||||
if code {
|
||||
return `</code>`
|
||||
}
|
||||
|
||||
return ``
|
||||
},
|
||||
}
|
||||
|
||||
} else {
|
||||
wrapper = getPreWrapper(lang, w)
|
||||
}
|
||||
|
||||
options = append(options, html.WithPreWrapper(wrapper))
|
||||
|
||||
formatter := html.New(options...)
|
||||
|
||||
writeDivStart(w, attributes)
|
||||
|
||||
if err := formatter.Format(w, style, iterator); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
writeDivEnd(w)
|
||||
|
||||
return preWrapper.low, preWrapper.high, nil
|
||||
if !cfg.Hl_inline {
|
||||
writeDivEnd(w)
|
||||
}
|
||||
|
||||
if p, ok := wrapper.(*preWrapper); ok {
|
||||
return p.low, p.high, nil
|
||||
}
|
||||
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
func getPreWrapper(language string, writeCounter *byteCountFlexiWriter) *preWrapper {
|
||||
@@ -232,6 +272,12 @@ func (p *preWrapper) Start(code bool, styleAttr string) string {
|
||||
return w.String()
|
||||
}
|
||||
|
||||
func inlineCodeAttrs(lang string) string {
|
||||
if lang == "" {
|
||||
}
|
||||
return fmt.Sprintf(` class="code-inline language-%s"`, lang)
|
||||
}
|
||||
|
||||
func WritePreStart(w io.Writer, language, styleAttr string) {
|
||||
fmt.Fprintf(w, `<pre tabindex="0"%s>`, styleAttr)
|
||||
fmt.Fprint(w, "<code")
|
||||
@@ -249,6 +295,19 @@ func (p *preWrapper) End(code bool) string {
|
||||
return preEnd
|
||||
}
|
||||
|
||||
type startEnd struct {
|
||||
start func(code bool, styleAttr string) string
|
||||
end func(code bool) string
|
||||
}
|
||||
|
||||
func (s startEnd) Start(code bool, styleAttr string) string {
|
||||
return s.start(code, styleAttr)
|
||||
}
|
||||
|
||||
func (s startEnd) End(code bool) string {
|
||||
return s.end(code)
|
||||
}
|
||||
|
||||
func WritePreEnd(w io.Writer) {
|
||||
fmt.Fprint(w, preEnd)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2022 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// 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 highlight_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestHighlightInline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
[markup]
|
||||
[markup.highlight]
|
||||
codeFences = true
|
||||
noClasses = false
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
|
||||
## Inline in Shortcode
|
||||
|
||||
Inline:{{< highlight emacs "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End.
|
||||
Inline Unknown:{{< highlight foo "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End.
|
||||
|
||||
## Inline in code block
|
||||
|
||||
Not sure if this makes sense, but add a test for it:
|
||||
|
||||
§§§bash {hl_inline=true}
|
||||
(message "highlight me")
|
||||
§§§
|
||||
|
||||
## HighlightCodeBlock in hook
|
||||
|
||||
§§§html
|
||||
(message "highlight me 2")
|
||||
§§§
|
||||
|
||||
## Unknown lexer
|
||||
|
||||
§§§foo {hl_inline=true}
|
||||
(message "highlight me 3")
|
||||
§§§
|
||||
|
||||
|
||||
-- layouts/_default/_markup/render-codeblock-html.html --
|
||||
{{ $opts := dict "hl_inline" true }}
|
||||
{{ $result := transform.HighlightCodeBlock . $opts }}
|
||||
HighlightCodeBlock: Wrapped:{{ $result.Wrapped }}|Inner:{{ $result.Inner }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
NeedsOsFS: false,
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"Inline:<code class=\"code-inline language-emacs\"><span class=\"p\">(</span><span class=\"nf\">message</span> <span class=\"s\">"this highlight shortcode"</span><span class=\"p\">)</span></code>:End.",
|
||||
"Inline Unknown:<code class=\"code-inline language-foo\">(message "this highlight shortcode")</code>:End.",
|
||||
"Not sure if this makes sense, but add a test for it:</p>\n<code class=\"code-inline language-bash\"><span class=\"o\">(</span>message <span class=\"s2\">"highlight me"</span><span class=\"o\">)</span>\n</code>",
|
||||
"HighlightCodeBlock: Wrapped:<code class=\"code-inline language-html\">(message "highlight me 2")</code>|Inner:<code class=\"code-inline language-html\">(message "highlight me 2")</code>",
|
||||
"<code class=\"code-inline language-foo\">(message "highlight me 3")\n</code>",
|
||||
)
|
||||
}
|
||||
@@ -30,6 +30,7 @@ var chromaHightlightProcessingAttributes = map[string]bool{
|
||||
"anchorLineNos": true,
|
||||
"guessSyntax": true,
|
||||
"hl_Lines": true,
|
||||
"hl_inline": true,
|
||||
"lineAnchors": true,
|
||||
"lineNos": true,
|
||||
"lineNoStart": true,
|
||||
|
||||
+13
-1
@@ -14,6 +14,7 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/highlight"
|
||||
@@ -43,6 +44,8 @@ func NewConverterProvider(cfg converter.ProviderConfig) (ConverterProvider, erro
|
||||
}
|
||||
|
||||
cfg.MarkupConfig = markupConfig
|
||||
defaultHandler := cfg.MarkupConfig.DefaultMarkdownHandler
|
||||
var defaultFound bool
|
||||
|
||||
add := func(p converter.ProviderProvider, aliases ...string) error {
|
||||
c, err := p.New(cfg)
|
||||
@@ -54,8 +57,9 @@ func NewConverterProvider(cfg converter.ProviderConfig) (ConverterProvider, erro
|
||||
|
||||
aliases = append(aliases, name)
|
||||
|
||||
if strings.EqualFold(name, cfg.MarkupConfig.DefaultMarkdownHandler) {
|
||||
if strings.EqualFold(name, defaultHandler) {
|
||||
aliases = append(aliases, "markdown")
|
||||
defaultFound = true
|
||||
}
|
||||
|
||||
addConverter(converters, c, aliases...)
|
||||
@@ -78,6 +82,14 @@ func NewConverterProvider(cfg converter.ProviderConfig) (ConverterProvider, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !defaultFound {
|
||||
msg := "markup: Configured defaultMarkdownHandler %q not found."
|
||||
if defaultHandler == "blackfriday" {
|
||||
msg += " Did you mean to use goldmark? Blackfriday was removed in Hugo v0.100.0."
|
||||
}
|
||||
return nil, fmt.Errorf(msg, defaultHandler)
|
||||
}
|
||||
|
||||
return &converterRegistry{
|
||||
config: cfg,
|
||||
converters: converters,
|
||||
|
||||
+55
-16
@@ -22,21 +22,59 @@ import (
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
type lowHigh struct {
|
||||
Low int
|
||||
High int
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Type ItemType
|
||||
Pos int
|
||||
Val []byte
|
||||
Type ItemType
|
||||
Err error
|
||||
|
||||
// The common case is a single segment.
|
||||
low int
|
||||
high int
|
||||
|
||||
// This is the uncommon case.
|
||||
segments []lowHigh
|
||||
|
||||
// Used for validation.
|
||||
firstByte byte
|
||||
|
||||
isString bool
|
||||
}
|
||||
|
||||
type Items []Item
|
||||
|
||||
func (i Item) ValStr() string {
|
||||
return string(i.Val)
|
||||
func (i Item) Pos() int {
|
||||
if len(i.segments) > 0 {
|
||||
return i.segments[0].Low
|
||||
}
|
||||
return i.low
|
||||
}
|
||||
|
||||
func (i Item) ValTyped() any {
|
||||
str := i.ValStr()
|
||||
func (i Item) Val(source []byte) []byte {
|
||||
if len(i.segments) == 0 {
|
||||
return source[i.low:i.high]
|
||||
}
|
||||
|
||||
if len(i.segments) == 1 {
|
||||
return source[i.segments[0].Low:i.segments[0].High]
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
for _, s := range i.segments {
|
||||
b.Write(source[s.Low:s.High])
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func (i Item) ValStr(source []byte) string {
|
||||
return string(i.Val(source))
|
||||
}
|
||||
|
||||
func (i Item) ValTyped(source []byte) any {
|
||||
str := i.ValStr(source)
|
||||
if i.isString {
|
||||
// A quoted value that is a string even if it looks like a number etc.
|
||||
return str
|
||||
@@ -73,8 +111,8 @@ func (i Item) IsIndentation() bool {
|
||||
return i.Type == tIndentation
|
||||
}
|
||||
|
||||
func (i Item) IsNonWhitespace() bool {
|
||||
return len(bytes.TrimSpace(i.Val)) > 0
|
||||
func (i Item) IsNonWhitespace(source []byte) bool {
|
||||
return len(bytes.TrimSpace(i.Val(source))) > 0
|
||||
}
|
||||
|
||||
func (i Item) IsShortcodeName() bool {
|
||||
@@ -125,20 +163,21 @@ func (i Item) IsError() bool {
|
||||
return i.Type == tError
|
||||
}
|
||||
|
||||
func (i Item) String() string {
|
||||
func (i Item) ToString(source []byte) string {
|
||||
val := i.Val(source)
|
||||
switch {
|
||||
case i.Type == tEOF:
|
||||
return "EOF"
|
||||
case i.Type == tError:
|
||||
return string(i.Val)
|
||||
return string(val)
|
||||
case i.Type == tIndentation:
|
||||
return fmt.Sprintf("%s:[%s]", i.Type, util.VisualizeSpaces(i.Val))
|
||||
return fmt.Sprintf("%s:[%s]", i.Type, util.VisualizeSpaces(val))
|
||||
case i.Type > tKeywordMarker:
|
||||
return fmt.Sprintf("<%s>", i.Val)
|
||||
case len(i.Val) > 50:
|
||||
return fmt.Sprintf("%v:%.20q...", i.Type, i.Val)
|
||||
return fmt.Sprintf("<%s>", val)
|
||||
case len(val) > 50:
|
||||
return fmt.Sprintf("%v:%.20q...", i.Type, val)
|
||||
}
|
||||
return fmt.Sprintf("%v:[%s]", i.Type, i.Val)
|
||||
return fmt.Sprintf("%v:[%s]", i.Type, val)
|
||||
}
|
||||
|
||||
type ItemType int
|
||||
|
||||
@@ -22,13 +22,22 @@ import (
|
||||
func TestItemValTyped(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(Item{Val: []byte("3.14")}.ValTyped(), qt.Equals, float64(3.14))
|
||||
c.Assert(Item{Val: []byte(".14")}.ValTyped(), qt.Equals, float64(.14))
|
||||
c.Assert(Item{Val: []byte("314")}.ValTyped(), qt.Equals, 314)
|
||||
c.Assert(Item{Val: []byte("314x")}.ValTyped(), qt.Equals, "314x")
|
||||
c.Assert(Item{Val: []byte("314 ")}.ValTyped(), qt.Equals, "314 ")
|
||||
c.Assert(Item{Val: []byte("314"), isString: true}.ValTyped(), qt.Equals, "314")
|
||||
c.Assert(Item{Val: []byte("true")}.ValTyped(), qt.Equals, true)
|
||||
c.Assert(Item{Val: []byte("false")}.ValTyped(), qt.Equals, false)
|
||||
c.Assert(Item{Val: []byte("trues")}.ValTyped(), qt.Equals, "trues")
|
||||
source := []byte("3.14")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, float64(3.14))
|
||||
source = []byte(".14")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, float64(0.14))
|
||||
source = []byte("314")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, 314)
|
||||
source = []byte("314")
|
||||
c.Assert(Item{low: 0, high: len(source), isString: true}.ValTyped(source), qt.Equals, "314")
|
||||
source = []byte("314x")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, "314x")
|
||||
source = []byte("314 ")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, "314 ")
|
||||
source = []byte("true")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, true)
|
||||
source = []byte("false")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, false)
|
||||
source = []byte("trued")
|
||||
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ type pageLexer struct {
|
||||
|
||||
// Implement the Result interface
|
||||
func (l *pageLexer) Iterator() *Iterator {
|
||||
return l.newIterator()
|
||||
return NewIterator(l.items)
|
||||
}
|
||||
|
||||
func (l *pageLexer) Input() []byte {
|
||||
@@ -85,10 +85,6 @@ func newPageLexer(input []byte, stateStart stateFunc, cfg Config) *pageLexer {
|
||||
return lexer
|
||||
}
|
||||
|
||||
func (l *pageLexer) newIterator() *Iterator {
|
||||
return &Iterator{l: l, lastPos: -1}
|
||||
}
|
||||
|
||||
// main loop
|
||||
func (l *pageLexer) run() *pageLexer {
|
||||
for l.state = l.stateStart; l.state != nil; {
|
||||
@@ -136,6 +132,13 @@ func (l *pageLexer) backup() {
|
||||
l.pos -= l.width
|
||||
}
|
||||
|
||||
func (l *pageLexer) append(item Item) {
|
||||
if item.Pos() < len(l.input) {
|
||||
item.firstByte = l.input[item.Pos()]
|
||||
}
|
||||
l.items = append(l.items, item)
|
||||
}
|
||||
|
||||
// sends an item back to the client.
|
||||
func (l *pageLexer) emit(t ItemType) {
|
||||
defer func() {
|
||||
@@ -151,11 +154,11 @@ func (l *pageLexer) emit(t ItemType) {
|
||||
break
|
||||
}
|
||||
if i == l.start && b != '\n' {
|
||||
l.items = append(l.items, Item{tIndentation, l.start, l.input[l.start:l.pos], false})
|
||||
l.append(Item{Type: tIndentation, low: l.start, high: l.pos})
|
||||
return
|
||||
} else if b == '\n' && i < l.pos-1 {
|
||||
l.items = append(l.items, Item{t, l.start, l.input[l.start : i+1], false})
|
||||
l.items = append(l.items, Item{tIndentation, i + 1, l.input[i+1 : l.pos], false})
|
||||
l.append(Item{Type: t, low: l.start, high: i + 1})
|
||||
l.append(Item{Type: tIndentation, low: i + 1, high: l.pos})
|
||||
return
|
||||
} else if b == '\n' && i == l.pos-1 {
|
||||
break
|
||||
@@ -164,13 +167,13 @@ func (l *pageLexer) emit(t ItemType) {
|
||||
}
|
||||
}
|
||||
|
||||
l.items = append(l.items, Item{t, l.start, l.input[l.start:l.pos], false})
|
||||
l.append(Item{Type: t, low: l.start, high: l.pos})
|
||||
|
||||
}
|
||||
|
||||
// sends a string item back to the client.
|
||||
func (l *pageLexer) emitString(t ItemType) {
|
||||
l.items = append(l.items, Item{t, l.start, l.input[l.start:l.pos], true})
|
||||
l.append(Item{Type: t, low: l.start, high: l.pos, isString: true})
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
@@ -180,14 +183,33 @@ func (l *pageLexer) isEOF() bool {
|
||||
|
||||
// special case, do not send '\\' back to client
|
||||
func (l *pageLexer) ignoreEscapesAndEmit(t ItemType, isString bool) {
|
||||
val := bytes.Map(func(r rune) rune {
|
||||
i := l.start
|
||||
k := i
|
||||
|
||||
var segments []lowHigh
|
||||
|
||||
for i < l.pos {
|
||||
r, w := utf8.DecodeRune(l.input[i:l.pos])
|
||||
if r == '\\' {
|
||||
return -1
|
||||
if i > k {
|
||||
segments = append(segments, lowHigh{k, i})
|
||||
}
|
||||
l.append(Item{Type: TypeIgnore, low: i, high: i + w})
|
||||
k = i + w
|
||||
}
|
||||
return r
|
||||
}, l.input[l.start:l.pos])
|
||||
l.items = append(l.items, Item{t, l.start, val, isString})
|
||||
i += w
|
||||
}
|
||||
|
||||
if k < l.pos {
|
||||
segments = append(segments, lowHigh{k, l.pos})
|
||||
}
|
||||
|
||||
if len(segments) > 0 {
|
||||
l.append(Item{Type: t, segments: segments})
|
||||
}
|
||||
|
||||
l.start = l.pos
|
||||
|
||||
}
|
||||
|
||||
// gets the current value (for debugging and error handling)
|
||||
@@ -204,7 +226,7 @@ var lf = []byte("\n")
|
||||
|
||||
// nil terminates the parser
|
||||
func (l *pageLexer) errorf(format string, args ...any) stateFunc {
|
||||
l.items = append(l.items, Item{tError, l.start, []byte(fmt.Sprintf(format, args...)), true})
|
||||
l.append(Item{Type: tError, Err: fmt.Errorf(format, args...)})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ package pageparser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -33,9 +34,6 @@ type Result interface {
|
||||
var _ Result = (*pageLexer)(nil)
|
||||
|
||||
// Parse parses the page in the given reader according to the given Config.
|
||||
// TODO(bep) now that we have improved the "lazy order" init, it *may* be
|
||||
// some potential saving in doing a buffered approach where the first pass does
|
||||
// the frontmatter only.
|
||||
func Parse(r io.Reader, cfg Config) (Result, error) {
|
||||
return parseSection(r, cfg, lexIntroSection)
|
||||
}
|
||||
@@ -63,12 +61,12 @@ func ParseFrontMatterAndContent(r io.Reader) (ContentFrontMatter, error) {
|
||||
walkFn := func(item Item) bool {
|
||||
if frontMatterSource != nil {
|
||||
// The rest is content.
|
||||
cf.Content = psr.Input()[item.Pos:]
|
||||
cf.Content = psr.Input()[item.low:]
|
||||
// Done
|
||||
return false
|
||||
} else if item.IsFrontMatter() {
|
||||
cf.FrontMatterFormat = FormatFromFrontMatterType(item.Type)
|
||||
frontMatterSource = item.Val
|
||||
frontMatterSource = item.Val(psr.Input())
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -113,10 +111,15 @@ func parseBytes(b []byte, cfg Config, start stateFunc) (Result, error) {
|
||||
return lexer, nil
|
||||
}
|
||||
|
||||
// NewIterator creates a new Iterator.
|
||||
func NewIterator(items Items) *Iterator {
|
||||
return &Iterator{items: items, lastPos: -1}
|
||||
}
|
||||
|
||||
// An Iterator has methods to iterate a parsed page with support going back
|
||||
// if needed.
|
||||
type Iterator struct {
|
||||
l *pageLexer
|
||||
items Items
|
||||
lastPos int // position of the last item returned by nextItem
|
||||
}
|
||||
|
||||
@@ -126,19 +129,14 @@ func (t *Iterator) Next() Item {
|
||||
return t.Current()
|
||||
}
|
||||
|
||||
// Input returns the input source.
|
||||
func (t *Iterator) Input() []byte {
|
||||
return t.l.Input()
|
||||
}
|
||||
|
||||
var errIndexOutOfBounds = Item{tError, 0, []byte("no more tokens"), true}
|
||||
var errIndexOutOfBounds = Item{Type: tError, Err: errors.New("no more tokens")}
|
||||
|
||||
// Current will repeatably return the current item.
|
||||
func (t *Iterator) Current() Item {
|
||||
if t.lastPos >= len(t.l.items) {
|
||||
if t.lastPos >= len(t.items) {
|
||||
return errIndexOutOfBounds
|
||||
}
|
||||
return t.l.items[t.lastPos]
|
||||
return t.items[t.lastPos]
|
||||
}
|
||||
|
||||
// backs up one token.
|
||||
@@ -163,14 +161,14 @@ func (t *Iterator) IsValueNext() bool {
|
||||
// look at, but do not consume, the next item
|
||||
// repeated, sequential calls will return the same item
|
||||
func (t *Iterator) Peek() Item {
|
||||
return t.l.items[t.lastPos+1]
|
||||
return t.items[t.lastPos+1]
|
||||
}
|
||||
|
||||
// PeekWalk will feed the next items in the iterator to walkFn
|
||||
// until it returns false.
|
||||
func (t *Iterator) PeekWalk(walkFn func(item Item) bool) {
|
||||
for i := t.lastPos + 1; i < len(t.l.items); i++ {
|
||||
item := t.l.items[i]
|
||||
for i := t.lastPos + 1; i < len(t.items); i++ {
|
||||
item := t.items[i]
|
||||
if !walkFn(item) {
|
||||
break
|
||||
}
|
||||
@@ -190,6 +188,49 @@ func (t *Iterator) Consume(cnt int) {
|
||||
}
|
||||
|
||||
// LineNumber returns the current line number. Used for logging.
|
||||
func (t *Iterator) LineNumber() int {
|
||||
return bytes.Count(t.l.input[:t.Current().Pos], lf) + 1
|
||||
func (t *Iterator) LineNumber(source []byte) int {
|
||||
return bytes.Count(source[:t.Current().low], lf) + 1
|
||||
}
|
||||
|
||||
// IsProbablySourceOfItems returns true if the given source looks like original
|
||||
// source of the items.
|
||||
// There may be some false positives, but that is highly unlikely and good enough
|
||||
// for the planned purpose.
|
||||
// It will also return false if the last item is not EOF (error situations) and
|
||||
// true if both source and items are empty.
|
||||
func IsProbablySourceOfItems(source []byte, items Items) bool {
|
||||
if len(source) == 0 && len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
last := items[len(items)-1]
|
||||
if last.Type != tEOF {
|
||||
return false
|
||||
}
|
||||
|
||||
if last.Pos() != len(source) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.Type == tError {
|
||||
return false
|
||||
}
|
||||
if item.Type == tEOF {
|
||||
return true
|
||||
}
|
||||
|
||||
if item.Pos() >= len(source) {
|
||||
return false
|
||||
}
|
||||
|
||||
if item.firstByte != source[item.Pos()] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -15,19 +15,25 @@ package pageparser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
type lexerTest struct {
|
||||
name string
|
||||
input string
|
||||
items []Item
|
||||
items []typeText
|
||||
}
|
||||
|
||||
func nti(tp ItemType, val string) Item {
|
||||
return Item{tp, 0, []byte(val), false}
|
||||
type typeText struct {
|
||||
typ ItemType
|
||||
text string
|
||||
}
|
||||
|
||||
func nti(tp ItemType, val string) typeText {
|
||||
return typeText{typ: tp, text: val}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -52,48 +58,79 @@ var crLfReplacer = strings.NewReplacer("\r", "#", "\n", "$")
|
||||
|
||||
// TODO(bep) a way to toggle ORG mode vs the rest.
|
||||
var frontMatterTests = []lexerTest{
|
||||
{"empty", "", []Item{tstEOF}},
|
||||
{"Byte order mark", "\ufeff\nSome text.\n", []Item{nti(TypeIgnore, "\ufeff"), tstSomeText, tstEOF}},
|
||||
{"HTML Document", ` <html> `, []Item{nti(tError, "plain HTML documents not supported")}},
|
||||
{"HTML Document with shortcode", `<html>{{< sc1 >}}</html>`, []Item{nti(tError, "plain HTML documents not supported")}},
|
||||
{"No front matter", "\nSome text.\n", []Item{tstSomeText, tstEOF}},
|
||||
{"YAML front matter", "---\nfoo: \"bar\"\n---\n\nSome text.\n", []Item{tstFrontMatterYAML, tstSomeText, tstEOF}},
|
||||
{"YAML empty front matter", "---\n---\n\nSome text.\n", []Item{nti(TypeFrontMatterYAML, ""), tstSomeText, tstEOF}},
|
||||
{"YAML commented out front matter", "<!--\n---\nfoo: \"bar\"\n---\n-->\nSome text.\n", []Item{nti(TypeIgnore, "<!--\n"), tstFrontMatterYAML, nti(TypeIgnore, "-->"), tstSomeText, tstEOF}},
|
||||
{"YAML commented out front matter, no end", "<!--\n---\nfoo: \"bar\"\n---\nSome text.\n", []Item{nti(TypeIgnore, "<!--\n"), tstFrontMatterYAML, nti(tError, "starting HTML comment with no end")}},
|
||||
{"empty", "", []typeText{tstEOF}},
|
||||
{"Byte order mark", "\ufeff\nSome text.\n", []typeText{nti(TypeIgnore, "\ufeff"), tstSomeText, tstEOF}},
|
||||
{"HTML Document", ` <html> `, []typeText{nti(tError, "plain HTML documents not supported")}},
|
||||
{"HTML Document with shortcode", `<html>{{< sc1 >}}</html>`, []typeText{nti(tError, "plain HTML documents not supported")}},
|
||||
{"No front matter", "\nSome text.\n", []typeText{tstSomeText, tstEOF}},
|
||||
{"YAML front matter", "---\nfoo: \"bar\"\n---\n\nSome text.\n", []typeText{tstFrontMatterYAML, tstSomeText, tstEOF}},
|
||||
{"YAML empty front matter", "---\n---\n\nSome text.\n", []typeText{nti(TypeFrontMatterYAML, ""), tstSomeText, tstEOF}},
|
||||
{"YAML commented out front matter", "<!--\n---\nfoo: \"bar\"\n---\n-->\nSome text.\n", []typeText{nti(TypeIgnore, "<!--\n"), tstFrontMatterYAML, nti(TypeIgnore, "-->"), tstSomeText, tstEOF}},
|
||||
{"YAML commented out front matter, no end", "<!--\n---\nfoo: \"bar\"\n---\nSome text.\n", []typeText{nti(TypeIgnore, "<!--\n"), tstFrontMatterYAML, nti(tError, "starting HTML comment with no end")}},
|
||||
// Note that we keep all bytes as they are, but we need to handle CRLF
|
||||
{"YAML front matter CRLF", "---\r\nfoo: \"bar\"\r\n---\n\nSome text.\n", []Item{tstFrontMatterYAMLCRLF, tstSomeText, tstEOF}},
|
||||
{"TOML front matter", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n", []Item{tstFrontMatterTOML, tstSomeText, tstEOF}},
|
||||
{"JSON front matter", tstJSON + "\r\n\nSome text.\n", []Item{tstFrontMatterJSON, tstSomeText, tstEOF}},
|
||||
{"ORG front matter", tstORG + "\nSome text.\n", []Item{tstFrontMatterORG, tstSomeText, tstEOF}},
|
||||
{"Summary divider ORG", tstORG + "\nSome text.\n# more\nSome text.\n", []Item{tstFrontMatterORG, tstSomeText, nti(TypeLeadSummaryDivider, "# more\n"), nti(tText, "Some text.\n"), tstEOF}},
|
||||
{"Summary divider", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n<!--more-->\nSome text.\n", []Item{tstFrontMatterTOML, tstSomeText, tstSummaryDivider, nti(tText, "Some text.\n"), tstEOF}},
|
||||
{"Summary divider same line", "+++\nfoo = \"bar\"\n+++\n\nSome text.<!--more-->Some text.\n", []Item{tstFrontMatterTOML, nti(tText, "\nSome text."), nti(TypeLeadSummaryDivider, "<!--more-->"), nti(tText, "Some text.\n"), tstEOF}},
|
||||
{"YAML front matter CRLF", "---\r\nfoo: \"bar\"\r\n---\n\nSome text.\n", []typeText{tstFrontMatterYAMLCRLF, tstSomeText, tstEOF}},
|
||||
{"TOML front matter", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n", []typeText{tstFrontMatterTOML, tstSomeText, tstEOF}},
|
||||
{"JSON front matter", tstJSON + "\r\n\nSome text.\n", []typeText{tstFrontMatterJSON, tstSomeText, tstEOF}},
|
||||
{"ORG front matter", tstORG + "\nSome text.\n", []typeText{tstFrontMatterORG, tstSomeText, tstEOF}},
|
||||
{"Summary divider ORG", tstORG + "\nSome text.\n# more\nSome text.\n", []typeText{tstFrontMatterORG, tstSomeText, nti(TypeLeadSummaryDivider, "# more\n"), nti(tText, "Some text.\n"), tstEOF}},
|
||||
{"Summary divider", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n<!--more-->\nSome text.\n", []typeText{tstFrontMatterTOML, tstSomeText, tstSummaryDivider, nti(tText, "Some text.\n"), tstEOF}},
|
||||
{"Summary divider same line", "+++\nfoo = \"bar\"\n+++\n\nSome text.<!--more-->Some text.\n", []typeText{tstFrontMatterTOML, nti(tText, "\nSome text."), nti(TypeLeadSummaryDivider, "<!--more-->"), nti(tText, "Some text.\n"), tstEOF}},
|
||||
// https://github.com/gohugoio/hugo/issues/5402
|
||||
{"Summary and shortcode, no space", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n<!--more-->{{< sc1 >}}\nSome text.\n", []Item{tstFrontMatterTOML, tstSomeText, nti(TypeLeadSummaryDivider, "<!--more-->"), tstLeftNoMD, tstSC1, tstRightNoMD, tstSomeText, tstEOF}},
|
||||
{"Summary and shortcode, no space", "+++\nfoo = \"bar\"\n+++\n\nSome text.\n<!--more-->{{< sc1 >}}\nSome text.\n", []typeText{tstFrontMatterTOML, tstSomeText, nti(TypeLeadSummaryDivider, "<!--more-->"), tstLeftNoMD, tstSC1, tstRightNoMD, tstSomeText, tstEOF}},
|
||||
// https://github.com/gohugoio/hugo/issues/5464
|
||||
{"Summary and shortcode only", "+++\nfoo = \"bar\"\n+++\n{{< sc1 >}}\n<!--more-->\n{{< sc2 >}}", []Item{tstFrontMatterTOML, tstLeftNoMD, tstSC1, tstRightNoMD, tstNewline, tstSummaryDivider, tstLeftNoMD, tstSC2, tstRightNoMD, tstEOF}},
|
||||
{"Summary and shortcode only", "+++\nfoo = \"bar\"\n+++\n{{< sc1 >}}\n<!--more-->\n{{< sc2 >}}", []typeText{tstFrontMatterTOML, tstLeftNoMD, tstSC1, tstRightNoMD, tstNewline, tstSummaryDivider, tstLeftNoMD, tstSC2, tstRightNoMD, tstEOF}},
|
||||
}
|
||||
|
||||
func TestFrontMatter(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
for i, test := range frontMatterTests {
|
||||
items := collect([]byte(test.input), false, lexIntroSection)
|
||||
if !equal(items, test.items) {
|
||||
got := crLfReplacer.Replace(fmt.Sprint(items))
|
||||
expected := crLfReplacer.Replace(fmt.Sprint(test.items))
|
||||
t.Errorf("[%d] %s: got\n\t%v\nexpected\n\t%v", i, test.name, got, expected)
|
||||
if !equal(test.input, items, test.items) {
|
||||
got := itemsToString(items, []byte(test.input))
|
||||
expected := testItemsToString(test.items)
|
||||
c.Assert(got, qt.Equals, expected, qt.Commentf("Test %d: %s", i, test.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func itemsToString(items []Item, source []byte) string {
|
||||
var sb strings.Builder
|
||||
for i, item := range items {
|
||||
var s string
|
||||
if item.Err != nil {
|
||||
s = item.Err.Error()
|
||||
} else {
|
||||
s = string(item.Val(source))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%s: %s\n", item.Type, s))
|
||||
|
||||
if i < len(items)-1 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return crLfReplacer.Replace(sb.String())
|
||||
}
|
||||
|
||||
func testItemsToString(items []typeText) string {
|
||||
var sb strings.Builder
|
||||
for i, item := range items {
|
||||
sb.WriteString(fmt.Sprintf("%s: %s\n", item.typ, item.text))
|
||||
|
||||
if i < len(items)-1 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return crLfReplacer.Replace(sb.String())
|
||||
}
|
||||
|
||||
func collectWithConfig(input []byte, skipFrontMatter bool, stateStart stateFunc, cfg Config) (items []Item) {
|
||||
l := newPageLexer(input, stateStart, cfg)
|
||||
l.run()
|
||||
t := l.newIterator()
|
||||
iter := NewIterator(l.items)
|
||||
|
||||
for {
|
||||
item := t.Next()
|
||||
item := iter.Next()
|
||||
items = append(items, item)
|
||||
if item.Type == tEOF || item.Type == tError {
|
||||
break
|
||||
@@ -108,19 +145,34 @@ func collect(input []byte, skipFrontMatter bool, stateStart stateFunc) (items []
|
||||
return collectWithConfig(input, skipFrontMatter, stateStart, cfg)
|
||||
}
|
||||
|
||||
func collectStringMain(input string) []Item {
|
||||
return collect([]byte(input), true, lexMainSection)
|
||||
}
|
||||
|
||||
// no positional checking, for now ...
|
||||
func equal(i1, i2 []Item) bool {
|
||||
if len(i1) != len(i2) {
|
||||
func equal(source string, got []Item, expect []typeText) bool {
|
||||
if len(got) != len(expect) {
|
||||
return false
|
||||
}
|
||||
for k := range i1 {
|
||||
if i1[k].Type != i2[k].Type {
|
||||
sourceb := []byte(source)
|
||||
for k := range got {
|
||||
g := got[k]
|
||||
e := expect[k]
|
||||
if g.Type != e.typ {
|
||||
return false
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(i1[k].Val, i2[k].Val) {
|
||||
var s string
|
||||
if g.Err != nil {
|
||||
s = g.Err.Error()
|
||||
} else {
|
||||
s = string(g.Val(sourceb))
|
||||
}
|
||||
|
||||
if s != e.text {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -14,27 +14,29 @@
|
||||
package pageparser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
mainTests := []lexerTest{
|
||||
{"emoji #1", "Some text with :emoji:", []Item{nti(tText, "Some text with "), nti(TypeEmoji, ":emoji:"), tstEOF}},
|
||||
{"emoji #2", "Some text with :emoji: and some text.", []Item{nti(tText, "Some text with "), nti(TypeEmoji, ":emoji:"), nti(tText, " and some text."), tstEOF}},
|
||||
{"looks like an emoji #1", "Some text and then :emoji", []Item{nti(tText, "Some text and then "), nti(tText, ":"), nti(tText, "emoji"), tstEOF}},
|
||||
{"looks like an emoji #2", "Some text and then ::", []Item{nti(tText, "Some text and then "), nti(tText, ":"), nti(tText, ":"), tstEOF}},
|
||||
{"looks like an emoji #3", ":Some :text", []Item{nti(tText, ":"), nti(tText, "Some "), nti(tText, ":"), nti(tText, "text"), tstEOF}},
|
||||
{"emoji #1", "Some text with :emoji:", []typeText{nti(tText, "Some text with "), nti(TypeEmoji, ":emoji:"), tstEOF}},
|
||||
{"emoji #2", "Some text with :emoji: and some text.", []typeText{nti(tText, "Some text with "), nti(TypeEmoji, ":emoji:"), nti(tText, " and some text."), tstEOF}},
|
||||
{"looks like an emoji #1", "Some text and then :emoji", []typeText{nti(tText, "Some text and then "), nti(tText, ":"), nti(tText, "emoji"), tstEOF}},
|
||||
{"looks like an emoji #2", "Some text and then ::", []typeText{nti(tText, "Some text and then "), nti(tText, ":"), nti(tText, ":"), tstEOF}},
|
||||
{"looks like an emoji #3", ":Some :text", []typeText{nti(tText, ":"), nti(tText, "Some "), nti(tText, ":"), nti(tText, "text"), tstEOF}},
|
||||
}
|
||||
|
||||
for i, test := range mainTests {
|
||||
items := collectWithConfig([]byte(test.input), false, lexMainSection, Config{EnableEmoji: true})
|
||||
if !equal(items, test.items) {
|
||||
got := crLfReplacer.Replace(fmt.Sprint(items))
|
||||
expected := crLfReplacer.Replace(fmt.Sprint(test.items))
|
||||
t.Errorf("[%d] %s: got\n\t%v\nexpected\n\t%v", i, test.name, got, expected)
|
||||
if !equal(test.input, items, test.items) {
|
||||
got := itemsToString(items, []byte(test.input))
|
||||
expected := testItemsToString(test.items)
|
||||
c.Assert(got, qt.Equals, expected, qt.Commentf("Test %d: %s", i, test.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ package pageparser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -38,27 +40,28 @@ var (
|
||||
tstParamFloat = nti(tScParam, "3.14")
|
||||
tstVal = nti(tScParamVal, "Hello World")
|
||||
tstText = nti(tText, "Hello World")
|
||||
tstIgnoreEscape = nti(TypeIgnore, "\\")
|
||||
)
|
||||
|
||||
var shortCodeLexerTests = []lexerTest{
|
||||
{"empty", "", []Item{tstEOF}},
|
||||
{"spaces", " \t\n", []Item{nti(tText, " \t\n"), tstEOF}},
|
||||
{"text", `to be or not`, []Item{nti(tText, "to be or not"), tstEOF}},
|
||||
{"no markup", `{{< sc1 >}}`, []Item{tstLeftNoMD, tstSC1, tstRightNoMD, tstEOF}},
|
||||
{"with EOL", "{{< sc1 \n >}}", []Item{tstLeftNoMD, tstSC1, tstRightNoMD, tstEOF}},
|
||||
{"empty", "", []typeText{tstEOF}},
|
||||
{"spaces", " \t\n", []typeText{nti(tText, " \t\n"), tstEOF}},
|
||||
{"text", `to be or not`, []typeText{nti(tText, "to be or not"), tstEOF}},
|
||||
{"no markup", `{{< sc1 >}}`, []typeText{tstLeftNoMD, tstSC1, tstRightNoMD, tstEOF}},
|
||||
{"with EOL", "{{< sc1 \n >}}", []typeText{tstLeftNoMD, tstSC1, tstRightNoMD, tstEOF}},
|
||||
|
||||
{"forward slash inside name", `{{< sc/sub >}}`, []Item{tstLeftNoMD, tstSCSlash, tstRightNoMD, tstEOF}},
|
||||
{"forward slash inside name", `{{< sc/sub >}}`, []typeText{tstLeftNoMD, tstSCSlash, tstRightNoMD, tstEOF}},
|
||||
|
||||
{"simple with markup", `{{% sc1 %}}`, []Item{tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"with spaces", `{{< sc1 >}}`, []Item{tstLeftNoMD, tstSC1, tstRightNoMD, tstEOF}},
|
||||
{"indented on new line", "Hello\n {{% sc1 %}}", []Item{nti(tText, "Hello\n"), nti(tIndentation, " "), tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"indented on new line tab", "Hello\n\t{{% sc1 %}}", []Item{nti(tText, "Hello\n"), nti(tIndentation, "\t"), tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"indented on first line", " {{% sc1 %}}", []Item{nti(tIndentation, " "), tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"mismatched rightDelim", `{{< sc1 %}}`, []Item{
|
||||
{"simple with markup", `{{% sc1 %}}`, []typeText{tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"with spaces", `{{< sc1 >}}`, []typeText{tstLeftNoMD, tstSC1, tstRightNoMD, tstEOF}},
|
||||
{"indented on new line", "Hello\n {{% sc1 %}}", []typeText{nti(tText, "Hello\n"), nti(tIndentation, " "), tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"indented on new line tab", "Hello\n\t{{% sc1 %}}", []typeText{nti(tText, "Hello\n"), nti(tIndentation, "\t"), tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"indented on first line", " {{% sc1 %}}", []typeText{nti(tIndentation, " "), tstLeftMD, tstSC1, tstRightMD, tstEOF}},
|
||||
{"mismatched rightDelim", `{{< sc1 %}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1,
|
||||
nti(tError, "unrecognized character in shortcode action: U+0025 '%'. Note: Parameters with non-alphanumeric args must be quoted"),
|
||||
}},
|
||||
{"inner, markup", `{{% sc1 %}} inner {{% /sc1 %}}`, []Item{
|
||||
{"inner, markup", `{{% sc1 %}} inner {{% /sc1 %}}`, []typeText{
|
||||
tstLeftMD,
|
||||
tstSC1,
|
||||
tstRightMD,
|
||||
@@ -69,79 +72,79 @@ var shortCodeLexerTests = []lexerTest{
|
||||
tstRightMD,
|
||||
tstEOF,
|
||||
}},
|
||||
{"close, but no open", `{{< /sc1 >}}`, []Item{
|
||||
{"close, but no open", `{{< /sc1 >}}`, []typeText{
|
||||
tstLeftNoMD, nti(tError, "got closing shortcode, but none is open"),
|
||||
}},
|
||||
{"close wrong", `{{< sc1 >}}{{< /another >}}`, []Item{
|
||||
{"close wrong", `{{< sc1 >}}{{< /another >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstRightNoMD, tstLeftNoMD, tstSCClose,
|
||||
nti(tError, "closing tag for shortcode 'another' does not match start tag"),
|
||||
}},
|
||||
{"close, but no open, more", `{{< sc1 >}}{{< /sc1 >}}{{< /another >}}`, []Item{
|
||||
{"close, but no open, more", `{{< sc1 >}}{{< /sc1 >}}{{< /another >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstRightNoMD, tstLeftNoMD, tstSCClose, tstSC1, tstRightNoMD, tstLeftNoMD, tstSCClose,
|
||||
nti(tError, "closing tag for shortcode 'another' does not match start tag"),
|
||||
}},
|
||||
{"close with extra keyword", `{{< sc1 >}}{{< /sc1 keyword>}}`, []Item{
|
||||
{"close with extra keyword", `{{< sc1 >}}{{< /sc1 keyword>}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstRightNoMD, tstLeftNoMD, tstSCClose, tstSC1,
|
||||
nti(tError, "unclosed shortcode"),
|
||||
}},
|
||||
{"float param, positional", `{{< sc1 3.14 >}}`, []Item{
|
||||
{"float param, positional", `{{< sc1 3.14 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "3.14"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"float param, named", `{{< sc1 param1=3.14 >}}`, []Item{
|
||||
{"float param, named", `{{< sc1 param1=3.14 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tScParamVal, "3.14"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"named param, raw string", `{{< sc1 param1=` + "`" + "Hello World" + "`" + " >}}", []Item{
|
||||
{"named param, raw string", `{{< sc1 param1=` + "`" + "Hello World" + "`" + " >}}", []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tScParamVal, "Hello World"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"float param, named, space before", `{{< sc1 param1= 3.14 >}}`, []Item{
|
||||
{"float param, named, space before", `{{< sc1 param1= 3.14 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tScParamVal, "3.14"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"Youtube id", `{{< sc1 -ziL-Q_456igdO-4 >}}`, []Item{
|
||||
{"Youtube id", `{{< sc1 -ziL-Q_456igdO-4 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "-ziL-Q_456igdO-4"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"non-alphanumerics param quoted", `{{< sc1 "-ziL-.%QigdO-4" >}}`, []Item{
|
||||
{"non-alphanumerics param quoted", `{{< sc1 "-ziL-.%QigdO-4" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "-ziL-.%QigdO-4"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"raw string", `{{< sc1` + "`" + "Hello World" + "`" + ` >}}`, []Item{
|
||||
{"raw string", `{{< sc1` + "`" + "Hello World" + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "Hello World"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"raw string with newline", `{{< sc1` + "`" + `Hello
|
||||
World` + "`" + ` >}}`, []Item{
|
||||
World` + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, `Hello
|
||||
World`), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"raw string with escape character", `{{< sc1` + "`" + `Hello \b World` + "`" + ` >}}`, []Item{
|
||||
{"raw string with escape character", `{{< sc1` + "`" + `Hello \b World` + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, `Hello \b World`), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"two params", `{{< sc1 param1 param2 >}}`, []Item{
|
||||
{"two params", `{{< sc1 param1 param2 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstParam2, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
// issue #934
|
||||
{"self-closing", `{{< sc1 />}}`, []Item{
|
||||
{"self-closing", `{{< sc1 />}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstSCClose, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
// Issue 2498
|
||||
{"multiple self-closing", `{{< sc1 />}}{{< sc1 />}}`, []Item{
|
||||
{"multiple self-closing", `{{< sc1 />}}{{< sc1 />}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstSCClose, tstRightNoMD,
|
||||
tstLeftNoMD, tstSC1, tstSCClose, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"self-closing with param", `{{< sc1 param1 />}}`, []Item{
|
||||
{"self-closing with param", `{{< sc1 param1 />}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"multiple self-closing with param", `{{< sc1 param1 />}}{{< sc1 param1 />}}`, []Item{
|
||||
{"multiple self-closing with param", `{{< sc1 param1 />}}{{< sc1 param1 />}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD,
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"multiple different self-closing with param", `{{< sc1 param1 />}}{{< sc2 param1 />}}`, []Item{
|
||||
{"multiple different self-closing with param", `{{< sc1 param1 />}}{{< sc2 param1 />}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstSCClose, tstRightNoMD,
|
||||
tstLeftNoMD, tstSC2, tstParam1, tstSCClose, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"nested simple", `{{< sc1 >}}{{< sc2 >}}{{< /sc1 >}}`, []Item{
|
||||
{"nested simple", `{{< sc1 >}}{{< sc2 >}}{{< /sc1 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstRightNoMD,
|
||||
tstLeftNoMD, tstSC2, tstRightNoMD,
|
||||
tstLeftNoMD, tstSCClose, tstSC1, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"nested complex", `{{< sc1 >}}ab{{% sc2 param1 %}}cd{{< sc3 >}}ef{{< /sc3 >}}gh{{% /sc2 %}}ij{{< /sc1 >}}kl`, []Item{
|
||||
{"nested complex", `{{< sc1 >}}ab{{% sc2 param1 %}}cd{{< sc3 >}}ef{{< /sc3 >}}gh{{% /sc2 %}}ij{{< /sc1 >}}kl`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstRightNoMD,
|
||||
nti(tText, "ab"),
|
||||
tstLeftMD, tstSC2, tstParam1, tstRightMD,
|
||||
@@ -156,106 +159,109 @@ var shortCodeLexerTests = []lexerTest{
|
||||
nti(tText, "kl"), tstEOF,
|
||||
}},
|
||||
|
||||
{"two quoted params", `{{< sc1 "param nr. 1" "param nr. 2" >}}`, []Item{
|
||||
{"two quoted params", `{{< sc1 "param nr. 1" "param nr. 2" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "param nr. 1"), nti(tScParam, "param nr. 2"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"two named params", `{{< sc1 param1="Hello World" param2="p2Val">}}`, []Item{
|
||||
{"two named params", `{{< sc1 param1="Hello World" param2="p2Val">}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstVal, tstParam2, nti(tScParamVal, "p2Val"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"escaped quotes", `{{< sc1 param1=\"Hello World\" >}}`, []Item{
|
||||
{"escaped quotes", `{{< sc1 param1=\"Hello World\" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstVal, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"escaped quotes, positional param", `{{< sc1 \"param1\" >}}`, []Item{
|
||||
{"escaped quotes, positional param", `{{< sc1 \"param1\" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"escaped quotes inside escaped quotes", `{{< sc1 param1=\"Hello \"escaped\" World\" >}}`, []Item{
|
||||
{"escaped quotes inside escaped quotes", `{{< sc1 param1=\"Hello \"escaped\" World\" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1,
|
||||
nti(tScParamVal, `Hello `), nti(tError, `got positional parameter 'escaped'. Cannot mix named and positional parameters`),
|
||||
}},
|
||||
{
|
||||
"escaped quotes inside nonescaped quotes",
|
||||
`{{< sc1 param1="Hello \"escaped\" World" >}}`,
|
||||
[]Item{
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tScParamVal, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
[]typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstIgnoreEscape, tstIgnoreEscape, nti(tScParamVal, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
},
|
||||
},
|
||||
{
|
||||
"escaped quotes inside nonescaped quotes in positional param",
|
||||
`{{< sc1 "Hello \"escaped\" World" >}}`,
|
||||
[]Item{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
[]typeText{
|
||||
tstLeftNoMD, tstSC1, tstIgnoreEscape, tstIgnoreEscape, nti(tScParam, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
},
|
||||
},
|
||||
{"escaped raw string, named param", `{{< sc1 param1=` + `\` + "`" + "Hello World" + `\` + "`" + ` >}}`, []Item{
|
||||
{"escaped raw string, named param", `{{< sc1 param1=` + `\` + "`" + "Hello World" + `\` + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tError, "unrecognized escape character"),
|
||||
}},
|
||||
{"escaped raw string, positional param", `{{< sc1 param1 ` + `\` + "`" + "Hello World" + `\` + "`" + ` >}}`, []Item{
|
||||
{"escaped raw string, positional param", `{{< sc1 param1 ` + `\` + "`" + "Hello World" + `\` + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tError, "unrecognized escape character"),
|
||||
}},
|
||||
{"two raw string params", `{{< sc1` + "`" + "Hello World" + "`" + "`" + "Second Param" + "`" + ` >}}`, []Item{
|
||||
{"two raw string params", `{{< sc1` + "`" + "Hello World" + "`" + "`" + "Second Param" + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "Hello World"), nti(tScParam, "Second Param"), tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"unterminated quote", `{{< sc1 param2="Hello World>}}`, []Item{
|
||||
{"unterminated quote", `{{< sc1 param2="Hello World>}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam2, nti(tError, "unterminated quoted string in shortcode parameter-argument: 'Hello World>}}'"),
|
||||
}},
|
||||
{"unterminated raw string", `{{< sc1` + "`" + "Hello World" + ` >}}`, []Item{
|
||||
{"unterminated raw string", `{{< sc1` + "`" + "Hello World" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tError, "unterminated raw string in shortcode parameter-argument: 'Hello World >}}'"),
|
||||
}},
|
||||
{"unterminated raw string in second argument", `{{< sc1` + "`" + "Hello World" + "`" + "`" + "Second Param" + ` >}}`, []Item{
|
||||
{"unterminated raw string in second argument", `{{< sc1` + "`" + "Hello World" + "`" + "`" + "Second Param" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, "Hello World"), nti(tError, "unterminated raw string in shortcode parameter-argument: 'Second Param >}}'"),
|
||||
}},
|
||||
{"one named param, one not", `{{< sc1 param1="Hello World" p2 >}}`, []Item{
|
||||
{"one named param, one not", `{{< sc1 param1="Hello World" p2 >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstVal,
|
||||
nti(tError, "got positional parameter 'p2'. Cannot mix named and positional parameters"),
|
||||
}},
|
||||
{"one named param, one quoted positional param, both raw strings", `{{< sc1 param1=` + "`" + "Hello World" + "`" + "`" + "Second Param" + "`" + ` >}}`, []Item{
|
||||
{"one named param, one quoted positional param, both raw strings", `{{< sc1 param1=` + "`" + "Hello World" + "`" + "`" + "Second Param" + "`" + ` >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstVal,
|
||||
nti(tError, "got quoted positional parameter. Cannot mix named and positional parameters"),
|
||||
}},
|
||||
{"one named param, one quoted positional param", `{{< sc1 param1="Hello World" "And Universe" >}}`, []Item{
|
||||
{"one named param, one quoted positional param", `{{< sc1 param1="Hello World" "And Universe" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstVal,
|
||||
nti(tError, "got quoted positional parameter. Cannot mix named and positional parameters"),
|
||||
}},
|
||||
{"one quoted positional param, one named param", `{{< sc1 "param1" param2="And Universe" >}}`, []Item{
|
||||
{"one quoted positional param, one named param", `{{< sc1 "param1" param2="And Universe" >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1,
|
||||
nti(tError, "got named parameter 'param2'. Cannot mix named and positional parameters"),
|
||||
}},
|
||||
{"ono positional param, one not", `{{< sc1 param1 param2="Hello World">}}`, []Item{
|
||||
{"ono positional param, one not", `{{< sc1 param1 param2="Hello World">}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1,
|
||||
nti(tError, "got named parameter 'param2'. Cannot mix named and positional parameters"),
|
||||
}},
|
||||
{"commented out", `{{</* sc1 */>}}`, []Item{
|
||||
{"commented out", `{{</* sc1 */>}}`, []typeText{
|
||||
nti(tText, "{{<"), nti(tText, " sc1 "), nti(tText, ">}}"), tstEOF,
|
||||
}},
|
||||
{"commented out, with asterisk inside", `{{</* sc1 "**/*.pdf" */>}}`, []Item{
|
||||
{"commented out, with asterisk inside", `{{</* sc1 "**/*.pdf" */>}}`, []typeText{
|
||||
nti(tText, "{{<"), nti(tText, " sc1 \"**/*.pdf\" "), nti(tText, ">}}"), tstEOF,
|
||||
}},
|
||||
{"commented out, missing close", `{{</* sc1 >}}`, []Item{
|
||||
{"commented out, missing close", `{{</* sc1 >}}`, []typeText{
|
||||
nti(tError, "comment must be closed"),
|
||||
}},
|
||||
{"commented out, misplaced close", `{{</* sc1 >}}*/`, []Item{
|
||||
{"commented out, misplaced close", `{{</* sc1 >}}*/`, []typeText{
|
||||
nti(tError, "comment must be closed"),
|
||||
}},
|
||||
// Inline shortcodes
|
||||
{"basic inline", `{{< sc1.inline >}}Hello World{{< /sc1.inline >}}`, []Item{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstEOF}},
|
||||
{"basic inline with space", `{{< sc1.inline >}}Hello World{{< / sc1.inline >}}`, []Item{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstEOF}},
|
||||
{"inline self closing", `{{< sc1.inline >}}Hello World{{< /sc1.inline >}}Hello World{{< sc1.inline />}}`, []Item{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSC1Inline, tstSCClose, tstRightNoMD, tstEOF}},
|
||||
{"inline self closing, then a new inline", `{{< sc1.inline >}}Hello World{{< /sc1.inline >}}Hello World{{< sc1.inline />}}{{< sc2.inline >}}Hello World{{< /sc2.inline >}}`, []Item{
|
||||
{"basic inline", `{{< sc1.inline >}}Hello World{{< /sc1.inline >}}`, []typeText{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstEOF}},
|
||||
{"basic inline with space", `{{< sc1.inline >}}Hello World{{< / sc1.inline >}}`, []typeText{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstEOF}},
|
||||
{"inline self closing", `{{< sc1.inline >}}Hello World{{< /sc1.inline >}}Hello World{{< sc1.inline />}}`, []typeText{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSC1Inline, tstSCClose, tstRightNoMD, tstEOF}},
|
||||
{"inline self closing, then a new inline", `{{< sc1.inline >}}Hello World{{< /sc1.inline >}}Hello World{{< sc1.inline />}}{{< sc2.inline >}}Hello World{{< /sc2.inline >}}`, []typeText{
|
||||
tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSC1Inline, tstSCClose, tstRightNoMD,
|
||||
tstLeftNoMD, tstSC2Inline, tstRightNoMD, tstText, tstLeftNoMD, tstSCClose, tstSC2Inline, tstRightNoMD, tstEOF,
|
||||
}},
|
||||
{"inline with template syntax", `{{< sc1.inline >}}{{ .Get 0 }}{{ .Get 1 }}{{< /sc1.inline >}}`, []Item{tstLeftNoMD, tstSC1Inline, tstRightNoMD, nti(tText, "{{ .Get 0 }}"), nti(tText, "{{ .Get 1 }}"), tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstEOF}},
|
||||
{"inline with nested shortcode (not supported)", `{{< sc1.inline >}}Hello World{{< sc1 >}}{{< /sc1.inline >}}`, []Item{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, nti(tError, "inline shortcodes do not support nesting")}},
|
||||
{"inline case mismatch", `{{< sc1.Inline >}}Hello World{{< /sc1.Inline >}}`, []Item{tstLeftNoMD, nti(tError, "period in shortcode name only allowed for inline identifiers")}},
|
||||
{"inline with template syntax", `{{< sc1.inline >}}{{ .Get 0 }}{{ .Get 1 }}{{< /sc1.inline >}}`, []typeText{tstLeftNoMD, tstSC1Inline, tstRightNoMD, nti(tText, "{{ .Get 0 }}"), nti(tText, "{{ .Get 1 }}"), tstLeftNoMD, tstSCClose, tstSC1Inline, tstRightNoMD, tstEOF}},
|
||||
{"inline with nested shortcode (not supported)", `{{< sc1.inline >}}Hello World{{< sc1 >}}{{< /sc1.inline >}}`, []typeText{tstLeftNoMD, tstSC1Inline, tstRightNoMD, tstText, nti(tError, "inline shortcodes do not support nesting")}},
|
||||
{"inline case mismatch", `{{< sc1.Inline >}}Hello World{{< /sc1.Inline >}}`, []typeText{tstLeftNoMD, nti(tError, "period in shortcode name only allowed for inline identifiers")}},
|
||||
}
|
||||
|
||||
func TestShortcodeLexer(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
for i, test := range shortCodeLexerTests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
items := collect([]byte(test.input), true, lexMainSection)
|
||||
if !equal(items, test.items) {
|
||||
t.Errorf("[%d] %s: got\n\t%v\nexpected\n\t%v", i, test.name, items, test.items)
|
||||
if !equal(test.input, items, test.items) {
|
||||
got := itemsToString(items, []byte(test.input))
|
||||
expected := testItemsToString(test.items)
|
||||
c.Assert(got, qt.Equals, expected, qt.Commentf("Test %d: %s", i, test.name))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package pageparser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -88,3 +89,15 @@ func TestFormatFromFrontMatterType(t *testing.T) {
|
||||
c.Assert(FormatFromFrontMatterType(test.typ), qt.Equals, test.expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsProbablyItemsSource(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
input := ` {{< foo >}} `
|
||||
items := collectStringMain(input)
|
||||
|
||||
c.Assert(IsProbablySourceOfItems([]byte(input), items), qt.IsTrue)
|
||||
c.Assert(IsProbablySourceOfItems(bytes.Repeat([]byte(" "), len(input)), items), qt.IsFalse)
|
||||
c.Assert(IsProbablySourceOfItems([]byte(`{{< foo >}} `), items), qt.IsFalse)
|
||||
c.Assert(IsProbablySourceOfItems([]byte(``), items), qt.IsFalse)
|
||||
}
|
||||
|
||||
-253
@@ -1,253 +0,0 @@
|
||||
// Copyright 2017-present 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 releaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
)
|
||||
|
||||
var issueRe = regexp.MustCompile(`(?i)(?:Updates?|Closes?|Fix.*|See) #(\d+)`)
|
||||
|
||||
type changeLog struct {
|
||||
Version string
|
||||
Notes gitInfos
|
||||
All gitInfos
|
||||
Docs gitInfos
|
||||
|
||||
// Overall stats
|
||||
Repo *gitHubRepo
|
||||
ContributorCount int
|
||||
ThemeCount int
|
||||
}
|
||||
|
||||
func newChangeLog(infos, docInfos gitInfos) *changeLog {
|
||||
log := &changeLog{
|
||||
Docs: docInfos,
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
// TODO(bep) improve
|
||||
if regexp.MustCompile("(?i)deprecate|note").MatchString(info.Subject) {
|
||||
log.Notes = append(log.Notes, info)
|
||||
}
|
||||
|
||||
log.All = append(log.All, info)
|
||||
info.Subject = strings.TrimSpace(info.Subject)
|
||||
|
||||
}
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
type gitInfo struct {
|
||||
Hash string
|
||||
Author string
|
||||
Subject string
|
||||
Body string
|
||||
|
||||
GitHubCommit *gitHubCommit
|
||||
}
|
||||
|
||||
func (g gitInfo) Issues() []int {
|
||||
return extractIssues(g.Body)
|
||||
}
|
||||
|
||||
func (g gitInfo) AuthorID() string {
|
||||
if g.GitHubCommit != nil {
|
||||
return g.GitHubCommit.Author.Login
|
||||
}
|
||||
return g.Author
|
||||
}
|
||||
|
||||
func extractIssues(body string) []int {
|
||||
var i []int
|
||||
m := issueRe.FindAllStringSubmatch(body, -1)
|
||||
for _, mm := range m {
|
||||
issueID, err := strconv.Atoi(mm[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
i = append(i, issueID)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
type gitInfos []gitInfo
|
||||
|
||||
func git(args ...string) (string, error) {
|
||||
cmd, _ := hexec.SafeCommand("git", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func getGitInfos(tag, repo, repoPath string, remote bool) (gitInfos, error) {
|
||||
return getGitInfosBefore("HEAD", tag, repo, repoPath, remote)
|
||||
}
|
||||
|
||||
type countribCount struct {
|
||||
Author string
|
||||
GitHubAuthor gitHubAuthor
|
||||
Count int
|
||||
}
|
||||
|
||||
func (c countribCount) AuthorLink() string {
|
||||
if c.GitHubAuthor.HTMLURL != "" {
|
||||
return fmt.Sprintf("[@%s](%s)", c.GitHubAuthor.Login, c.GitHubAuthor.HTMLURL)
|
||||
}
|
||||
|
||||
if !strings.Contains(c.Author, "@") {
|
||||
return c.Author
|
||||
}
|
||||
|
||||
return c.Author[:strings.Index(c.Author, "@")]
|
||||
}
|
||||
|
||||
type contribCounts []countribCount
|
||||
|
||||
func (c contribCounts) Less(i, j int) bool { return c[i].Count > c[j].Count }
|
||||
func (c contribCounts) Len() int { return len(c) }
|
||||
func (c contribCounts) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
|
||||
func (g gitInfos) ContribCountPerAuthor() contribCounts {
|
||||
var c contribCounts
|
||||
|
||||
counters := make(map[string]countribCount)
|
||||
|
||||
for _, gi := range g {
|
||||
authorID := gi.AuthorID()
|
||||
if count, ok := counters[authorID]; ok {
|
||||
count.Count = count.Count + 1
|
||||
counters[authorID] = count
|
||||
} else {
|
||||
var ghA gitHubAuthor
|
||||
if gi.GitHubCommit != nil {
|
||||
ghA = gi.GitHubCommit.Author
|
||||
}
|
||||
authorCount := countribCount{Count: 1, Author: gi.Author, GitHubAuthor: ghA}
|
||||
counters[authorID] = authorCount
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range counters {
|
||||
c = append(c, v)
|
||||
}
|
||||
|
||||
sort.Sort(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func getGitInfosBefore(ref, tag, repo, repoPath string, remote bool) (gitInfos, error) {
|
||||
client := newGitHubAPI(repo)
|
||||
var g gitInfos
|
||||
|
||||
log, err := gitLogBefore(ref, tag, repoPath)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
|
||||
log = strings.Trim(log, "\n\x1e'")
|
||||
entries := strings.Split(log, "\x1e")
|
||||
|
||||
for _, entry := range entries {
|
||||
items := strings.Split(entry, "\x1f")
|
||||
gi := gitInfo{}
|
||||
|
||||
if len(items) > 0 {
|
||||
gi.Hash = items[0]
|
||||
}
|
||||
if len(items) > 1 {
|
||||
gi.Author = items[1]
|
||||
}
|
||||
if len(items) > 2 {
|
||||
gi.Subject = items[2]
|
||||
}
|
||||
if len(items) > 3 {
|
||||
gi.Body = items[3]
|
||||
}
|
||||
|
||||
if remote && gi.Hash != "" {
|
||||
gc, err := client.fetchCommit(gi.Hash)
|
||||
if err == nil {
|
||||
gi.GitHubCommit = &gc
|
||||
}
|
||||
}
|
||||
g = append(g, gi)
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// Ignore autogenerated commits etc. in change log. This is a regexp.
|
||||
const ignoredCommits = "snapcraft:|Merge commit|Squashed"
|
||||
|
||||
func gitLogBefore(ref, tag, repoPath string) (string, error) {
|
||||
var prevTag string
|
||||
var err error
|
||||
if tag != "" {
|
||||
prevTag = tag
|
||||
} else {
|
||||
prevTag, err = gitVersionTagBefore(ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
defaultArgs := []string{"log", "-E", fmt.Sprintf("--grep=%s", ignoredCommits), "--invert-grep", "--pretty=format:%x1e%h%x1f%aE%x1f%s%x1f%b", "--abbrev-commit", prevTag + ".." + ref}
|
||||
|
||||
var args []string
|
||||
|
||||
if repoPath != "" {
|
||||
args = append([]string{"-C", repoPath}, defaultArgs...)
|
||||
} else {
|
||||
args = defaultArgs
|
||||
}
|
||||
|
||||
log, err := git(args...)
|
||||
if err != nil {
|
||||
return ",", err
|
||||
}
|
||||
|
||||
return log, err
|
||||
}
|
||||
|
||||
func gitVersionTagBefore(ref string) (string, error) {
|
||||
return gitShort("describe", "--tags", "--abbrev=0", "--always", "--match", "v[0-9]*", ref+"^")
|
||||
}
|
||||
|
||||
func gitShort(args ...string) (output string, err error) {
|
||||
output, err = git(args...)
|
||||
return strings.Replace(strings.Split(output, "\n")[0], "'", "", -1), err
|
||||
}
|
||||
|
||||
func tagExists(tag string) (bool, error) {
|
||||
out, err := git("tag", "-l", tag)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if strings.Contains(out, tag) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2017-present 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 releaser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestGitInfos(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
skipIfCI(t)
|
||||
infos, err := getGitInfos("v0.20", "hugo", "", false)
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(infos) > 0, qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestIssuesRe(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
body := `
|
||||
This is a commit message.
|
||||
|
||||
Updates #123
|
||||
Fix #345
|
||||
closes #543
|
||||
See #456
|
||||
`
|
||||
|
||||
issues := extractIssues(body)
|
||||
|
||||
c.Assert(len(issues), qt.Equals, 4)
|
||||
c.Assert(issues[0], qt.Equals, 123)
|
||||
c.Assert(issues[2], qt.Equals, 543)
|
||||
|
||||
bodyNoIssues := `
|
||||
This is a commit message without issue refs.
|
||||
|
||||
But it has e #10 to make old regexp confused.
|
||||
Streets #20.
|
||||
`
|
||||
|
||||
emptyIssuesList := extractIssues(bodyNoIssues)
|
||||
c.Assert(len(emptyIssuesList), qt.Equals, 0)
|
||||
}
|
||||
|
||||
func TestGitVersionTagBefore(t *testing.T) {
|
||||
skipIfCI(t)
|
||||
c := qt.New(t)
|
||||
v1, err := gitVersionTagBefore("v0.18")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(v1, qt.Equals, "v0.17")
|
||||
}
|
||||
|
||||
func TestTagExists(t *testing.T) {
|
||||
skipIfCI(t)
|
||||
c := qt.New(t)
|
||||
b1, err := tagExists("v0.18")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(b1, qt.Equals, true)
|
||||
|
||||
b2, err := tagExists("adfagdsfg")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(b2, qt.Equals, false)
|
||||
}
|
||||
|
||||
func skipIfCI(t *testing.T) {
|
||||
if isCI() {
|
||||
// Travis has an ancient git with no --invert-grep: https://github.com/travis-ci/travis-ci/issues/6328
|
||||
// Also Travis clones very shallowly, making some of the tests above shaky.
|
||||
t.Skip("Skip git test on Linux to make Travis happy.")
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user