mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-25 15:58:53 +00:00
Compare commits
75 Commits
v0.124.0
...
bep-patch-1
| Author | SHA1 | Date | |
|---|---|---|---|
| 445eb9e6c3 | |||
| 509ab08c1b | |||
| 2d75f539e1 | |||
| 15a4b9b337 | |||
| 10a8448eee | |||
| 722c486a34 | |||
| f40f50ead0 | |||
| 4e483f5d4a | |||
| 06d248910c | |||
| 004b694390 | |||
| da6112fc65 | |||
| faf9fedc3d | |||
| 11aa893198 | |||
| d88cb5269a | |||
| 68c5ad638c | |||
| 0c188fda24 | |||
| bbc6888d02 | |||
| 8c14d1edc3 | |||
| a32400b5f4 | |||
| df9f2fb617 | |||
| fa60a2fbc3 | |||
| fe63de3a83 | |||
| e197c7b29d | |||
| 74e9129568 | |||
| df11327ba9 | |||
| a18e2bcb9a | |||
| 6049ba99f0 | |||
| 8e50ccfae7 | |||
| bfc3122f8e | |||
| 00ae8e8c72 | |||
| e423e56273 | |||
| 09eb822822 | |||
| a6e8439176 | |||
| 38f68cd162 | |||
| a67650b6f7 | |||
| 2a060b37a3 | |||
| 97df6be59f | |||
| 9323376dfa | |||
| bf0b140364 | |||
| e9b8bec433 | |||
| 888cc1e61e | |||
| 17765a7451 | |||
| 92de8625c7 | |||
| 7907935a42 | |||
| 02d5ec14f3 | |||
| 26640525a3 | |||
| 488b21d15b | |||
| 4500b0e423 | |||
| 060cce0a91 | |||
| 5608ba1f75 | |||
| 7bf1abfc55 | |||
| 2fedca6c8a | |||
| 07873b74bd | |||
| 8a0ea12d8a | |||
| 6f07e5976d | |||
| 2da4ec5738 | |||
| 6624979e1b | |||
| 983b8d537c | |||
| 6738a3e79d | |||
| 2f7df4b926 | |||
| f0a26cf58e | |||
| 74ce5dc841 | |||
| 54a8f0ce21 | |||
| 38e05bd3c7 | |||
| ebfca61ac4 | |||
| e1917740af | |||
| 27414d43a0 | |||
| c837f36ab4 | |||
| a2f67152b3 | |||
| db083b05f1 | |||
| 758a876f90 | |||
| 19937a20ad | |||
| c1ea22a232 | |||
| 0750a9ec91 | |||
| 90bc1f802a |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22200.20100
|
||||
- image: bepsays/ci-hugoreleaser:1.22200.20201
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20100
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20201
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
name: TestDartSassV1
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
DART_SASS_VERSION: 1.62.1
|
||||
DART_SASS_SHA_LINUX: 3574da75a7322a539034648b8ff84ff2cca162eb924d72b663d718cd3936f075
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
DART_SASS_VERSION: 1.62.1
|
||||
DART_SASS_SHA_LINUX: 3574da75a7322a539034648b8ff84ff2cca162eb924d72b663d718cd3936f075
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
@@ -18,56 +18,69 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
cache: true
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b
|
||||
with:
|
||||
ruby-version: '2.7'
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Mage
|
||||
run: go install github.com/magefile/mage@v1.15.0
|
||||
- name: Install asciidoctor
|
||||
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
|
||||
- name: Install docutils
|
||||
run: |
|
||||
pip install docutils
|
||||
rst2html.py --version
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install pandoc on Linux
|
||||
run: |
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
cache: true
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899
|
||||
with:
|
||||
ruby-version: "2.7"
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install Mage
|
||||
run: go install github.com/magefile/mage@v1.15.0
|
||||
- name: Install asciidoctor
|
||||
uses: reitzig/actions-asciidoctor@03fcc74cd74880b697950c4930c9ec8a67c69ecc
|
||||
- name: Install docutils
|
||||
run: |
|
||||
pip install docutils
|
||||
rst2html --version
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install pandoc on Linux
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y pandoc
|
||||
- if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Choco-Install -PackageName pandoc
|
||||
- run: pandoc -v
|
||||
- name: Install dart-sass-embedded Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
|
||||
- name: Check
|
||||
run: |
|
||||
dart-sass-embedded --version
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
- if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Choco-Install -PackageName pandoc
|
||||
- run: pandoc -v
|
||||
- name: Install dart-sass-embedded Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
|
||||
- name: Check
|
||||
run: |
|
||||
dart-sass-embedded --version
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
|
||||
+116
-103
@@ -1,119 +1,132 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
name: Test
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
SASS_VERSION: 1.63.2
|
||||
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
|
||||
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
|
||||
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
SASS_VERSION: 1.63.2
|
||||
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
|
||||
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
|
||||
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.21.x,1.22.x]
|
||||
go-version: [1.21.x, 1.22.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
cache: true
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@036ef458ddccddb148a2b9fb67e95a22fdbf728b
|
||||
with:
|
||||
ruby-version: '2.7'
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Mage
|
||||
run: go install github.com/magefile/mage@v1.15.0
|
||||
- name: Install asciidoctor
|
||||
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
|
||||
- name: Install docutils
|
||||
run: |
|
||||
pip install docutils
|
||||
rst2html.py --version
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install pandoc on Linux
|
||||
run: |
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: true
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
cache: true
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899
|
||||
with:
|
||||
ruby-version: "2.7"
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Install Mage
|
||||
run: go install github.com/magefile/mage@v1.15.0
|
||||
- name: Install asciidoctor
|
||||
uses: reitzig/actions-asciidoctor@03fcc74cd74880b697950c4930c9ec8a67c69ecc
|
||||
- name: Install docutils
|
||||
run: |
|
||||
pip install docutils
|
||||
rst2html --version
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install pandoc on Linux
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y pandoc
|
||||
- if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
choco install pandoc
|
||||
- run: pandoc -v
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
choco install mingw
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GOBIN"
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'macos-latest'
|
||||
name: Install dart-sass MacOS
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c;
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Install dart-sass Windows
|
||||
run: |
|
||||
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
|
||||
Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .;
|
||||
echo "$env:GITHUB_WORKSPACE/dart-sass/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install staticcheck
|
||||
run: go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Run staticcheck
|
||||
run: staticcheck ./...
|
||||
- if: matrix.os != 'windows-latest'
|
||||
name: Check
|
||||
run: |
|
||||
sass --version;
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
- if: matrix.os == 'windows-latest'
|
||||
# See issue #11052. We limit the build to regular test (no -race flag) on Windows for now.
|
||||
name: Test
|
||||
run: |
|
||||
mage -v test;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
- name: Build tags
|
||||
run: |
|
||||
go install -tags extended,nodeploy
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Build for dragonfly
|
||||
run: |
|
||||
go install
|
||||
env:
|
||||
GOARCH: amd64
|
||||
GOOS: dragonfly
|
||||
- if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
choco install pandoc
|
||||
- run: pandoc -v
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
choco install mingw
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GOBIN"
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'macos-latest'
|
||||
name: Install dart-sass MacOS
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c;
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Install dart-sass Windows
|
||||
run: |
|
||||
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
|
||||
Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .;
|
||||
echo "$env:GITHUB_WORKSPACE/dart-sass/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install staticcheck
|
||||
run: go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Run staticcheck
|
||||
run: staticcheck ./...
|
||||
- if: matrix.os != 'windows-latest'
|
||||
name: Check
|
||||
run: |
|
||||
sass --version;
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
- if: matrix.os == 'windows-latest'
|
||||
# See issue #11052. We limit the build to regular test (no -race flag) on Windows for now.
|
||||
name: Test
|
||||
run: |
|
||||
mage -v test;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
- name: Build tags
|
||||
run: |
|
||||
go install -tags extended,nodeploy
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Build for dragonfly
|
||||
run: |
|
||||
go install
|
||||
env:
|
||||
GOARCH: amd64
|
||||
GOOS: dragonfly
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 The Hugo Authors.
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
|
||||
Please report (suspected) security vulnerabilities to **[bjorn.erik.pedersen@gmail.com](mailto:bjorn.erik.pedersen@gmail.com)**. You will receive a response from us within 48 hours. If we can confirm the issue, we will release a patch as soon as possible depending on the complexity of the issue but historically within days.
|
||||
|
||||
Also see [Hugo's Security Model](https://gohugo.io/about/security-model/).
|
||||
Also see [Hugo's Security Model](https://gohugo.io/about/security/).
|
||||
|
||||
Vendored
+14
-4
@@ -140,16 +140,25 @@ func (c *Cache) DrainEvictedIdentities() []identity.Identity {
|
||||
}
|
||||
|
||||
// ClearMatching clears all partition for which the predicate returns true.
|
||||
func (c *Cache) ClearMatching(predicate func(k, v any) bool) {
|
||||
func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManager) bool, predicateValue func(k, v any) bool) {
|
||||
if predicatePartition == nil {
|
||||
predicatePartition = func(k string, p PartitionManager) bool { return true }
|
||||
}
|
||||
if predicateValue == nil {
|
||||
panic("nil predicateValue")
|
||||
}
|
||||
g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{
|
||||
NumWorkers: len(c.partitions),
|
||||
Handle: func(ctx context.Context, partition PartitionManager) error {
|
||||
partition.clearMatching(predicate)
|
||||
partition.clearMatching(predicateValue)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
for _, p := range c.partitions {
|
||||
for k, p := range c.partitions {
|
||||
if !predicatePartition(k, p) {
|
||||
continue
|
||||
}
|
||||
g.Enqueue(p)
|
||||
}
|
||||
|
||||
@@ -340,7 +349,7 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
|
||||
return p.(*Partition[K, V])
|
||||
}
|
||||
|
||||
// At this point, we don't know the the number of partitions or their configuration, but
|
||||
// At this point, we don't know the number of partitions or their configuration, but
|
||||
// this will be re-adjusted later.
|
||||
const numberOfPartitionsEstimate = 10
|
||||
maxSize := opts.CalculateMaxSize(c.opts.MaxSize / numberOfPartitionsEstimate)
|
||||
@@ -356,6 +365,7 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
|
||||
trace: c.opts.Log.Logger().WithLevel(logg.LevelTrace).WithField("partition", name),
|
||||
opts: opts,
|
||||
}
|
||||
|
||||
c.partitions[name] = partition
|
||||
|
||||
return partition
|
||||
|
||||
Vendored
+1
-1
@@ -156,7 +156,7 @@ func TestClear(t *testing.T) {
|
||||
|
||||
cache = newTestCache(t)
|
||||
|
||||
cache.ClearMatching(func(k, v any) bool {
|
||||
cache.ClearMatching(nil, func(k, v any) bool {
|
||||
return k.(string) == "clearOnRebuild"
|
||||
})
|
||||
|
||||
|
||||
+44
-33
@@ -48,6 +48,7 @@ import (
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -127,6 +128,7 @@ type rootCommand struct {
|
||||
verbose bool
|
||||
debug bool
|
||||
quiet bool
|
||||
devMode bool // Hidden flag.
|
||||
|
||||
renderToMemory bool
|
||||
|
||||
@@ -422,29 +424,33 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
|
||||
level := logg.LevelWarn
|
||||
|
||||
if r.logLevel != "" {
|
||||
switch strings.ToLower(r.logLevel) {
|
||||
case "debug":
|
||||
level = logg.LevelDebug
|
||||
case "info":
|
||||
level = logg.LevelInfo
|
||||
case "warn", "warning":
|
||||
level = logg.LevelWarn
|
||||
case "error":
|
||||
level = logg.LevelError
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel)
|
||||
}
|
||||
if r.devMode {
|
||||
level = logg.LevelTrace
|
||||
} else {
|
||||
if r.verbose {
|
||||
hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0")
|
||||
hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0")
|
||||
level = logg.LevelInfo
|
||||
}
|
||||
if r.logLevel != "" {
|
||||
switch strings.ToLower(r.logLevel) {
|
||||
case "debug":
|
||||
level = logg.LevelDebug
|
||||
case "info":
|
||||
level = logg.LevelInfo
|
||||
case "warn", "warning":
|
||||
level = logg.LevelWarn
|
||||
case "error":
|
||||
level = logg.LevelError
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel)
|
||||
}
|
||||
} else {
|
||||
if r.verbose {
|
||||
hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0")
|
||||
hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0")
|
||||
level = logg.LevelInfo
|
||||
}
|
||||
|
||||
if r.debug {
|
||||
hugo.Deprecate("--debug", "use --logLevel debug", "v0.114.0")
|
||||
level = logg.LevelDebug
|
||||
if r.debug {
|
||||
hugo.Deprecate("--debug", "use --logLevel debug", "v0.114.0")
|
||||
level = logg.LevelDebug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,47 +488,50 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
|
||||
// Configure persistent flags
|
||||
cmd.PersistentFlags().StringVarP(&r.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.MarkFlagDirname("source")
|
||||
cmd.PersistentFlags().StringP("destination", "d", "", "filesystem path to write files to")
|
||||
cmd.PersistentFlags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.MarkFlagDirname("destination")
|
||||
|
||||
cmd.PersistentFlags().StringVarP(&r.environment, "environment", "e", "", "build environment")
|
||||
_ = cmd.RegisterFlagCompletionFunc("environment", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
|
||||
_ = cmd.MarkFlagDirname("themesDir")
|
||||
cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
|
||||
_ = cmd.RegisterFlagCompletionFunc("ignoreVendorPaths", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().String("clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00")
|
||||
_ = cmd.RegisterFlagCompletionFunc("clock", cobra.NoFileCompletions)
|
||||
|
||||
cmd.PersistentFlags().StringVar(&r.cfgFile, "config", "", "config file (default is hugo.yaml|json|toml)")
|
||||
_ = cmd.MarkFlagFilename("config", config.ValidConfigFileExtensions...)
|
||||
cmd.PersistentFlags().StringVar(&r.cfgDir, "configDir", "config", "config dir")
|
||||
_ = cmd.MarkFlagDirname("configDir")
|
||||
cmd.PersistentFlags().BoolVar(&r.quiet, "quiet", false, "build in quiet mode")
|
||||
cmd.PersistentFlags().BoolVar(&r.renderToMemory, "renderToMemory", false, "render to memory (mostly useful when running the server)")
|
||||
|
||||
// Set bash-completion
|
||||
_ = cmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, config.ValidConfigFileExtensions)
|
||||
|
||||
cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output")
|
||||
cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output")
|
||||
cmd.PersistentFlags().BoolVarP(&r.devMode, "devMode", "", false, "only used for internal testing, flag hidden.")
|
||||
cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp))
|
||||
cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
||||
|
||||
cmd.PersistentFlags().MarkHidden("devMode")
|
||||
|
||||
// Configure local flags
|
||||
applyLocalFlagsBuild(cmd, r)
|
||||
|
||||
// Set bash-completion.
|
||||
// Each flag must first be defined before using the SetAnnotation() call.
|
||||
_ = cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// A sub set of the complete build flags. These flags are used by new and mod.
|
||||
func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
|
||||
_ = cmd.MarkFlagDirname("theme")
|
||||
cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory")
|
||||
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.MarkFlagDirname("cacheDir")
|
||||
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
|
||||
cmd.Flags().StringSliceP("renderSegments", "", []string{}, "named segments to render (configured in the segments config)")
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
}
|
||||
|
||||
// Flags needed to do a build (used by hugo and hugo server commands)
|
||||
@@ -535,8 +544,10 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory")
|
||||
cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages")
|
||||
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
|
||||
_ = cmd.MarkFlagDirname("layoutDir")
|
||||
cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
|
||||
cmd.Flags().StringVar(&r.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
|
||||
_ = cmd.RegisterFlagCompletionFunc("poll", cobra.NoFileCompletions)
|
||||
cmd.Flags().Bool("panicOnWarning", false, "panic on first WARNING log")
|
||||
cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions")
|
||||
cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics")
|
||||
@@ -559,8 +570,8 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().MarkHidden("profile-mutex")
|
||||
|
||||
cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("disableKinds", cobra.FixedCompletions(kinds.AllKinds, cobra.ShellCompDirectiveNoFileComp))
|
||||
cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)")
|
||||
_ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
|
||||
}
|
||||
|
||||
func (r *rootCommand) timeTrack(start time.Time, name string) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newConfigCommand creates a new config command and its subcommands.
|
||||
@@ -112,7 +113,9 @@ func (c *configCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd.Short = "Print the site configuration"
|
||||
cmd.Long = `Print the site configuration, both default and custom settings.`
|
||||
cmd.Flags().StringVar(&c.format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp))
|
||||
cmd.Flags().StringVar(&c.lang, "lang", "", "the language to display config for. Defaults to the first language defined.")
|
||||
_ = cmd.RegisterFlagCompletionFunc("lang", cobra.NoFileCompletions)
|
||||
applyLocalFlagsBuildConfig(cmd, c.r)
|
||||
|
||||
return nil
|
||||
@@ -223,6 +226,7 @@ func (c *configMountsCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
c.r = cd.Root.Command.(*rootCommand)
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Short = "Print the configured file mounts"
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, c.r)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ to use JSON for the front matter.`,
|
||||
return c.convertContents(metadecoders.JSON)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -57,6 +58,7 @@ to use TOML for the front matter.`,
|
||||
return c.convertContents(metadecoders.TOML)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -68,6 +70,7 @@ to use YAML for the front matter.`,
|
||||
return c.convertContents(metadecoders.YAML)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -108,6 +111,7 @@ func (c *convertCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
See convert's subcommands toJSON, toTOML and toYAML for more information.`
|
||||
|
||||
cmd.PersistentFlags().StringVarP(&c.outputDir, "output", "o", "", "filesystem path to write files to")
|
||||
_ = cmd.MarkFlagDirname("output")
|
||||
cmd.PersistentFlags().BoolVar(&c.unsafe, "unsafe", false, "enable less safe operations, please backup first")
|
||||
|
||||
cmd.RunE = nil
|
||||
|
||||
@@ -60,13 +60,17 @@ documentation.
|
||||
return deployer.Deploy(ctx)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
|
||||
_ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions)
|
||||
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
|
||||
cmd.Flags().Bool("dryRun", false, "dry run")
|
||||
cmd.Flags().Bool("force", false, "force upload of all files")
|
||||
cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
|
||||
cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
|
||||
_ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions)
|
||||
cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
|
||||
_ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newEnvCommand() simplecobra.Commander {
|
||||
@@ -47,6 +48,9 @@ func newEnvCommand() simplecobra.Commander {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,5 +63,8 @@ func newVersionCmd() simplecobra.Commander {
|
||||
},
|
||||
short: "Print Hugo version and environment info",
|
||||
long: "Print Hugo version and environment info. This is useful in Hugo bug reports.",
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+22
-11
@@ -45,9 +45,10 @@ func newGenCommand() *genCommand {
|
||||
genmandir string
|
||||
|
||||
// Chroma flags.
|
||||
style string
|
||||
highlightStyle string
|
||||
linesStyle string
|
||||
style string
|
||||
highlightStyle string
|
||||
lineNumbersInlineStyle string
|
||||
lineNumbersTableStyle string
|
||||
)
|
||||
|
||||
newChromaStyles := func() simplecobra.Commander {
|
||||
@@ -63,8 +64,11 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
|
||||
if highlightStyle != "" {
|
||||
builder.Add(chroma.LineHighlight, highlightStyle)
|
||||
}
|
||||
if linesStyle != "" {
|
||||
builder.Add(chroma.LineNumbers, linesStyle)
|
||||
if lineNumbersInlineStyle != "" {
|
||||
builder.Add(chroma.LineNumbers, lineNumbersInlineStyle)
|
||||
}
|
||||
if lineNumbersTableStyle != "" {
|
||||
builder.Add(chroma.LineNumbersTable, lineNumbersTableStyle)
|
||||
}
|
||||
style, err := builder.Build()
|
||||
if err != nil {
|
||||
@@ -75,9 +79,15 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style (see https://xyproto.github.io/splash/docs/)")
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", "style used for highlighting lines (see https://github.com/alecthomas/chroma)")
|
||||
cmd.PersistentFlags().StringVar(&linesStyle, "linesStyle", "", "style used for line numbers (see https://github.com/alecthomas/chroma)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("style", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", `foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("highlightStyle", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&lineNumbersInlineStyle, "lineNumbersInlineStyle", "", `foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("lineNumbersInlineStyle", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&lineNumbersTableStyle, "lineNumbersTableStyle", "", `foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("lineNumbersTableStyle", cobra.NoFileCompletions)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -115,9 +125,9 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.PersistentFlags().StringVar(&genmandir, "dir", "man/", "the directory to write the man pages.")
|
||||
// For bash-completion
|
||||
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.MarkFlagDirname("dir")
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -172,9 +182,9 @@ url: %s
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.PersistentFlags().StringVar(&gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.")
|
||||
// For bash-completion
|
||||
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.MarkFlagDirname("dir")
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -227,6 +237,7 @@ url: %s
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Hidden = true
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.PersistentFlags().StringVarP(&docsHelperTarget, "dir", "", "docs/data", "data dir")
|
||||
},
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root
|
||||
return c.importFromJekyll(args)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.Flags().BoolVar(&c.force, "force", false, "allow import into non-empty target directory")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newListCommand creates a new list command and its subcommands.
|
||||
@@ -102,6 +103,9 @@ func newListCommand() *listCommand {
|
||||
"buildExpired", true,
|
||||
)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
name: "future",
|
||||
@@ -119,6 +123,9 @@ func newListCommand() *listCommand {
|
||||
"buildDrafts", true,
|
||||
)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
name: "expired",
|
||||
@@ -136,6 +143,9 @@ func newListCommand() *listCommand {
|
||||
"buildDrafts", true,
|
||||
)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
name: "all",
|
||||
@@ -147,6 +157,9 @@ func newListCommand() *listCommand {
|
||||
}
|
||||
return list(cd, r, shouldInclude, "buildDrafts", true, "buildFuture", true, "buildExpired", true)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ removed from Hugo, but we need to test this out in "real life" to get a feel of
|
||||
so this may/will change in future versions of Hugo.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -89,6 +90,7 @@ so this may/will change in future versions of Hugo.
|
||||
inside a subfolder on GitHub, as one example.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -108,6 +110,7 @@ so this may/will change in future versions of Hugo.
|
||||
short: "Verify dependencies.",
|
||||
long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded.`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
|
||||
},
|
||||
@@ -127,6 +130,7 @@ so this may/will change in future versions of Hugo.
|
||||
Note that for vendored modules, that is the version listed and not the one from go.mod.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
|
||||
},
|
||||
@@ -144,8 +148,10 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
short: "Delete the Hugo Module cache for the current project.",
|
||||
long: `Delete the Hugo Module cache for the current project.`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().StringVarP(&pattern, "pattern", "", "", `pattern matching module paths to clean (all if not set), e.g. "**hugo*"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("pattern", cobra.NoFileCompletions)
|
||||
cmd.Flags().BoolVarP(&all, "all", "", false, "clean entire module cache")
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -167,6 +173,7 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
name: "tidy",
|
||||
short: "Remove unused entries in go.mod and go.sum.",
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -184,6 +191,7 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
If a module is vendored, that is where Hugo will look for it's dependencies.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -225,6 +233,7 @@ Run "go help get" for more information. All flags available for "go get" is also
|
||||
` + commonUsageMod,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.DisableFlagParsing = true
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
// We currently just pass on the flags we get to Go and
|
||||
|
||||
@@ -60,8 +60,15 @@ Ensure you run this within the root directory of your site.`,
|
||||
return create.NewContent(h, contentType, args[0], force)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs
|
||||
}
|
||||
cmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create")
|
||||
cmd.Flags().String("editor", "", "edit new content with this editor, if provided")
|
||||
_ = cmd.RegisterFlagCompletionFunc("editor", cobra.NoFileCompletions)
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite file if it already exists")
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
@@ -103,8 +110,15 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs
|
||||
}
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "init inside non-empty directory")
|
||||
cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp))
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -137,6 +151,9 @@ according to your needs.`,
|
||||
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -43,9 +43,11 @@ func newReleaseCommand() simplecobra.Commander {
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Hidden = true
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.PersistentFlags().BoolVarP(&skipPush, "skip-push", "", false, "skip pushing to remote")
|
||||
cmd.PersistentFlags().BoolVarP(&try, "try", "", false, "no changes")
|
||||
cmd.PersistentFlags().IntVarP(&step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("step", cobra.FixedCompletions([]string{"1", "2"}, cobra.ShellCompDirectiveNoFileComp))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+12
-9
@@ -123,6 +123,7 @@ func newServerCommand() *serverCommand {
|
||||
return mclib.RunMain()
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.Flags().BoolVar(&uninstall, "uninstall", false, "Uninstall the local CA (but do not delete it).")
|
||||
},
|
||||
},
|
||||
@@ -236,9 +237,8 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
listener := f.c.serverPorts[i].ln
|
||||
logger := f.c.r.logger
|
||||
|
||||
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
|
||||
|
||||
if i == 0 {
|
||||
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
|
||||
mainTarget := "disk"
|
||||
if f.c.r.renderToMemory {
|
||||
mainTarget = "memory"
|
||||
@@ -307,7 +307,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
if redirect := serverConfig.MatchRedirect(requestURI); !redirect.IsZero() {
|
||||
// fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
|
||||
doRedirect := true
|
||||
// This matches Netlify's behaviour and is needed for SPA behaviour.
|
||||
// This matches Netlify's behavior and is needed for SPA behavior.
|
||||
// See https://docs.netlify.com/routing/redirects/rewrites-proxies/
|
||||
if !redirect.Force {
|
||||
path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path()))
|
||||
@@ -523,10 +523,15 @@ of a second, you will be able to save and see your changes nearly instantly.`
|
||||
cmd.Aliases = []string{"serve"}
|
||||
|
||||
cmd.Flags().IntVarP(&c.serverPort, "port", "p", 1313, "port on which the server will listen")
|
||||
_ = cmd.RegisterFlagCompletionFunc("port", cobra.NoFileCompletions)
|
||||
cmd.Flags().IntVar(&c.liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)")
|
||||
_ = cmd.RegisterFlagCompletionFunc("liveReloadPort", cobra.NoFileCompletions)
|
||||
cmd.Flags().StringVarP(&c.serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
|
||||
_ = cmd.RegisterFlagCompletionFunc("bind", cobra.NoFileCompletions)
|
||||
cmd.Flags().StringVarP(&c.tlsCertFile, "tlsCertFile", "", "", "path to TLS certificate file")
|
||||
_ = cmd.MarkFlagFilename("tlsCertFile", "pem")
|
||||
cmd.Flags().StringVarP(&c.tlsKeyFile, "tlsKeyFile", "", "", "path to TLS key file")
|
||||
_ = cmd.MarkFlagFilename("tlsKeyFile", "pem")
|
||||
cmd.Flags().BoolVar(&c.tlsAuto, "tlsAuto", false, "generate and use locally-trusted certificates.")
|
||||
cmd.Flags().BoolVar(&c.pprof, "pprof", false, "enable the pprof server (port 8080)")
|
||||
cmd.Flags().BoolVarP(&c.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
|
||||
@@ -538,9 +543,6 @@ of a second, you will be able to save and see your changes nearly instantly.`
|
||||
cmd.Flags().BoolVar(&c.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
|
||||
cmd.Flags().BoolVar(&c.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
|
||||
|
||||
cmd.Flags().SetAnnotation("tlsCertFile", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.Flags().SetAnnotation("tlsKeyFile", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
r := cd.Root.Command.(*rootCommand)
|
||||
applyLocalFlagsBuild(cmd, r)
|
||||
|
||||
@@ -566,7 +568,7 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.setBaseURLsInConfig(); err != nil {
|
||||
if err := c.setServerInfoInConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -611,7 +613,7 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *serverCommand) setBaseURLsInConfig() error {
|
||||
func (c *serverCommand) setServerInfoInConfig() error {
|
||||
if len(c.serverPorts) == 0 {
|
||||
panic("no server ports set")
|
||||
}
|
||||
@@ -638,7 +640,8 @@ func (c *serverCommand) setBaseURLsInConfig() error {
|
||||
if c.liveReloadPort != -1 {
|
||||
baseURLLiveReload, _ = baseURLLiveReload.WithPort(c.liveReloadPort)
|
||||
}
|
||||
langConfig.C.SetBaseURL(baseURL, baseURLLiveReload)
|
||||
langConfig.C.SetServerInfo(baseURL, baseURLLiveReload, c.serverInterface)
|
||||
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -123,6 +123,20 @@ func InSlicEqualFold(arr []string, el string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ToString converts the given value to a string.
|
||||
// Note that this is a more strict version compared to cast.ToString,
|
||||
// as it will not try to convert numeric values to strings,
|
||||
// but only accept strings or fmt.Stringer.
|
||||
func ToString(v any) (string, bool) {
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
return vv, true
|
||||
case fmt.Stringer:
|
||||
return vv.String(), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
type Tuple struct {
|
||||
First string
|
||||
Second string
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 124,
|
||||
PatchLevel: 0,
|
||||
Minor: 125,
|
||||
PatchLevel: 2,
|
||||
Suffix: "",
|
||||
}
|
||||
|
||||
@@ -400,6 +400,7 @@ type ConfigCompiled struct {
|
||||
Timeout time.Duration
|
||||
BaseURL urls.BaseURL
|
||||
BaseURLLiveReload urls.BaseURL
|
||||
ServerInterface string
|
||||
KindOutputFormats map[string]output.Formats
|
||||
DisabledKinds map[string]bool
|
||||
DisabledLanguages map[string]bool
|
||||
@@ -434,9 +435,10 @@ func (c *ConfigCompiled) IsMainSectionsSet() bool {
|
||||
}
|
||||
|
||||
// This is set after the config is compiled by the server command.
|
||||
func (c *ConfigCompiled) SetBaseURL(baseURL, baseURLLiveReload urls.BaseURL) {
|
||||
func (c *ConfigCompiled) SetServerInfo(baseURL, baseURLLiveReload urls.BaseURL, serverInterface string) {
|
||||
c.BaseURL = baseURL
|
||||
c.BaseURLLiveReload = baseURLLiveReload
|
||||
c.ServerInterface = serverInterface
|
||||
}
|
||||
|
||||
// RootConfig holds all the top-level configuration options in Hugo
|
||||
|
||||
@@ -215,6 +215,8 @@ type SitemapConfig struct {
|
||||
Priority float64
|
||||
// The sitemap filename.
|
||||
Filename string
|
||||
// Whether to disable page inclusion.
|
||||
Disable bool
|
||||
}
|
||||
|
||||
func DecodeSitemap(prototype SitemapConfig, input map[string]any) (SitemapConfig, error) {
|
||||
|
||||
@@ -44,15 +44,9 @@ type Disqus struct {
|
||||
type GoogleAnalytics struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
|
||||
UseSessionStorage bool
|
||||
|
||||
// Enabling this will make the GA templates respect the
|
||||
// "Do Not Track" HTTP header. See https://www.paulfurley.com/google-analytics-dnt/.
|
||||
RespectDoNotTrack bool
|
||||
|
||||
// Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
|
||||
AnonymizeIP bool
|
||||
}
|
||||
|
||||
// Instagram holds the privacy configuration settings related to the Instagram shortcode.
|
||||
|
||||
@@ -33,8 +33,6 @@ disable = true
|
||||
[privacy.googleAnalytics]
|
||||
disable = true
|
||||
respectDoNotTrack = true
|
||||
anonymizeIP = true
|
||||
useSessionStorage = true
|
||||
[privacy.instagram]
|
||||
disable = true
|
||||
simple = true
|
||||
@@ -60,8 +58,7 @@ simple = true
|
||||
|
||||
got := []bool{
|
||||
pc.Disqus.Disable, pc.GoogleAnalytics.Disable,
|
||||
pc.GoogleAnalytics.RespectDoNotTrack, pc.GoogleAnalytics.AnonymizeIP,
|
||||
pc.GoogleAnalytics.UseSessionStorage, pc.Instagram.Disable,
|
||||
pc.GoogleAnalytics.RespectDoNotTrack, pc.Instagram.Disable,
|
||||
pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT,
|
||||
pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple,
|
||||
pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable,
|
||||
|
||||
@@ -20,10 +20,11 @@ hugo gen chromastyles [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for chromastyles
|
||||
--highlightStyle string style used for highlighting lines (see https://github.com/alecthomas/chroma)
|
||||
--linesStyle string style used for line numbers (see https://github.com/alecthomas/chroma)
|
||||
--style string highlighter style (see https://xyproto.github.io/splash/docs/) (default "friendly")
|
||||
-h, --help help for chromastyles
|
||||
--highlightStyle string foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"
|
||||
--lineNumbersInlineStyle string foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff"
|
||||
--lineNumbersTableStyle string foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff"
|
||||
--style string highlighter style (see https://xyproto.github.io/splash/docs/) (default "friendly")
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
+9
-5
@@ -1557,10 +1557,8 @@ config:
|
||||
disqus:
|
||||
disable: false
|
||||
googleAnalytics:
|
||||
anonymizeIP: false
|
||||
disable: false
|
||||
respectDoNotTrack: false
|
||||
useSessionStorage: false
|
||||
instagram:
|
||||
disable: false
|
||||
simple: false
|
||||
@@ -1651,6 +1649,7 @@ config:
|
||||
disableInlineCSS: false
|
||||
sitemap:
|
||||
changeFreq: ""
|
||||
disable: false
|
||||
filename: sitemap.xml
|
||||
priority: -1
|
||||
social: null
|
||||
@@ -2799,8 +2798,8 @@ tpl:
|
||||
{{ $m.Set "Hugo" "Rocks!" }}
|
||||
{{ $m.Values | debug.Dump | safeHTML }}
|
||||
- |-
|
||||
map[string]interface {}{
|
||||
"Hugo": "Rocks!",
|
||||
{
|
||||
"Hugo": "Rocks!"
|
||||
}
|
||||
TestDeprecationErr:
|
||||
Aliases: null
|
||||
@@ -4018,6 +4017,11 @@ tpl:
|
||||
- s
|
||||
Description: CountWords returns the approximate word count in s.
|
||||
Examples: []
|
||||
Diff:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
FindRE:
|
||||
Aliases:
|
||||
- findRE
|
||||
@@ -4582,5 +4586,5 @@ tpl:
|
||||
- urlize
|
||||
Args:
|
||||
- s
|
||||
Description: URLize returns the the strings s formatted as an URL.
|
||||
Description: URLize returns the strings s formatted as an URL.
|
||||
Examples: []
|
||||
|
||||
@@ -4,8 +4,8 @@ require (
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4
|
||||
github.com/bep/clocks v0.5.0
|
||||
github.com/bep/debounce v1.2.0
|
||||
github.com/bep/gitmap v1.1.2
|
||||
@@ -30,7 +30,7 @@ require (
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.6
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/getkin/kin-openapi v0.123.0
|
||||
github.com/getkin/kin-openapi v0.124.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gobuffalo/flect v1.0.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
@@ -55,7 +55,7 @@ require (
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/pelletier/go-toml/v2 v2.2.1
|
||||
github.com/rogpeppe/go-internal v1.12.0
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
github.com/sanity-io/litter v1.5.5
|
||||
@@ -66,17 +66,17 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tdewolff/minify/v2 v2.20.19
|
||||
github.com/tdewolff/parse/v2 v2.7.12
|
||||
github.com/yuin/goldmark v1.7.0
|
||||
github.com/yuin/goldmark v1.7.1
|
||||
github.com/yuin/goldmark-emoji v1.0.2
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.36.0
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/mod v0.16.0
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/mod v0.17.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/tools v0.19.0
|
||||
golang.org/x/tools v0.20.0
|
||||
google.golang.org/api v0.152.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
@@ -100,8 +100,8 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
@@ -112,7 +112,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/aws/smithy-go v1.20.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
@@ -142,9 +142,9 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
@@ -152,7 +152,7 @@ require (
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
|
||||
|
||||
@@ -74,8 +74,8 @@ github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtS
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA=
|
||||
github.com/aws/aws-sdk-go v1.50.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=
|
||||
@@ -86,16 +86,16 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6Jk
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6 h1:xKbFXea2CIF/Wskauz1TMr//wZ6FyzEafMdSBIQqn80=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6/go.mod h1:iB6PQSb3ULRrrlEiuFfVE318JiBOdk4k46BbuzrrgXc=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4 h1:a4gfRHHCzvV0jEjOUdZOK0oJ4H21x5WT+E4ucWk4jeM=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.35.4/go.mod h1:Pphkts8iBnexoEpcMti5fUvN3/yoGRLtl2heOeppF70=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo=
|
||||
@@ -112,8 +112,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsY
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
|
||||
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
|
||||
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
|
||||
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
||||
@@ -193,8 +193,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
|
||||
github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
|
||||
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@@ -376,8 +376,8 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg=
|
||||
github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
@@ -417,6 +417,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
@@ -425,8 +426,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
|
||||
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
||||
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
|
||||
@@ -441,8 +443,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
|
||||
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
|
||||
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@@ -466,8 +468,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -508,8 +510,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -545,8 +547,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
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=
|
||||
@@ -570,8 +572,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -615,8 +617,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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=
|
||||
@@ -685,8 +687,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
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=
|
||||
@@ -795,8 +797,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
||||
+16
-14
@@ -36,11 +36,6 @@ import (
|
||||
"github.com/gohugoio/hugo/config"
|
||||
)
|
||||
|
||||
var (
|
||||
openingPTag = []byte("<p>")
|
||||
closingPTag = []byte("</p>")
|
||||
)
|
||||
|
||||
// ContentSpec provides functionality to render markdown content.
|
||||
type ContentSpec struct {
|
||||
Converters markup.ConverterProvider
|
||||
@@ -242,19 +237,26 @@ func (c *ContentSpec) TruncateWordsToWholeSentence(s string) (string, bool) {
|
||||
return strings.TrimSpace(s[:endIndex]), endIndex < len(s)
|
||||
}
|
||||
|
||||
// TrimShortHTML removes the <p>/</p> tags from HTML input in the situation
|
||||
// where said tags are the only <p> tags in the input and enclose the content
|
||||
// of the input (whitespace excluded).
|
||||
func (c *ContentSpec) TrimShortHTML(input []byte) []byte {
|
||||
if bytes.Count(input, openingPTag) == 1 {
|
||||
// TrimShortHTML removes the outer tags from HTML input where (a) the opening
|
||||
// tag is present only once with the input, and (b) the opening and closing
|
||||
// tags wrap the input after white space removal.
|
||||
func (c *ContentSpec) TrimShortHTML(input []byte, markup string) []byte {
|
||||
openingTag := []byte("<p>")
|
||||
closingTag := []byte("</p>")
|
||||
|
||||
if markup == "asciidocext" {
|
||||
openingTag = []byte("<div class=\"paragraph\">\n<p>")
|
||||
closingTag = []byte("</p>\n</div>")
|
||||
}
|
||||
|
||||
if bytes.Count(input, openingTag) == 1 {
|
||||
input = bytes.TrimSpace(input)
|
||||
if bytes.HasPrefix(input, openingPTag) && bytes.HasSuffix(input, closingPTag) {
|
||||
input = bytes.TrimPrefix(input, openingPTag)
|
||||
input = bytes.TrimSuffix(input, closingPTag)
|
||||
if bytes.HasPrefix(input, openingTag) && bytes.HasSuffix(input, closingTag) {
|
||||
input = bytes.TrimPrefix(input, openingTag)
|
||||
input = bytes.TrimSuffix(input, closingTag)
|
||||
input = bytes.TrimSpace(input)
|
||||
}
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
|
||||
+24
-13
@@ -26,30 +26,41 @@ import (
|
||||
|
||||
func TestTrimShortHTML(t *testing.T) {
|
||||
tests := []struct {
|
||||
input, output []byte
|
||||
markup string
|
||||
input []byte
|
||||
output []byte
|
||||
}{
|
||||
{[]byte(""), []byte("")},
|
||||
{[]byte("Plain text"), []byte("Plain text")},
|
||||
// This seems wrong. Why touch it if it doesn't have p tag?
|
||||
// {[]byte(" \t\n Whitespace text\n\n"), []byte("Whitespace text")},
|
||||
{[]byte("<p>Simple paragraph</p>"), []byte("Simple paragraph")},
|
||||
{[]byte("\n \n \t <p> \t Whitespace\nHTML \n\t </p>\n\t"), []byte("Whitespace\nHTML")},
|
||||
{[]byte("<p>Multiple</p><p>paragraphs</p>"), []byte("<p>Multiple</p><p>paragraphs</p>")},
|
||||
{[]byte("<p>Nested<p>paragraphs</p></p>"), []byte("<p>Nested<p>paragraphs</p></p>")},
|
||||
{[]byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>"), []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>")},
|
||||
// Issue #11698
|
||||
{[]byte("<h2 id=`a`>b</h2>\n\n<p>c</p>"), []byte("<h2 id=`a`>b</h2>\n\n<p>c</p>")},
|
||||
{"markdown", []byte(""), []byte("")},
|
||||
{"markdown", []byte("Plain text"), []byte("Plain text")},
|
||||
{"markdown", []byte("<p>Simple paragraph</p>"), []byte("Simple paragraph")},
|
||||
{"markdown", []byte("\n \n \t <p> \t Whitespace\nHTML \n\t </p>\n\t"), []byte("Whitespace\nHTML")},
|
||||
{"markdown", []byte("<p>Multiple</p><p>paragraphs</p>"), []byte("<p>Multiple</p><p>paragraphs</p>")},
|
||||
{"markdown", []byte("<p>Nested<p>paragraphs</p></p>"), []byte("<p>Nested<p>paragraphs</p></p>")},
|
||||
{"markdown", []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>"), []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>")},
|
||||
// Issue 11698
|
||||
{"markdown", []byte("<h2 id=`a`>b</h2>\n\n<p>c</p>"), []byte("<h2 id=`a`>b</h2>\n\n<p>c</p>")},
|
||||
// Issue 12369
|
||||
{"markdown", []byte("<div class=\"paragraph\">\n<p>foo</p>\n</div>"), []byte("<div class=\"paragraph\">\n<p>foo</p>\n</div>")},
|
||||
{"asciidocext", []byte("<div class=\"paragraph\">\n<p>foo</p>\n</div>"), []byte("foo")},
|
||||
}
|
||||
|
||||
c := newTestContentSpec(nil)
|
||||
for i, test := range tests {
|
||||
output := c.TrimShortHTML(test.input)
|
||||
output := c.TrimShortHTML(test.input, test.markup)
|
||||
if !bytes.Equal(test.output, output) {
|
||||
t.Errorf("Test %d failed. Expected %q got %q", i, test.output, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTrimShortHTML(b *testing.B) {
|
||||
c := newTestContentSpec(nil)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.TrimShortHTML([]byte("<p>Simple paragraph</p>"), "markdown")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesToHTML(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
c.Assert(helpers.BytesToHTML([]byte("dobedobedo")), qt.Equals, template.HTML("dobedobedo"))
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ func cacheDirDefault(cacheDir string) string {
|
||||
// Turns out that Cloudflare also sets NETLIFY=true in its build environment,
|
||||
// but all of these 3 should not give any false positives.
|
||||
if os.Getenv("NETLIFY") == "true" && os.Getenv("PULL_REQUEST") != "" && os.Getenv("DEPLOY_PRIME_URL") != "" {
|
||||
// Netlify's cache behaviour is not documented, the currently best example
|
||||
// Netlify's cache behavior is not documented, the currently best example
|
||||
// is this project:
|
||||
// https://github.com/philhawksworth/content-shards/blob/master/gulpfile.js
|
||||
return "/opt/build/cache/hugo_cache/"
|
||||
|
||||
@@ -16,6 +16,7 @@ package hqt
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@@ -38,6 +39,11 @@ var IsSameType qt.Checker = &typeChecker{
|
||||
argNames: []string{"got", "want"},
|
||||
}
|
||||
|
||||
// IsSameFloat64 asserts that two float64 values are equal within a small delta.
|
||||
var IsSameFloat64 = qt.CmpEquals(cmp.Comparer(func(a, b float64) bool {
|
||||
return math.Abs(a-b) < 0.0001
|
||||
}))
|
||||
|
||||
type argNames []string
|
||||
|
||||
func (a argNames) ArgNames() []string {
|
||||
|
||||
@@ -490,11 +490,10 @@ name = "menu-theme"
|
||||
got := b.Configs.Base
|
||||
|
||||
if mergeStrategy == "none" {
|
||||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Priority: -1, Filename: "sitemap.xml"})
|
||||
|
||||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Disable: false, Priority: -1, Filename: "sitemap.xml"})
|
||||
b.AssertFileContent("public/sitemap.xml", "schemas/sitemap")
|
||||
} else {
|
||||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Priority: -1, Filename: "sitemap.xml"})
|
||||
b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: -1, Filename: "sitemap.xml"})
|
||||
b.AssertFileContent("public/sitemap.xml", "<changefreq>monthly</changefreq>")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -181,7 +181,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
|
||||
|
||||
var rs *resourceSource
|
||||
if pi.IsContent() {
|
||||
// Create the page now as we need it at assemembly time.
|
||||
// Create the page now as we need it at assembly time.
|
||||
// The other resources are created if needed.
|
||||
pageResource, pi, err := m.s.h.newPage(
|
||||
&pageMeta{
|
||||
|
||||
+28
-12
@@ -93,7 +93,8 @@ type pageMap struct {
|
||||
// Used for simple page lookups by name, e.g. "mypage.md" or "mypage".
|
||||
pageReverseIndex *contentTreeReverseIndex
|
||||
|
||||
cachePages *dynacache.Partition[string, page.Pages]
|
||||
cachePages1 *dynacache.Partition[string, page.Pages]
|
||||
cachePages2 *dynacache.Partition[string, page.Pages]
|
||||
cacheResources *dynacache.Partition[string, resource.Resources]
|
||||
cacheContentRendered *dynacache.Partition[string, *resources.StaleValue[contentSummary]]
|
||||
cacheContentPlain *dynacache.Partition[string, *resources.StaleValue[contentPlainPlainWords]]
|
||||
@@ -132,7 +133,7 @@ type pageTrees struct {
|
||||
func (t *pageTrees) collectAndMarkStaleIdentities(p *paths.Path) []identity.Identity {
|
||||
key := p.Base()
|
||||
var ids []identity.Identity
|
||||
// We need only one identity sample per dimensio.
|
||||
// We need only one identity sample per dimension.
|
||||
nCount := 0
|
||||
cb := func(n contentNodeI) bool {
|
||||
if n == nil {
|
||||
@@ -324,15 +325,19 @@ func (m *pageMap) forEeachPageIncludingBundledPages(include predicate.P[*pageSta
|
||||
}
|
||||
|
||||
func (m *pageMap) getOrCreatePagesFromCache(
|
||||
cache *dynacache.Partition[string, page.Pages],
|
||||
key string, create func(string) (page.Pages, error),
|
||||
) (page.Pages, error) {
|
||||
return m.cachePages.GetOrCreate(key, create)
|
||||
if cache == nil {
|
||||
cache = m.cachePages1
|
||||
}
|
||||
return cache.GetOrCreate(key, create)
|
||||
}
|
||||
|
||||
func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages {
|
||||
cacheKey := q.Key()
|
||||
|
||||
pages, err := m.getOrCreatePagesFromCache(cacheKey, func(string) (page.Pages, error) {
|
||||
pages, err := m.getOrCreatePagesFromCache(nil, cacheKey, func(string) (page.Pages, error) {
|
||||
prefix := paths.AddTrailingSlash(q.Path)
|
||||
|
||||
var (
|
||||
@@ -397,7 +402,7 @@ func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages {
|
||||
func (m *pageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) page.Pages {
|
||||
key := q.Key()
|
||||
|
||||
v, err := m.cachePages.GetOrCreate(key, func(string) (page.Pages, error) {
|
||||
v, err := m.cachePages1.GetOrCreate(key, func(string) (page.Pages, error) {
|
||||
var pas page.Pages
|
||||
include := q.Include
|
||||
if include == nil {
|
||||
@@ -434,7 +439,7 @@ func (m *pageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) page.Pages {
|
||||
func (m *pageMap) getTermsForPageInTaxonomy(path, taxonomy string) page.Pages {
|
||||
prefix := paths.AddLeadingSlash(taxonomy)
|
||||
|
||||
v, err := m.cachePages.GetOrCreate(prefix+path, func(string) (page.Pages, error) {
|
||||
v, err := m.cachePages1.GetOrCreate(prefix+path, func(string) (page.Pages, error) {
|
||||
var pas page.Pages
|
||||
|
||||
err := m.treeTaxonomyEntries.WalkPrefix(
|
||||
@@ -484,12 +489,17 @@ func (m *pageMap) forEachResourceInPage(
|
||||
|
||||
rw.Handle = func(resourceKey string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
|
||||
if isBranch {
|
||||
ownerKey, _ := m.treePages.LongestPrefixAll(resourceKey)
|
||||
if ownerKey != keyPage && path.Dir(ownerKey) != path.Dir(resourceKey) {
|
||||
// A resourceKey always represents a filename with extension.
|
||||
// A page key points to the logical path of a page, which when sourced from the filesystem
|
||||
// may represent a directory (bundles) or a single content file (e.g. p1.md).
|
||||
// So, to avoid any overlapping ambiguity, we start looking from the owning directory.
|
||||
ownerKey, _ := m.treePages.LongestPrefixAll(path.Dir(resourceKey))
|
||||
if ownerKey != keyPage {
|
||||
// Stop walking downwards, someone else owns this resource.
|
||||
rw.SkipPrefix(ownerKey + "/")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
}
|
||||
return handle(resourceKey, n, match)
|
||||
}
|
||||
@@ -861,7 +871,8 @@ func newPageMap(i int, s *Site, mcache *dynacache.Cache, pageTrees *pageTrees) *
|
||||
|
||||
m = &pageMap{
|
||||
pageTrees: pageTrees.Shape(0, i),
|
||||
cachePages: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pags/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}),
|
||||
cachePages1: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pag1/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}),
|
||||
cachePages2: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pag2/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}),
|
||||
cacheResources: dynacache.GetOrCreatePartition[string, resource.Resources](mcache, fmt.Sprintf("/ress/%d", i), dynacache.OptionsPartition{Weight: 60, ClearWhen: dynacache.ClearOnRebuild}),
|
||||
cacheContentRendered: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentSummary]](mcache, fmt.Sprintf("/cont/ren/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
cacheContentPlain: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentPlainPlainWords]](mcache, fmt.Sprintf("/cont/pla/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
@@ -1073,7 +1084,7 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
return b
|
||||
}
|
||||
|
||||
h.MemCache.ClearMatching(shouldDelete)
|
||||
h.MemCache.ClearMatching(nil, shouldDelete)
|
||||
|
||||
return ll, nil
|
||||
}); err != nil {
|
||||
@@ -1081,7 +1092,7 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
}
|
||||
}
|
||||
|
||||
// Drain the the cache eviction stack.
|
||||
// Drain the cache eviction stack.
|
||||
evicted := h.Deps.MemCache.DrainEvictedIdentities()
|
||||
if len(evicted) < 200 {
|
||||
changes = append(changes, evicted...)
|
||||
@@ -1592,7 +1603,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
|
||||
targetPaths := ps.targetPaths()
|
||||
baseTarget := targetPaths.SubResourceBaseTarget
|
||||
duplicateResourceFiles := true
|
||||
if ps.s.ContentSpec.Converters.IsGoldmark(ps.m.pageConfig.Markup) {
|
||||
if ps.m.pageConfig.IsGoldmark {
|
||||
duplicateResourceFiles = ps.s.ContentSpec.Converters.GetMarkupConfig().Goldmark.DuplicateResourceFiles
|
||||
}
|
||||
|
||||
@@ -1678,6 +1689,11 @@ func (sa *sitePagesAssembler) assemblePagesStep2() error {
|
||||
if err := sa.applyAggregatesToTaxonomiesAndTerms(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sa *sitePagesAssembler) assemblePagesStepFinal() error {
|
||||
if err := sa.assembleResources(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -241,3 +242,52 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
|
||||
"p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRenderHooksDefaultEscape(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
[markup.goldmark.renderHooks]
|
||||
[markup.goldmark.renderHooks.image]
|
||||
enableDefault = ENABLE
|
||||
[markup.goldmark.renderHooks.link]
|
||||
enableDefault = ENABLE
|
||||
[markup.goldmark.parser]
|
||||
wrapStandAloneImageWithinParagraph = false
|
||||
[markup.goldmark.parser.attribute]
|
||||
block = true
|
||||
title = true
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "Home"
|
||||
---
|
||||
Link: [text-"<>&](/destination-"<> 'title-"<>&')
|
||||
|
||||
Image: 
|
||||
{class="><script>alert()</script>" id="baz"}
|
||||
|
||||
-- layouts/index.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
for _, enabled := range []bool{true, false} {
|
||||
enabled := enabled
|
||||
t.Run(fmt.Sprint(enabled), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Test(t, strings.ReplaceAll(files, "ENABLE", fmt.Sprint(enabled)))
|
||||
|
||||
// The escaping is slightly different between the two.
|
||||
if enabled {
|
||||
b.AssertFileContent("public/index.html",
|
||||
"Link: <a href=\"/destination-%22%3C%3E\" title=\"title-"<>&\">text-"<>&</a>",
|
||||
"img alt=\"alt-"<>&\" src=\"/destination-%22%3C%3E\" title=\"title-"<>&\">",
|
||||
"><script>",
|
||||
)
|
||||
} else {
|
||||
b.AssertFileContent("public/index.html",
|
||||
"Link: <a href=\"/destination-%22%3C%3E\" title=\"title-"<>&\">text-"<>&</a>",
|
||||
"Image: <img src=\"/destination-%22%3C%3E\" alt=\"alt-"<>&\" title=\"title-"<>&\">",
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ package main
|
||||
|
||||
## YouTube
|
||||
|
||||
{{< youtube PArFPgHrNZM >}}
|
||||
{{< youtube 0RKpf3rK57I >}}
|
||||
|
||||
## Param
|
||||
|
||||
@@ -83,7 +83,7 @@ Content: {{ .Content }}|
|
||||
https://gist.github.com/spf13/7896402.js
|
||||
<span style="color:#a6e22e">main</span></span>
|
||||
https://t.co/X94FmYDEZJ
|
||||
https://www.youtube.com/embed/PArFPgHrNZM
|
||||
https://www.youtube.com/embed/0RKpf3rK57I
|
||||
Foo: bar
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ package hugolib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestInternalTemplatesImage(t *testing.T) {
|
||||
@@ -62,78 +60,41 @@ title: My Site
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
b.AssertFileContent("public/mybundle/index.html", `
|
||||
<meta name="twitter:image" content="https://example.org/mybundle/featured-sunset.jpg" />
|
||||
<meta name="twitter:title" content="My Bundle"/>
|
||||
<meta property="og:title" content="My Bundle" />
|
||||
<meta property="og:url" content="https://example.org/mybundle/" />
|
||||
<meta property="og:image" content="https://example.org/mybundle/featured-sunset.jpg" />
|
||||
<meta property="article:published_time" content="2021-02-26T18:02:00-01:00" />
|
||||
<meta property="article:modified_time" content="2021-05-22T19:25:00-01:00" />
|
||||
<meta name="twitter:image" content="https://example.org/mybundle/featured-sunset.jpg">
|
||||
<meta name="twitter:title" content="My Bundle">
|
||||
<meta property="og:title" content="My Bundle">
|
||||
<meta property="og:url" content="https://example.org/mybundle/">
|
||||
<meta property="og:image" content="https://example.org/mybundle/featured-sunset.jpg">
|
||||
<meta property="article:published_time" content="2021-02-26T18:02:00-01:00">
|
||||
<meta property="article:modified_time" content="2021-05-22T19:25:00-01:00">
|
||||
<meta itemprop="name" content="My Bundle">
|
||||
<meta itemprop="image" content="https://example.org/mybundle/featured-sunset.jpg" />
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00-01:00" />
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00-01:00" />
|
||||
<meta itemprop="image" content="https://example.org/mybundle/featured-sunset.jpg">
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00-01:00">
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00-01:00">
|
||||
|
||||
`)
|
||||
b.AssertFileContent("public/mypage/index.html", `
|
||||
<meta name="twitter:image" content="https://example.org/pageimg1.jpg" />
|
||||
<meta property="og:image" content="https://example.org/pageimg1.jpg" />
|
||||
<meta property="og:image" content="https://example.org/pageimg2.jpg" />
|
||||
<meta property="og:image" content="https://example.local/logo.png" />
|
||||
<meta property="og:image" content="https://example.org/mypage/sample.jpg" />
|
||||
<meta property="article:published_time" content="2021-02-26T18:02:00+01:00" />
|
||||
<meta property="article:modified_time" content="2021-05-22T19:25:00+01:00" />
|
||||
<meta itemprop="image" content="https://example.org/pageimg1.jpg" />
|
||||
<meta itemprop="image" content="https://example.org/pageimg2.jpg" />
|
||||
<meta itemprop="image" content="https://example.local/logo.png" />
|
||||
<meta itemprop="image" content="https://example.org/mypage/sample.jpg" />
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00+01:00" />
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00+01:00" />
|
||||
<meta name="twitter:image" content="https://example.org/pageimg1.jpg">
|
||||
<meta property="og:image" content="https://example.org/pageimg1.jpg">
|
||||
<meta property="og:image" content="https://example.org/pageimg2.jpg">
|
||||
<meta property="og:image" content="https://example.local/logo.png">
|
||||
<meta property="og:image" content="https://example.org/mypage/sample.jpg">
|
||||
<meta property="article:published_time" content="2021-02-26T18:02:00+01:00">
|
||||
<meta property="article:modified_time" content="2021-05-22T19:25:00+01:00">
|
||||
<meta itemprop="image" content="https://example.org/pageimg1.jpg">
|
||||
<meta itemprop="image" content="https://example.org/pageimg2.jpg">
|
||||
<meta itemprop="image" content="https://example.local/logo.png">
|
||||
<meta itemprop="image" content="https://example.org/mypage/sample.jpg">
|
||||
<meta itemprop="datePublished" content="2021-02-26T18:02:00+01:00">
|
||||
<meta itemprop="dateModified" content="2021-05-22T19:25:00+01:00">
|
||||
`)
|
||||
b.AssertFileContent("public/mysite/index.html", `
|
||||
<meta name="twitter:image" content="https://example.org/siteimg1.jpg" />
|
||||
<meta property="og:image" content="https://example.org/siteimg1.jpg" />
|
||||
<meta itemprop="image" content="https://example.org/siteimg1.jpg" />
|
||||
<meta name="twitter:image" content="https://example.org/siteimg1.jpg">
|
||||
<meta property="og:image" content="https://example.org/siteimg1.jpg">
|
||||
<meta itemprop="image" content="https://example.org/siteimg1.jpg">
|
||||
`)
|
||||
}
|
||||
|
||||
// Just some simple test of the embedded templates to avoid
|
||||
// https://github.com/gohugoio/hugo/issues/4757 and similar.
|
||||
func TestEmbeddedTemplates(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := qt.New(t)
|
||||
c.Assert(true, qt.Equals, true)
|
||||
|
||||
home := []string{"index.html", `
|
||||
GA:
|
||||
{{ template "_internal/google_analytics.html" . }}
|
||||
|
||||
GA async:
|
||||
|
||||
{{ template "_internal/google_analytics_async.html" . }}
|
||||
|
||||
Disqus:
|
||||
|
||||
{{ template "_internal/disqus.html" . }}
|
||||
|
||||
`}
|
||||
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithSimpleConfigFile().WithTemplatesAdded(home...)
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
// Gheck GA regular and async
|
||||
b.AssertFileContent("public/index.html",
|
||||
"'anonymizeIp', true",
|
||||
"'script','https://www.google-analytics.com/analytics.js','ga');\n\tga('create', 'UA-ga_id', 'auto')",
|
||||
"<script async src='https://www.google-analytics.com/analytics.js'>")
|
||||
|
||||
// Disqus
|
||||
b.AssertFileContent("public/index.html", "\"disqus_shortname\" + '.disqus.com/embed.js';")
|
||||
}
|
||||
|
||||
func TestEmbeddedPaginationTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
+61
-14
@@ -23,6 +23,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
@@ -46,6 +47,7 @@ import (
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/siteidentities"
|
||||
"github.com/gohugoio/hugo/resources/postpub"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
@@ -282,11 +284,6 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil
|
||||
return err
|
||||
}
|
||||
}
|
||||
h.renderFormats = output.Formats{}
|
||||
for _, s := range h.Sites {
|
||||
s.s.initRenderFormats()
|
||||
h.renderFormats = append(h.renderFormats, s.renderFormats...)
|
||||
}
|
||||
|
||||
for _, s := range assemblers {
|
||||
if err := s.assemblePagesStep2(); err != nil {
|
||||
@@ -296,9 +293,16 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil
|
||||
|
||||
h.renderFormats = output.Formats{}
|
||||
for _, s := range h.Sites {
|
||||
s.s.initRenderFormats()
|
||||
h.renderFormats = append(h.renderFormats, s.renderFormats...)
|
||||
}
|
||||
|
||||
for _, s := range assemblers {
|
||||
if err := s.assemblePagesStepFinal(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -608,7 +612,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
|
||||
// For a list of events for the different OSes, see the test output in https://github.com/bep/fsnotifyeventlister/.
|
||||
events = h.fileEventsFilter(events)
|
||||
events = h.fileEventsTranslate(events)
|
||||
events = h.fileEventsTrim(events)
|
||||
eventInfos := h.fileEventsApplyInfo(events)
|
||||
|
||||
logger := h.Log
|
||||
@@ -728,7 +732,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
|
||||
case files.ComponentFolderLayouts:
|
||||
tmplChanged = true
|
||||
templatePath := pathInfo.TrimLeadingSlash().PathNoLang()
|
||||
templatePath := pathInfo.Unnormalized().TrimLeadingSlash().PathNoLang()
|
||||
if !h.Tmpl().HasTemplate(templatePath) {
|
||||
tmplAdded = true
|
||||
}
|
||||
@@ -756,15 +760,45 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
}
|
||||
}
|
||||
case files.ComponentFolderAssets:
|
||||
logger.Println("Asset changed", pathInfo.Path())
|
||||
p := pathInfo.Path()
|
||||
logger.Println("Asset changed", p)
|
||||
|
||||
var matches []any
|
||||
var mu sync.Mutex
|
||||
|
||||
h.MemCache.ClearMatching(
|
||||
func(k string, pm dynacache.PartitionManager) bool {
|
||||
// Avoid going through everything.
|
||||
return strings.HasPrefix(k, "/res")
|
||||
},
|
||||
func(k, v any) bool {
|
||||
if strings.Contains(k.(string), p) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
switch vv := v.(type) {
|
||||
case resource.Resources:
|
||||
// GetMatch/Match.
|
||||
for _, r := range vv {
|
||||
matches = append(matches, r)
|
||||
}
|
||||
return true
|
||||
default:
|
||||
matches = append(matches, vv)
|
||||
return true
|
||||
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
var hasID bool
|
||||
r, _ := h.ResourceSpec.ResourceCache.Get(context.Background(), dynacache.CleanKey(pathInfo.Base()))
|
||||
identity.WalkIdentitiesShallow(r, func(level int, rid identity.Identity) bool {
|
||||
hasID = true
|
||||
changes = append(changes, rid)
|
||||
return false
|
||||
})
|
||||
for _, r := range matches {
|
||||
identity.WalkIdentitiesShallow(r, func(level int, rid identity.Identity) bool {
|
||||
hasID = true
|
||||
changes = append(changes, rid)
|
||||
return false
|
||||
})
|
||||
}
|
||||
if !hasID {
|
||||
changes = append(changes, pathInfo)
|
||||
}
|
||||
@@ -917,9 +951,22 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
}
|
||||
}
|
||||
|
||||
h.logServerAddresses()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HugoSites) logServerAddresses() {
|
||||
if h.hugoInfo.IsMultihost() {
|
||||
for _, s := range h.Sites {
|
||||
h.Log.Printf("Web Server is available at %s (bind address %s) %s\n", s.conf.C.BaseURL, s.conf.C.ServerInterface, s.Language().Lang)
|
||||
}
|
||||
} else {
|
||||
s := h.Sites[0]
|
||||
h.Log.Printf("Web Server is available at %s (bind address %s)\n", s.conf.C.BaseURL, s.conf.C.ServerInterface)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HugoSites) processFull(ctx context.Context, l logg.LevelLogger, config BuildCfg) (err error) {
|
||||
if err = h.processFiles(ctx, l, config); err != nil {
|
||||
err = fmt.Errorf("readAndProcessContent: %w", err)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -685,8 +686,17 @@ func (s *IntegrationTestBuilder) build(cfg BuildCfg) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We simulate the fsnotify events.
|
||||
// See the test output in https://github.com/bep/fsnotifyeventlister for what events gets produced
|
||||
// by the different OSes.
|
||||
func (s *IntegrationTestBuilder) changeEvents() []fsnotify.Event {
|
||||
var events []fsnotify.Event
|
||||
var (
|
||||
events []fsnotify.Event
|
||||
isLinux = runtime.GOOS == "linux"
|
||||
isMacOs = runtime.GOOS == "darwin"
|
||||
isWindows = runtime.GOOS == "windows"
|
||||
)
|
||||
|
||||
for _, v := range s.removedFiles {
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
@@ -713,12 +723,32 @@ func (s *IntegrationTestBuilder) changeEvents() []fsnotify.Event {
|
||||
Name: v,
|
||||
Op: fsnotify.Write,
|
||||
})
|
||||
if isLinux || isWindows {
|
||||
// Duplicate write events, for some reason.
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
Op: fsnotify.Write,
|
||||
})
|
||||
}
|
||||
if isMacOs {
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
Op: fsnotify.Chmod,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, v := range s.createdFiles {
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
Op: fsnotify.Create,
|
||||
})
|
||||
if isLinux || isWindows {
|
||||
events = append(events, fsnotify.Event{
|
||||
Name: v,
|
||||
Op: fsnotify.Write,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Shuffle events.
|
||||
|
||||
@@ -636,3 +636,77 @@ Menu Item: {{ $i }}|{{ .URL }}|
|
||||
Menu Item: 0|/foo/posts|
|
||||
`)
|
||||
}
|
||||
|
||||
func TestSectionPagesMenuMultilingualWarningIssue12306(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['section','rss','sitemap','taxonomy','term']
|
||||
defaultContentLanguageInSubdir = true
|
||||
sectionPagesMenu = "main"
|
||||
[languages.en]
|
||||
[languages.fr]
|
||||
-- layouts/_default/home.html --
|
||||
{{- range site.Menus.main -}}
|
||||
<a href="{{ .URL }}">{{ .Name }}</a>
|
||||
{{- end -}}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
-- content/p1.en.md --
|
||||
---
|
||||
title: p1
|
||||
menu: main
|
||||
---
|
||||
-- content/p1.fr.md --
|
||||
---
|
||||
title: p1
|
||||
menu: main
|
||||
---
|
||||
-- content/p2.en.md --
|
||||
---
|
||||
title: p2
|
||||
menu: main
|
||||
---
|
||||
`
|
||||
|
||||
b := Test(t, files, TestOptWarn())
|
||||
|
||||
b.AssertFileContent("public/en/index.html", `<a href="/en/p1/">p1</a><a href="/en/p2/">p2</a>`)
|
||||
b.AssertFileContent("public/fr/index.html", `<a href="/fr/p1/">p1</a>`)
|
||||
b.AssertLogNotContains("WARN")
|
||||
}
|
||||
|
||||
func TestSectionPagesIssue12399(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['rss','sitemap','taxonomy','term']
|
||||
capitalizeListTitles = false
|
||||
pluralizeListTitles = false
|
||||
sectionPagesMenu = 'main'
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
-- content/s1/p2.md --
|
||||
---
|
||||
title: p2
|
||||
menus: main
|
||||
---
|
||||
-- content/s1/p3.md --
|
||||
---
|
||||
title: p3
|
||||
---
|
||||
-- layouts/_default/list.html --
|
||||
{{ range site.Menus.main }}<a href="{{ .URL }}">{{ .Name }}</a>{{ end }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/index.html", true)
|
||||
b.AssertFileContent("public/index.html", `<a href="/s1/p2/">p2</a><a href="/s1/">s1</a>`)
|
||||
}
|
||||
|
||||
+4
-2
@@ -380,7 +380,9 @@ func (p *pageState) TranslationKey() string {
|
||||
// AllTranslations returns all translations, including the current Page.
|
||||
func (p *pageState) AllTranslations() page.Pages {
|
||||
key := p.Path() + "/" + "translations-all"
|
||||
pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) {
|
||||
// This is called from Translations, so we need to use a different partition, cachePages2,
|
||||
// to avoid potential deadlocks.
|
||||
pages, err := p.s.pageMap.getOrCreatePagesFromCache(p.s.pageMap.cachePages2, key, func(string) (page.Pages, error) {
|
||||
if p.m.pageConfig.TranslationKey != "" {
|
||||
// translationKey set by user.
|
||||
pas, _ := p.s.h.translationKeyPages.Get(p.m.pageConfig.TranslationKey)
|
||||
@@ -413,7 +415,7 @@ func (p *pageState) AllTranslations() page.Pages {
|
||||
// Translations returns the translations excluding the current Page.
|
||||
func (p *pageState) Translations() page.Pages {
|
||||
key := p.Path() + "/" + "translations"
|
||||
pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) {
|
||||
pages, err := p.s.pageMap.getOrCreatePagesFromCache(nil, key, func(string) (page.Pages, error) {
|
||||
var pas page.Pages
|
||||
for _, pp := range p.AllTranslations() {
|
||||
if !pp.Eq(p) {
|
||||
|
||||
@@ -522,6 +522,7 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New("invalid state: astDoc is set but RenderContent returned false")
|
||||
}
|
||||
@@ -626,8 +627,10 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Callback called from above (e.g. in .RenderString)
|
||||
// Callback called from below (e.g. in .RenderString)
|
||||
ctxCallback := func(cp2 *pageContentOutput, ct2 contentTableOfContents) {
|
||||
cp.otherOutputs[cp2.po.p.pid] = cp2
|
||||
|
||||
// Merge content placeholders
|
||||
for k, v := range ct2.contentPlaceholders {
|
||||
ct.contentPlaceholders[k] = v
|
||||
@@ -770,7 +773,7 @@ func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput)
|
||||
result.readingTime = (result.wordCount + 212) / 213
|
||||
}
|
||||
|
||||
if rendered.summary != "" {
|
||||
if c.pi.hasSummaryDivider || rendered.summary != "" {
|
||||
result.summary = rendered.summary
|
||||
result.summaryTruncated = rendered.summaryTruncated
|
||||
} else if cp.po.p.m.pageConfig.Summary != "" {
|
||||
@@ -778,7 +781,7 @@ func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
html := cp.po.p.s.ContentSpec.TrimShortHTML(b.Bytes())
|
||||
html := cp.po.p.s.ContentSpec.TrimShortHTML(b.Bytes(), cp.po.p.m.pageConfig.Markup)
|
||||
result.summary = helpers.BytesToHTML(html)
|
||||
} else {
|
||||
var summary string
|
||||
|
||||
+21
-5
@@ -676,7 +676,7 @@ params:
|
||||
}
|
||||
|
||||
// shouldList returns whether this page should be included in the list of pages.
|
||||
// glogal indicates site.Pages etc.
|
||||
// global indicates site.Pages etc.
|
||||
func (p *pageMeta) shouldList(global bool) bool {
|
||||
if p.isStandalone() {
|
||||
// Never list 404, sitemap and similar.
|
||||
@@ -737,6 +737,8 @@ func (p *pageMeta) applyDefaultValues() error {
|
||||
}
|
||||
}
|
||||
|
||||
p.pageConfig.IsGoldmark = p.s.ContentSpec.Converters.IsGoldmark(p.pageConfig.Markup)
|
||||
|
||||
if p.pageConfig.Title == "" && p.f == nil {
|
||||
switch p.Kind() {
|
||||
case kinds.KindHome:
|
||||
@@ -794,12 +796,26 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.
|
||||
path = p.Path()
|
||||
}
|
||||
|
||||
doc := newPageForRenderHook(ps)
|
||||
|
||||
documentLookup := func(id uint64) any {
|
||||
if id == ps.pid {
|
||||
// This prevents infinite recursion in some cases.
|
||||
return doc
|
||||
}
|
||||
if v, ok := ps.pageOutput.pco.otherOutputs[id]; ok {
|
||||
return v.po.p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
cpp, err := cp.New(
|
||||
converter.DocumentContext{
|
||||
Document: newPageForRenderHook(ps),
|
||||
DocumentID: id,
|
||||
DocumentName: path,
|
||||
Filename: filename,
|
||||
Document: doc,
|
||||
DocumentLookup: documentLookup,
|
||||
DocumentID: id,
|
||||
DocumentName: path,
|
||||
Filename: filename,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter/hooks"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/hugocontext"
|
||||
"github.com/gohugoio/hugo/markup/highlight/chromalexers"
|
||||
"github.com/gohugoio/hugo/markup/tableofcontents"
|
||||
|
||||
@@ -68,8 +69,9 @@ var (
|
||||
|
||||
func newPageContentOutput(po *pageOutput) (*pageContentOutput, error) {
|
||||
cp := &pageContentOutput{
|
||||
po: po,
|
||||
renderHooks: &renderHooks{},
|
||||
po: po,
|
||||
renderHooks: &renderHooks{},
|
||||
otherOutputs: make(map[uint64]*pageContentOutput),
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
@@ -83,6 +85,10 @@ type renderHooks struct {
|
||||
type pageContentOutput struct {
|
||||
po *pageOutput
|
||||
|
||||
// Other pages involved in rendering of this page,
|
||||
// typically included with .RenderShortcodes.
|
||||
otherOutputs map[uint64]*pageContentOutput
|
||||
|
||||
contentRenderedVersion int // Incremented on reset.
|
||||
contentRendered bool // Set on content render.
|
||||
|
||||
@@ -165,6 +171,13 @@ func (pco *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HT
|
||||
cb(pco, ct)
|
||||
}
|
||||
|
||||
if tpl.Context.IsInGoldmark.Get(ctx) {
|
||||
// This content will be parsed and rendered by Goldmark.
|
||||
// Wrap it in a special Hugo markup to assign the correct Page from
|
||||
// the stack.
|
||||
return template.HTML(hugocontext.Wrap(c, pco.po.p.pid)), nil
|
||||
}
|
||||
|
||||
return helpers.BytesToHTML(c), nil
|
||||
}
|
||||
|
||||
@@ -363,9 +376,11 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
|
||||
}
|
||||
|
||||
if opts.Display == "inline" {
|
||||
// We may have to rethink this in the future when we get other
|
||||
// renderers.
|
||||
rendered = pco.po.p.s.ContentSpec.TrimShortHTML(rendered)
|
||||
markup := pco.po.p.m.pageConfig.Markup
|
||||
if opts.Markup != "" {
|
||||
markup = pco.po.p.s.ContentSpec.ResolveMarkup(opts.Markup)
|
||||
}
|
||||
rendered = pco.po.p.s.ContentSpec.TrimShortHTML(rendered, markup)
|
||||
}
|
||||
|
||||
return template.HTML(string(rendered)), nil
|
||||
|
||||
@@ -63,6 +63,15 @@ Summary Next Line
|
||||
|
||||
<!--more-->
|
||||
Some more text
|
||||
`
|
||||
|
||||
simplePageWithBlankSummary = `---
|
||||
title: SimpleWithBlankSummary
|
||||
---
|
||||
|
||||
<!--more-->
|
||||
|
||||
Some text.
|
||||
`
|
||||
|
||||
simplePageWithSummaryParameter = `---
|
||||
@@ -351,6 +360,9 @@ func normalizeExpected(ext, str string) string {
|
||||
|
||||
return expected
|
||||
case "rst":
|
||||
if str == "" {
|
||||
return "<div class=\"document\"></div>"
|
||||
}
|
||||
return fmt.Sprintf("<div class=\"document\">\n\n\n%s</div>", str)
|
||||
}
|
||||
}
|
||||
@@ -630,6 +642,19 @@ func TestPageWithDelimiter(t *testing.T) {
|
||||
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiter)
|
||||
}
|
||||
|
||||
func TestPageWithBlankSummary(t *testing.T) {
|
||||
t.Parallel()
|
||||
assertFunc := func(t *testing.T, ext string, pages page.Pages) {
|
||||
p := pages[0]
|
||||
checkPageTitle(t, p, "SimpleWithBlankSummary")
|
||||
checkPageContent(t, p, normalizeExpected(ext, "<p>Some text.</p>\n"), ext)
|
||||
checkPageSummary(t, p, normalizeExpected(ext, ""), ext)
|
||||
checkPageType(t, p, "page")
|
||||
}
|
||||
|
||||
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithBlankSummary)
|
||||
}
|
||||
|
||||
func TestPageWithSummaryParameter(t *testing.T) {
|
||||
t.Parallel()
|
||||
assertFunc := func(t *testing.T, ext string, pages page.Pages) {
|
||||
|
||||
@@ -919,3 +919,42 @@ GetMatch: {{ with .Resources.GetMatch "f1.*" }}{{ .Name }}: {{ .Content }}|{{ en
|
||||
|
||||
b.AssertFileContent("public/mybundle/index.html", "GetMatch: f1.en.txt: F1.|")
|
||||
}
|
||||
|
||||
func TestBundleBranchIssue12320(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['rss','sitemap','taxonomy','term']
|
||||
defaultContentLanguage = 'en'
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages.en]
|
||||
baseURL = "https://en.example.org/"
|
||||
contentDir = "content/en"
|
||||
[languages.fr]
|
||||
baseURL = "https://fr.example.org/"
|
||||
contentDir = "content/fr"
|
||||
-- content/en/s1/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
-- content/en/s1/p1.txt --
|
||||
---
|
||||
p1.txt
|
||||
---
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}|
|
||||
-- layouts/_default/list.html --
|
||||
{{ .Title }}|
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/en/s1/index.html", true)
|
||||
b.AssertFileExists("public/en/s1/p1/index.html", true)
|
||||
b.AssertFileExists("public/en/s1/p1.txt", true)
|
||||
|
||||
b.AssertFileExists("public/fr/s1/index.html", false)
|
||||
b.AssertFileExists("public/fr/s1/p1/index.html", false)
|
||||
b.AssertFileExists("public/fr/s1/p1.txt", false) // failing test
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/gohugoio/hugo/markup/asciidocext"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
|
||||
)
|
||||
@@ -1490,3 +1491,89 @@ title: "Default"
|
||||
// Just make sure that it doesn't panic.
|
||||
b.EditFileReplaceAll("archetypes/default.md", "Default", "Default Edited").Build()
|
||||
}
|
||||
|
||||
func TestRebuildEditMixedCaseTemplateFileIssue12165(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
-- layouts/partials/MyTemplate.html --
|
||||
MyTemplate
|
||||
-- layouts/index.html --
|
||||
MyTemplate: {{ partial "MyTemplate.html" . }}|
|
||||
|
||||
|
||||
`
|
||||
|
||||
b := TestRunning(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate")
|
||||
|
||||
b.EditFileReplaceAll("layouts/partials/MyTemplate.html", "MyTemplate", "MyTemplate Edited").Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate Edited")
|
||||
}
|
||||
|
||||
func TestRebuildEditAsciidocContentFile(t *testing.T) {
|
||||
if !asciidocext.Supports() {
|
||||
t.Skip("skip asciidoc")
|
||||
}
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404", "rss", "home", "section"]
|
||||
[security]
|
||||
[security.exec]
|
||||
allow = ['^python$', '^rst2html.*', '^asciidoctor$']
|
||||
-- content/posts/p1.adoc --
|
||||
---
|
||||
title: "P1"
|
||||
---
|
||||
P1 Content.
|
||||
-- content/posts/p2.adoc --
|
||||
---
|
||||
title: "P2"
|
||||
---
|
||||
P2 Content.
|
||||
-- layouts/_default/single.html --
|
||||
Single: {{ .Title }}|{{ .Content }}|
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertFileContent("public/posts/p1/index.html",
|
||||
"Single: P1|<div class=\"paragraph\">\n<p>P1 Content.</p>\n</div>\n|")
|
||||
b.AssertRenderCountPage(2)
|
||||
b.AssertRenderCountContent(2)
|
||||
|
||||
b.EditFileReplaceAll("content/posts/p1.adoc", "P1 Content.", "P1 Content Edited.").Build()
|
||||
|
||||
b.AssertFileContent("public/posts/p1/index.html", "Single: P1|<div class=\"paragraph\">\n<p>P1 Content Edited.</p>\n</div>\n|")
|
||||
b.AssertRenderCountPage(1)
|
||||
b.AssertRenderCountContent(1)
|
||||
}
|
||||
|
||||
func TestRebuildEditSingleListChangeUbuntuIssue12362(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['rss','section','sitemap','taxonomy','term']
|
||||
disableLiveReload = true
|
||||
-- layouts/_default/list.html --
|
||||
{{ range .Pages }}{{ .Title }}|{{ end }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
`
|
||||
|
||||
b := TestRunning(t, files)
|
||||
b.AssertFileContent("public/index.html", "p1|")
|
||||
|
||||
b.AddFiles("content/p2.md", "---\ntitle: p2\n---").Build()
|
||||
b.AssertFileContent("public/index.html", "p1|p2|") // this test passes, which doesn't match reality
|
||||
}
|
||||
|
||||
@@ -200,3 +200,99 @@ Myshort Original.
|
||||
b.Build()
|
||||
b.AssertFileContent("public/p1/index.html", "Edited")
|
||||
}
|
||||
|
||||
func TestRenderShortcodesNestedPageContextIssue12356(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ["taxonomy", "term", "rss", "sitemap", "robotsTXT", "404"]
|
||||
-- layouts/_default/_markup/render-image.html --
|
||||
{{- with .PageInner.Resources.Get .Destination -}}Image: {{ .RelPermalink }}|{{- end -}}
|
||||
-- layouts/_default/_markup/render-link.html --
|
||||
{{- with .PageInner.GetPage .Destination -}}Link: {{ .RelPermalink }}|{{- end -}}
|
||||
-- layouts/_default/_markup/render-heading.html --
|
||||
Heading: {{ .PageInner.Title }}: {{ .PlainText }}|
|
||||
-- layouts/_default/_markup/render-codeblock.html --
|
||||
CodeBlock: {{ .PageInner.Title }}: {{ .Type }}|
|
||||
-- layouts/_default/list.html --
|
||||
Content:{{ .Content }}|
|
||||
Fragments: {{ with .Fragments }}{{.Identifiers }}{{ end }}|
|
||||
-- layouts/_default/single.html --
|
||||
Content:{{ .Content }}|
|
||||
-- layouts/shortcodes/include.html --
|
||||
{{ with site.GetPage (.Get 0) }}
|
||||
{{ .RenderShortcodes }}
|
||||
{{ end }}
|
||||
-- content/markdown/_index.md --
|
||||
---
|
||||
title: "Markdown"
|
||||
---
|
||||
# H1
|
||||
|{{% include "/posts/p1" %}}|
|
||||

|
||||
|
||||
§§§go
|
||||
fmt.Println("Hello")
|
||||
§§§
|
||||
|
||||
-- content/markdown2/_index.md --
|
||||
---
|
||||
title: "Markdown 2"
|
||||
---
|
||||
|{{< include "/posts/p1" >}}|
|
||||
-- content/html/_index.html --
|
||||
---
|
||||
title: "HTML"
|
||||
---
|
||||
|{{% include "/posts/p1" %}}|
|
||||
|
||||
-- content/posts/p1/index.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
## H2-p1
|
||||

|
||||

|
||||
[p2](p2)
|
||||
|
||||
§§§bash
|
||||
echo "Hello"
|
||||
§§§
|
||||
|
||||
-- content/posts/p2/index.md --
|
||||
---
|
||||
title: "p2"
|
||||
---
|
||||
-- content/posts/p1/pixel1.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- content/posts/p1/pixel2.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- content/markdown/pixel3.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- content/html/pixel4.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/markdown/index.html",
|
||||
// Images.
|
||||
"Image: /posts/p1/pixel1.png|\nImage: /posts/p1/pixel2.png|\n|\nImage: /markdown/pixel3.png|</p>\n|",
|
||||
// Links.
|
||||
"Link: /posts/p2/|",
|
||||
// Code blocks
|
||||
"CodeBlock: p1: bash|", "CodeBlock: Markdown: go|",
|
||||
// Headings.
|
||||
"Heading: Markdown: H1|", "Heading: p1: H2-p1|",
|
||||
// Fragments.
|
||||
"Fragments: [h1 h2-p1]|",
|
||||
// Check that the special context markup is not rendered.
|
||||
"! hugo_ctx",
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/markdown2/index.html", "! hugo_ctx", "Content:<p>|\n \n\n|</p>\n|")
|
||||
|
||||
b.AssertFileContent("public/html/index.html", "! hugo_ctx")
|
||||
}
|
||||
|
||||
@@ -321,10 +321,16 @@ func prepareShortcode(
|
||||
|
||||
// Allow the caller to delay the rendering of the shortcode if needed.
|
||||
var fn shortcodeRenderFunc = func(ctx context.Context) ([]byte, bool, error) {
|
||||
if p.m.pageConfig.IsGoldmark && sc.doMarkup {
|
||||
// Signal downwards that the content rendered will be
|
||||
// parsed and rendered by Goldmark.
|
||||
ctx = tpl.Context.IsInGoldmark.Set(ctx, true)
|
||||
}
|
||||
r, err := doRenderShortcode(ctx, level, s, tplVariants, sc, parent, p, isRenderString)
|
||||
if err != nil {
|
||||
return nil, false, toParseErr(err)
|
||||
}
|
||||
|
||||
b, hasVariants, err := r.renderShortcode(ctx)
|
||||
if err != nil {
|
||||
return nil, false, toParseErr(err)
|
||||
|
||||
+43
-32
@@ -424,7 +424,35 @@ func (h *HugoSites) fileEventsFilter(events []fsnotify.Event) []fsnotify.Event {
|
||||
events[n] = ev
|
||||
n++
|
||||
}
|
||||
return events[:n]
|
||||
events = events[:n]
|
||||
|
||||
eventOrdinal := func(e fsnotify.Event) int {
|
||||
// Pull the structural changes to the top.
|
||||
if e.Op.Has(fsnotify.Create) {
|
||||
return 1
|
||||
}
|
||||
if e.Op.Has(fsnotify.Remove) {
|
||||
return 2
|
||||
}
|
||||
if e.Op.Has(fsnotify.Rename) {
|
||||
return 3
|
||||
}
|
||||
if e.Op.Has(fsnotify.Write) {
|
||||
return 4
|
||||
}
|
||||
return 5
|
||||
}
|
||||
|
||||
sort.Slice(events, func(i, j int) bool {
|
||||
// First sort by event type.
|
||||
if eventOrdinal(events[i]) != eventOrdinal(events[j]) {
|
||||
return eventOrdinal(events[i]) < eventOrdinal(events[j])
|
||||
}
|
||||
// Then sort by name.
|
||||
return events[i].Name < events[j].Name
|
||||
})
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
type fileEventInfo struct {
|
||||
@@ -494,41 +522,17 @@ func (h *HugoSites) fileEventsApplyInfo(events []fsnotify.Event) []fileEventInfo
|
||||
return infos
|
||||
}
|
||||
|
||||
func (h *HugoSites) fileEventsTranslate(events []fsnotify.Event) []fsnotify.Event {
|
||||
eventMap := make(map[string][]fsnotify.Event)
|
||||
|
||||
// We often get a Remove etc. followed by a Create, a Create followed by a Write.
|
||||
// Remove the superfluous events to make the update logic simpler.
|
||||
for _, ev := range events {
|
||||
eventMap[ev.Name] = append(eventMap[ev.Name], ev)
|
||||
}
|
||||
|
||||
func (h *HugoSites) fileEventsTrim(events []fsnotify.Event) []fsnotify.Event {
|
||||
seen := make(map[string]bool)
|
||||
n := 0
|
||||
for _, ev := range events {
|
||||
mapped := eventMap[ev.Name]
|
||||
|
||||
// Keep one
|
||||
found := false
|
||||
var kept fsnotify.Event
|
||||
for i, ev2 := range mapped {
|
||||
if i == 0 {
|
||||
kept = ev2
|
||||
}
|
||||
|
||||
if ev2.Op&fsnotify.Write == fsnotify.Write {
|
||||
kept = ev2
|
||||
found = true
|
||||
}
|
||||
|
||||
if !found && ev2.Op&fsnotify.Create == fsnotify.Create {
|
||||
kept = ev2
|
||||
}
|
||||
if seen[ev.Name] {
|
||||
continue
|
||||
}
|
||||
|
||||
events[n] = kept
|
||||
seen[ev.Name] = true
|
||||
events[n] = ev
|
||||
n++
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
@@ -655,11 +659,16 @@ func (s *Site) assembleMenus() error {
|
||||
|
||||
if sectionPagesMenu != "" {
|
||||
if err := s.pageMap.forEachPage(pagePredicates.ShouldListGlobal, func(p *pageState) (bool, error) {
|
||||
if p.IsHome() || !p.m.shouldBeCheckedForMenuDefinitions() {
|
||||
if p.Kind() != kinds.KindSection || !p.m.shouldBeCheckedForMenuDefinitions() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// The section pages menus are attached to the top level section.
|
||||
id := p.Section()
|
||||
if id == "" {
|
||||
id = "/"
|
||||
}
|
||||
|
||||
if _, ok := flat[twoD{sectionPagesMenu, id}]; ok {
|
||||
return false, nil
|
||||
}
|
||||
@@ -671,6 +680,7 @@ func (s *Site) assembleMenus() error {
|
||||
},
|
||||
Page: p,
|
||||
}
|
||||
|
||||
navigation.SetPageValues(&me, p)
|
||||
flat[twoD{sectionPagesMenu, me.KeyName()}] = &me
|
||||
return false, nil
|
||||
@@ -678,6 +688,7 @@ func (s *Site) assembleMenus() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add menu entries provided by pages
|
||||
if err := s.pageMap.forEachPage(pagePredicates.ShouldListGlobal, func(p *pageState) (bool, error) {
|
||||
for name, me := range p.pageMenus.menus() {
|
||||
|
||||
+3
-1
@@ -449,7 +449,9 @@ func (s *Site) Params() maps.Params {
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Author() map[string]any {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
if len(s.conf.Author) != 0 {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
}
|
||||
return s.conf.Author
|
||||
}
|
||||
|
||||
|
||||
@@ -646,3 +646,40 @@ WordCount: {{ .WordCount }}
|
||||
b.AssertFileContent("public/outputs-empty/index.html", "HTML:", "Word1. Word2.")
|
||||
b.AssertFileContent("public/outputs-string/index.html", "O1:", "Word1. Word2.")
|
||||
}
|
||||
|
||||
func TestOuputFormatFrontMatterTermIssue12275(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','page','rss','section','sitemap','taxonomy']
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
tags:
|
||||
- tag-a
|
||||
- tag-b
|
||||
---
|
||||
-- content/tags/tag-a/_index.md --
|
||||
---
|
||||
title: tag-a
|
||||
outputs:
|
||||
- html
|
||||
- json
|
||||
---
|
||||
-- content/tags/tag-b/_index.md --
|
||||
---
|
||||
title: tag-b
|
||||
---
|
||||
-- layouts/_default/term.html --
|
||||
{{ .Title }}
|
||||
-- layouts/_default/term.json --
|
||||
{{ jsonify (dict "title" .Title) }}
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/tags/tag-a/index.html", "tag-a")
|
||||
b.AssertFileContent("public/tags/tag-b/index.html", "tag-b")
|
||||
b.AssertFileContent("public/tags/tag-a/index.json", `{"title":"tag-a"}`) // failing test
|
||||
}
|
||||
|
||||
@@ -108,11 +108,12 @@ outputs: [ "html", "amp" ]
|
||||
|
||||
func TestParseSitemap(t *testing.T) {
|
||||
t.Parallel()
|
||||
expected := config.SitemapConfig{Priority: 3.0, Filename: "doo.xml", ChangeFreq: "3"}
|
||||
expected := config.SitemapConfig{ChangeFreq: "3", Disable: true, Filename: "doo.xml", Priority: 3.0}
|
||||
input := map[string]any{
|
||||
"changefreq": "3",
|
||||
"priority": 3.0,
|
||||
"disable": true,
|
||||
"filename": "doo.xml",
|
||||
"priority": 3.0,
|
||||
"unknown": "ignore",
|
||||
}
|
||||
result, err := config.DecodeSitemap(config.SitemapConfig{}, input)
|
||||
|
||||
@@ -258,7 +258,6 @@ id = "UA-ga_id"
|
||||
disable = false
|
||||
[privacy.googleAnalytics]
|
||||
respectDoNotTrack = true
|
||||
anonymizeIP = true
|
||||
[privacy.instagram]
|
||||
simple = true
|
||||
[privacy.twitter]
|
||||
|
||||
+6
-2
@@ -1,7 +1,11 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.123.8
|
||||
HUGORELEASER_COMMITISH=5fed9c591b694f314e5939548e11cc3dcb79a79c
|
||||
HUGORELEASER_TAG=v0.125.1
|
||||
HUGORELEASER_COMMITISH=68c5ad638c2072969e47262926b912e80fd71a77
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ func (t Translator) initFuncs(bndl *i18n.Bundle) {
|
||||
// the context.Context.
|
||||
// A common pattern is to pass Page to i18n, and use .ReadingTime etc.
|
||||
// We need to improve this, but that requires some upstream changes.
|
||||
// For now, just creata a wrepper.
|
||||
// For now, just create a wrapper.
|
||||
templateData = page.PageWithContext{Page: p, Ctx: ctx}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func TestAsciidoctorAttributes(t *testing.T) {
|
||||
trace = false
|
||||
[markup.asciidocext.attributes]
|
||||
my-base-url = "https://gohugo.io/"
|
||||
my-attribute-name = "my value"
|
||||
my-attribute-name = "my value"
|
||||
`)
|
||||
conf := testconfig.GetTestConfig(nil, cfg)
|
||||
p, err := asciidocext.Provider.New(
|
||||
@@ -301,7 +301,7 @@ func getProvider(c *qt.C, mConfStr string) converter.Provider {
|
||||
confStr := `
|
||||
[security]
|
||||
[security.exec]
|
||||
allow = ['asciidoctor']
|
||||
allow = ['asciidoctor']
|
||||
`
|
||||
confStr += mConfStr
|
||||
|
||||
@@ -368,6 +368,13 @@ testContent
|
||||
c.Assert(ok, qt.Equals, true)
|
||||
|
||||
c.Assert(toc.TableOfContents().Identifiers, qt.DeepEquals, collections.SortedStringSlice{"_introduction", "_section_1", "_section_1_1", "_section_1_1_1", "_section_1_2", "_section_2"})
|
||||
// Although "Introduction" has a level 3 markup heading, AsciiDoc treats the first heading as level 2.
|
||||
c.Assert(toc.TableOfContents().HeadingsMap["_introduction"].Level, qt.Equals, 2)
|
||||
c.Assert(toc.TableOfContents().HeadingsMap["_section_1"].Level, qt.Equals, 2)
|
||||
c.Assert(toc.TableOfContents().HeadingsMap["_section_1_1"].Level, qt.Equals, 3)
|
||||
c.Assert(toc.TableOfContents().HeadingsMap["_section_1_1_1"].Level, qt.Equals, 4)
|
||||
c.Assert(toc.TableOfContents().HeadingsMap["_section_1_2"].Level, qt.Equals, 3)
|
||||
c.Assert(toc.TableOfContents().HeadingsMap["_section_2"].Level, qt.Equals, 2)
|
||||
c.Assert(string(r.Bytes()), qt.Not(qt.Contains), "<div id=\"toc\" class=\"toc\">")
|
||||
}
|
||||
|
||||
|
||||
@@ -243,6 +243,7 @@ func parseTOC(doc *html.Node) *tableofcontents.Fragments {
|
||||
toc.AddAt(&tableofcontents.Heading{
|
||||
Title: nodeContent(c),
|
||||
ID: href,
|
||||
Level: level + 1,
|
||||
}, row, level)
|
||||
}
|
||||
f(n.FirstChild, row, level)
|
||||
|
||||
@@ -135,10 +135,11 @@ func (b Bytes) Bytes() []byte {
|
||||
|
||||
// DocumentContext holds contextual information about the document to convert.
|
||||
type DocumentContext struct {
|
||||
Document any // May be nil. Usually a page.Page
|
||||
DocumentID string
|
||||
DocumentName string
|
||||
Filename string
|
||||
Document any // May be nil. Usually a page.Page
|
||||
DocumentLookup func(uint64) any // May be nil.
|
||||
DocumentID string
|
||||
DocumentName string
|
||||
Filename string
|
||||
}
|
||||
|
||||
// RenderContext holds contextual information about the content to render.
|
||||
|
||||
@@ -32,8 +32,7 @@ type AttributesProvider interface {
|
||||
|
||||
// LinkContext is the context passed to a link render hook.
|
||||
type LinkContext interface {
|
||||
// The Page being rendered.
|
||||
Page() any
|
||||
PageProvider
|
||||
|
||||
// The link URL.
|
||||
Destination() string
|
||||
@@ -64,6 +63,7 @@ type ImageLinkContext interface {
|
||||
type CodeblockContext interface {
|
||||
AttributesProvider
|
||||
text.Positioner
|
||||
PageProvider
|
||||
|
||||
// Chroma highlighting processing options. This will only be filled if Type is a known Chroma Lexer.
|
||||
Options() map[string]any
|
||||
@@ -76,9 +76,6 @@ type CodeblockContext interface {
|
||||
|
||||
// Zero-based ordinal for all code blocks in the current document.
|
||||
Ordinal() int
|
||||
|
||||
// The owning Page.
|
||||
Page() any
|
||||
}
|
||||
|
||||
type AttributesOptionsSliceProvider interface {
|
||||
@@ -101,8 +98,7 @@ type IsDefaultCodeBlockRendererProvider interface {
|
||||
// HeadingContext contains accessors to all attributes that a HeadingRenderer
|
||||
// can use to render a heading.
|
||||
type HeadingContext interface {
|
||||
// Page is the page containing the heading.
|
||||
Page() any
|
||||
PageProvider
|
||||
// Level is the level of the header (i.e. 1 for top-level, 2 for sub-level, etc.).
|
||||
Level() int
|
||||
// Anchor is the HTML id assigned to the heading.
|
||||
@@ -116,6 +112,16 @@ type HeadingContext interface {
|
||||
AttributesProvider
|
||||
}
|
||||
|
||||
type PageProvider interface {
|
||||
// Page is the page being rendered.
|
||||
Page() any
|
||||
|
||||
// PageInner may be different than Page when .RenderShortcodes is in play.
|
||||
// The main use case for this is to include other pages' markdown into the current page
|
||||
// but resolve resources and pages relative to the original.
|
||||
PageInner() any
|
||||
}
|
||||
|
||||
// HeadingRenderer describes a uniquely identifiable rendering hook.
|
||||
type HeadingRenderer interface {
|
||||
// RenderHeading writes the rendered content to w using the data in w.
|
||||
|
||||
@@ -108,6 +108,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
}
|
||||
cbctx := &codeBlockContext{
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
lang: lang,
|
||||
code: s,
|
||||
ordinal: ordinal,
|
||||
@@ -132,7 +133,6 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
w,
|
||||
cbctx,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return ast.WalkContinue, herrors.NewFileErrorFromPos(err, cbctx.createPos())
|
||||
}
|
||||
@@ -140,11 +140,24 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *htmlRenderer) getPageInner(rctx *render.Context) any {
|
||||
pid := rctx.PeekPid()
|
||||
if pid > 0 {
|
||||
if lookup := rctx.DocumentContext().DocumentLookup; lookup != nil {
|
||||
if v := rctx.DocumentContext().DocumentLookup(pid); v != nil {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
return rctx.DocumentContext().Document
|
||||
}
|
||||
|
||||
type codeBlockContext struct {
|
||||
page any
|
||||
lang string
|
||||
code string
|
||||
ordinal int
|
||||
page any
|
||||
pageInner any
|
||||
lang string
|
||||
code string
|
||||
ordinal int
|
||||
|
||||
// This is only used in error situations and is expensive to create,
|
||||
// to delay creation until needed.
|
||||
@@ -159,6 +172,10 @@ func (c *codeBlockContext) Page() any {
|
||||
return c.page
|
||||
}
|
||||
|
||||
func (c *codeBlockContext) PageInner() any {
|
||||
return c.pageInner
|
||||
}
|
||||
|
||||
func (c *codeBlockContext) Type() string {
|
||||
return c.lang
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gohugoio/hugo-goldmark-extensions/passthrough"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/hugocontext"
|
||||
"github.com/yuin/goldmark/util"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
|
||||
@@ -103,6 +104,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
|
||||
renderer.WithNodeRenderers(util.Prioritized(emoji.NewHTMLRenderer(), 200)))
|
||||
var (
|
||||
extensions = []goldmark.Extender{
|
||||
hugocontext.New(),
|
||||
newLinks(cfg),
|
||||
newTocExtension(tocRendererOptions),
|
||||
}
|
||||
|
||||
@@ -217,6 +217,6 @@ type Parser struct {
|
||||
type ParserAttribute struct {
|
||||
// Enables custom attributes for titles.
|
||||
Title bool
|
||||
// Enables custom attributeds for blocks.
|
||||
// Enables custom attributes for blocks.
|
||||
Block bool
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// 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 hugocontext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gohugoio/hugo/bufferpool"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
func New() goldmark.Extender {
|
||||
return &hugoContextExtension{}
|
||||
}
|
||||
|
||||
// Wrap wraps the given byte slice in a Hugo context that used to determine the correct Page
|
||||
// in .RenderShortcodes.
|
||||
func Wrap(b []byte, pid uint64) string {
|
||||
buf := bufferpool.GetBuffer()
|
||||
defer bufferpool.PutBuffer(buf)
|
||||
buf.Write(prefix)
|
||||
buf.WriteString(" pid=")
|
||||
buf.WriteString(strconv.FormatUint(pid, 10))
|
||||
buf.Write(endDelim)
|
||||
buf.WriteByte('\n')
|
||||
buf.Write(b)
|
||||
buf.Write(prefix)
|
||||
buf.Write(closingDelimAndNewline)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var kindHugoContext = ast.NewNodeKind("HugoContext")
|
||||
|
||||
// HugoContext is a node that represents a Hugo context.
|
||||
type HugoContext struct {
|
||||
ast.BaseInline
|
||||
|
||||
Closing bool
|
||||
|
||||
// Internal page ID. Not persisted.
|
||||
Pid uint64
|
||||
}
|
||||
|
||||
// Dump implements Node.Dump.
|
||||
func (n *HugoContext) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["Pid"] = fmt.Sprintf("%v", n.Pid)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
func (n *HugoContext) parseAttrs(attrBytes []byte) {
|
||||
keyPairs := bytes.Split(attrBytes, []byte(" "))
|
||||
for _, keyPair := range keyPairs {
|
||||
kv := bytes.Split(keyPair, []byte("="))
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
key := string(kv[0])
|
||||
val := string(kv[1])
|
||||
switch key {
|
||||
case "pid":
|
||||
pid, _ := strconv.ParseUint(val, 10, 64)
|
||||
n.Pid = pid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HugoContext) Kind() ast.NodeKind {
|
||||
return kindHugoContext
|
||||
}
|
||||
|
||||
var (
|
||||
prefix = []byte("{{__hugo_ctx")
|
||||
endDelim = []byte("}}")
|
||||
closingDelimAndNewline = []byte("/}}\n")
|
||||
)
|
||||
|
||||
var _ parser.InlineParser = (*hugoContextParser)(nil)
|
||||
|
||||
type hugoContextParser struct{}
|
||||
|
||||
func (s *hugoContextParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
||||
line, _ := block.PeekLine()
|
||||
if !bytes.HasPrefix(line, prefix) {
|
||||
return nil
|
||||
}
|
||||
end := bytes.Index(line, endDelim)
|
||||
if end == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
block.Advance(end + len(endDelim) + 1) // +1 for the newline
|
||||
|
||||
if line[end-1] == '/' {
|
||||
return &HugoContext{Closing: true}
|
||||
}
|
||||
|
||||
attrBytes := line[len(prefix)+1 : end]
|
||||
h := &HugoContext{}
|
||||
h.parseAttrs(attrBytes)
|
||||
return h
|
||||
}
|
||||
|
||||
func (a *hugoContextParser) Trigger() []byte {
|
||||
return []byte{'{'}
|
||||
}
|
||||
|
||||
type hugoContextRenderer struct{}
|
||||
|
||||
func (r *hugoContextRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(kindHugoContext, r.handleHugoContext)
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) handleHugoContext(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
hctx := node.(*HugoContext)
|
||||
ctx, ok := w.(*render.Context)
|
||||
if !ok {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
if hctx.Closing {
|
||||
_ = ctx.PopPid()
|
||||
} else {
|
||||
ctx.PushPid(hctx.Pid)
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
type hugoContextExtension struct{}
|
||||
|
||||
func (a *hugoContextExtension) Extend(m goldmark.Markdown) {
|
||||
m.Parser().AddOptions(
|
||||
parser.WithInlineParsers(
|
||||
util.Prioritized(&hugoContextParser{}, 50),
|
||||
),
|
||||
)
|
||||
|
||||
m.Renderer().AddOptions(
|
||||
renderer.WithNodeRenderers(
|
||||
util.Prioritized(&hugoContextRenderer{}, 50),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// 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 hugocontext
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
b := []byte("test")
|
||||
|
||||
c.Assert(Wrap(b, 42), qt.Equals, "{{__hugo_ctx pid=42}}\ntest{{__hugo_ctx/}}\n")
|
||||
}
|
||||
|
||||
func BenchmarkWrap(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Wrap([]byte("test"), 42)
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ func (b *BufWriter) Flush() error {
|
||||
type Context struct {
|
||||
*BufWriter
|
||||
positions []int
|
||||
pids []uint64
|
||||
ContextData
|
||||
}
|
||||
|
||||
@@ -55,6 +56,30 @@ func (ctx *Context) PopPos() int {
|
||||
return p
|
||||
}
|
||||
|
||||
// PushPid pushes a new page ID to the stack.
|
||||
func (ctx *Context) PushPid(pid uint64) {
|
||||
ctx.pids = append(ctx.pids, pid)
|
||||
}
|
||||
|
||||
// PeekPid returns the current page ID without removing it from the stack.
|
||||
func (ctx *Context) PeekPid() uint64 {
|
||||
if len(ctx.pids) == 0 {
|
||||
return 0
|
||||
}
|
||||
return ctx.pids[len(ctx.pids)-1]
|
||||
}
|
||||
|
||||
// PopPid pops the last page ID from the stack.
|
||||
func (ctx *Context) PopPid() uint64 {
|
||||
if len(ctx.pids) == 0 {
|
||||
return 0
|
||||
}
|
||||
i := len(ctx.pids) - 1
|
||||
p := ctx.pids[i]
|
||||
ctx.pids = ctx.pids[:i]
|
||||
return p
|
||||
}
|
||||
|
||||
type ContextData interface {
|
||||
RenderContext() converter.RenderContext
|
||||
DocumentContext() converter.DocumentContext
|
||||
|
||||
@@ -49,6 +49,7 @@ func newLinks(cfg goldmark_config.Config) goldmark.Extender {
|
||||
|
||||
type linkContext struct {
|
||||
page any
|
||||
pageInner any
|
||||
destination string
|
||||
title string
|
||||
text hstring.RenderedString
|
||||
@@ -64,6 +65,10 @@ func (ctx linkContext) Page() any {
|
||||
return ctx.page
|
||||
}
|
||||
|
||||
func (ctx linkContext) PageInner() any {
|
||||
return ctx.pageInner
|
||||
}
|
||||
|
||||
func (ctx linkContext) Text() hstring.RenderedString {
|
||||
return ctx.text
|
||||
}
|
||||
@@ -92,6 +97,7 @@ func (ctx imageLinkContext) Ordinal() int {
|
||||
|
||||
type headingContext struct {
|
||||
page any
|
||||
pageInner any
|
||||
level int
|
||||
anchor string
|
||||
text hstring.RenderedString
|
||||
@@ -103,6 +109,10 @@ func (ctx headingContext) Page() any {
|
||||
return ctx.page
|
||||
}
|
||||
|
||||
func (ctx headingContext) PageInner() any {
|
||||
return ctx.pageInner
|
||||
}
|
||||
|
||||
func (ctx headingContext) Level() int {
|
||||
return ctx.level
|
||||
}
|
||||
@@ -186,6 +196,7 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
|
||||
imageLinkContext{
|
||||
linkContext: linkContext{
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
destination: string(n.Destination),
|
||||
title: string(n.Title),
|
||||
text: hstring.RenderedString(text),
|
||||
@@ -200,6 +211,18 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
|
||||
return ast.WalkContinue, err
|
||||
}
|
||||
|
||||
func (r *hookedRenderer) getPageInner(rctx *render.Context) any {
|
||||
pid := rctx.PeekPid()
|
||||
if pid > 0 {
|
||||
if lookup := rctx.DocumentContext().DocumentLookup; lookup != nil {
|
||||
if v := rctx.DocumentContext().DocumentLookup(pid); v != nil {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
return rctx.DocumentContext().Document
|
||||
}
|
||||
|
||||
func (r *hookedRenderer) filterInternalAttributes(attrs []ast.Attribute) []ast.Attribute {
|
||||
n := 0
|
||||
for _, x := range attrs {
|
||||
@@ -274,6 +297,7 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No
|
||||
w,
|
||||
linkContext{
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
destination: string(n.Destination),
|
||||
title: string(n.Title),
|
||||
text: hstring.RenderedString(text),
|
||||
@@ -339,6 +363,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as
|
||||
w,
|
||||
linkContext{
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
destination: url,
|
||||
text: hstring.RenderedString(label),
|
||||
plainText: label,
|
||||
@@ -423,6 +448,7 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
|
||||
w,
|
||||
headingContext{
|
||||
page: ctx.DocumentContext().Document,
|
||||
pageInner: r.getPageInner(ctx),
|
||||
level: n.Level,
|
||||
anchor: string(anchor),
|
||||
text: hstring.RenderedString(text),
|
||||
|
||||
@@ -136,7 +136,7 @@ type AttributesHolder struct {
|
||||
// Attributes considered to be an option (code blocks)
|
||||
options []Attribute
|
||||
|
||||
// What we send to the the render hooks.
|
||||
// What we send to the render hooks.
|
||||
attributesMapInit sync.Once
|
||||
attributesMap map[string]any
|
||||
optionsMapInit sync.Once
|
||||
|
||||
+4
-1
@@ -261,7 +261,10 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import) (*moduleAdapt
|
||||
// This will select the latest release-version (not beta etc.).
|
||||
versionQuery = "upgrade"
|
||||
}
|
||||
if err := c.Get(fmt.Sprintf("%s@%s", modulePath, versionQuery)); err != nil {
|
||||
|
||||
// Note that we cannot use c.Get for this, as that may
|
||||
// trigger a new module collection and potentially create a infinite loop.
|
||||
if err := c.get(fmt.Sprintf("%s@%s", modulePath, versionQuery)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.loadModules(); err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
|
||||
var (
|
||||
_ error = (*errorResource)(nil)
|
||||
// Imnage covers all current Resource implementations.
|
||||
// Image covers all current Resource implementations.
|
||||
_ images.ImageResource = (*errorResource)(nil)
|
||||
// The list of user facing and exported interfaces in resource.go
|
||||
// Note that if we're missing some interface here, the user will still
|
||||
@@ -128,7 +128,7 @@ func (e *errorResource) Exif() *exif.ExifInfo {
|
||||
panic(e.ResourceError)
|
||||
}
|
||||
|
||||
func (e *errorResource) Colors() ([]string, error) {
|
||||
func (e *errorResource) Colors() ([]images.Color, error) {
|
||||
panic(e.ResourceError)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -67,7 +67,7 @@ type imageResource struct {
|
||||
meta *imageMeta
|
||||
|
||||
dominantColorInit sync.Once
|
||||
dominantColors []string
|
||||
dominantColors []images.Color
|
||||
|
||||
baseResource
|
||||
}
|
||||
@@ -143,7 +143,7 @@ func (i *imageResource) getExif() *exif.ExifInfo {
|
||||
|
||||
// Colors returns a slice of the most dominant colors in an image
|
||||
// using a simple histogram method.
|
||||
func (i *imageResource) Colors() ([]string, error) {
|
||||
func (i *imageResource) Colors() ([]images.Color, error) {
|
||||
var err error
|
||||
i.dominantColorInit.Do(func() {
|
||||
var img image.Image
|
||||
@@ -153,7 +153,7 @@ func (i *imageResource) Colors() ([]string, error) {
|
||||
}
|
||||
colors := color_extractor.ExtractColors(img)
|
||||
for _, c := range colors {
|
||||
i.dominantColors = append(i.dominantColors, images.ColorToHexString(c))
|
||||
i.dominantColors = append(i.dominantColors, images.ColorGoToColor(c))
|
||||
}
|
||||
})
|
||||
return i.dominantColors, nil
|
||||
|
||||
+29
-3
@@ -85,9 +85,16 @@ func TestImageTransformBasic(t *testing.T) {
|
||||
assertWidthHeight(c, img, w, h)
|
||||
}
|
||||
|
||||
colors, err := image.Colors()
|
||||
gotColors, err := image.Colors()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(colors, qt.DeepEquals, []string{"#2d2f33", "#a49e93", "#d39e59", "#a76936", "#737a84", "#7c838b"})
|
||||
expectedColors := images.HexStringsToColors("#2d2f33", "#a49e93", "#d39e59", "#a76936", "#737a84", "#7c838b")
|
||||
c.Assert(len(gotColors), qt.Equals, len(expectedColors))
|
||||
for i := range gotColors {
|
||||
c1, c2 := gotColors[i], expectedColors[i]
|
||||
c.Assert(c1.ColorHex(), qt.Equals, c2.ColorHex())
|
||||
c.Assert(c1.ColorGo(), qt.DeepEquals, c2.ColorGo())
|
||||
c.Assert(c1.Luminance(), qt.Equals, c2.Luminance())
|
||||
}
|
||||
|
||||
c.Assert(image.RelPermalink(), qt.Equals, "/a/sunset.jpg")
|
||||
c.Assert(image.ResourceType(), qt.Equals, "image")
|
||||
@@ -445,6 +452,24 @@ func TestImageExif(t *testing.T) {
|
||||
getAndCheckExif(c, image)
|
||||
}
|
||||
|
||||
func TestImageColorsLuminance(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
_, image := fetchSunset(c)
|
||||
c.Assert(image, qt.Not(qt.IsNil))
|
||||
colors, err := image.Colors()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(colors), qt.Equals, 6)
|
||||
var prevLuminance float64
|
||||
for i, color := range colors {
|
||||
luminance := color.Luminance()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(luminance > 0, qt.IsTrue)
|
||||
c.Assert(luminance, qt.Not(qt.Equals), prevLuminance, qt.Commentf("i=%d", i))
|
||||
prevLuminance = luminance
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkImageExif(b *testing.B) {
|
||||
getImages := func(c *qt.C, b *testing.B, fs afero.Fs) []images.ImageResource {
|
||||
spec := newTestResourceSpec(specDescriptor{fs: fs, c: c})
|
||||
@@ -509,7 +534,8 @@ func BenchmarkImageExif(b *testing.B) {
|
||||
var usesFMA = runtime.GOARCH == "s390x" ||
|
||||
runtime.GOARCH == "ppc64" ||
|
||||
runtime.GOARCH == "ppc64le" ||
|
||||
runtime.GOARCH == "arm64"
|
||||
runtime.GOARCH == "arm64" ||
|
||||
runtime.GOARCH == "riscv64"
|
||||
|
||||
// goldenEqual compares two NRGBA images. It is used in golden tests only.
|
||||
// A small tolerance is allowed on architectures using "fused multiply and add"
|
||||
|
||||
+116
-4
@@ -16,10 +16,76 @@ package images
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"image/color"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hstrings"
|
||||
)
|
||||
|
||||
type colorGoProvider interface {
|
||||
ColorGo() color.Color
|
||||
}
|
||||
|
||||
type Color struct {
|
||||
// The color.
|
||||
color color.Color
|
||||
|
||||
// The color prefixed with a #.
|
||||
hex string
|
||||
|
||||
// The relative luminance of the color.
|
||||
luminance float64
|
||||
}
|
||||
|
||||
// Luminance as defined by w3.org.
|
||||
// See https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
|
||||
func (c Color) Luminance() float64 {
|
||||
return c.luminance
|
||||
}
|
||||
|
||||
// ColorGo returns the color as a color.Color.
|
||||
// For internal use only.
|
||||
func (c Color) ColorGo() color.Color {
|
||||
return c.color
|
||||
}
|
||||
|
||||
// ColorHex returns the color as a hex string prefixed with a #.
|
||||
func (c Color) ColorHex() string {
|
||||
return c.hex
|
||||
}
|
||||
|
||||
// String returns the color as a hex string prefixed with a #.
|
||||
func (c Color) String() string {
|
||||
return c.hex
|
||||
}
|
||||
|
||||
// For hashstructure. This struct is used in template func options
|
||||
// that needs to be able to hash a Color.
|
||||
// For internal use only.
|
||||
func (c Color) Hash() (uint64, error) {
|
||||
h := fnv.New64a()
|
||||
h.Write([]byte(c.hex))
|
||||
return h.Sum64(), nil
|
||||
}
|
||||
|
||||
func (c *Color) init() error {
|
||||
c.hex = ColorGoToHexString(c.color)
|
||||
r, g, b, _ := c.color.RGBA()
|
||||
c.luminance = 0.2126*c.toSRGB(uint8(r)) + 0.7152*c.toSRGB(uint8(g)) + 0.0722*c.toSRGB(uint8(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Color) toSRGB(i uint8) float64 {
|
||||
v := float64(i) / 255
|
||||
if v <= 0.04045 {
|
||||
return v / 12.92
|
||||
} else {
|
||||
return math.Pow((v+0.055)/1.055, 2.4)
|
||||
}
|
||||
}
|
||||
|
||||
// AddColorToPalette adds c as the first color in p if not already there.
|
||||
// Note that it does no additional checks, so callers must make sure
|
||||
// that the palette is valid for the relevant format.
|
||||
@@ -45,14 +111,60 @@ func ReplaceColorInPalette(c color.Color, p color.Palette) {
|
||||
p[p.Index(c)] = c
|
||||
}
|
||||
|
||||
// ColorToHexString converts a color to a hex string.
|
||||
func ColorToHexString(c color.Color) string {
|
||||
// ColorGoToHexString converts a color.Color to a hex string.
|
||||
func ColorGoToHexString(c color.Color) string {
|
||||
r, g, b, a := c.RGBA()
|
||||
rgba := color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
|
||||
return fmt.Sprintf("#%.2x%.2x%.2x", rgba.R, rgba.G, rgba.B)
|
||||
if rgba.A == 0xff {
|
||||
return fmt.Sprintf("#%.2x%.2x%.2x", rgba.R, rgba.G, rgba.B)
|
||||
}
|
||||
return fmt.Sprintf("#%.2x%.2x%.2x%.2x", rgba.R, rgba.G, rgba.B, rgba.A)
|
||||
}
|
||||
|
||||
func hexStringToColor(s string) (color.Color, error) {
|
||||
// ColorGoToColor converts a color.Color to a Color.
|
||||
func ColorGoToColor(c color.Color) Color {
|
||||
cc := Color{color: c}
|
||||
if err := cc.init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cc
|
||||
}
|
||||
|
||||
func hexStringToColor(s string) Color {
|
||||
c, err := hexStringToColorGo(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ColorGoToColor(c)
|
||||
}
|
||||
|
||||
// HexStringsToColors converts a slice of hex strings to a slice of Colors.
|
||||
func HexStringsToColors(s ...string) []Color {
|
||||
var colors []Color
|
||||
for _, v := range s {
|
||||
colors = append(colors, hexStringToColor(v))
|
||||
}
|
||||
return colors
|
||||
}
|
||||
|
||||
func toColorGo(v any) (color.Color, bool, error) {
|
||||
switch vv := v.(type) {
|
||||
case colorGoProvider:
|
||||
return vv.ColorGo(), true, nil
|
||||
default:
|
||||
s, ok := hstrings.ToString(v)
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
c, err := hexStringToColorGo(s)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return c, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func hexStringToColorGo(s string) (color.Color, error) {
|
||||
s = strings.TrimPrefix(s, "#")
|
||||
|
||||
if len(s) != 3 && len(s) != 4 && len(s) != 6 && len(s) != 8 {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
)
|
||||
|
||||
func TestHexStringToColor(t *testing.T) {
|
||||
@@ -46,7 +47,7 @@ func TestHexStringToColor(t *testing.T) {
|
||||
c.Run(test.arg, func(c *qt.C) {
|
||||
c.Parallel()
|
||||
|
||||
result, err := hexStringToColor(test.arg)
|
||||
result, err := hexStringToColorGo(test.arg)
|
||||
|
||||
if b, ok := test.expect.(bool); ok && !b {
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
@@ -70,13 +71,18 @@ func TestColorToHexString(t *testing.T) {
|
||||
{color.White, "#ffffff"},
|
||||
{color.Black, "#000000"},
|
||||
{color.RGBA{R: 0x42, G: 0x87, B: 0xf5, A: 0xff}, "#4287f5"},
|
||||
|
||||
// 50% opacity.
|
||||
// Note that the .Colors (dominant colors) received from the Image resource
|
||||
// will always have an alpha value of 0xff.
|
||||
{color.RGBA{R: 0x42, G: 0x87, B: 0xf5, A: 0x80}, "#4287f580"},
|
||||
} {
|
||||
|
||||
test := test
|
||||
c.Run(test.expect, func(c *qt.C) {
|
||||
c.Parallel()
|
||||
|
||||
result := ColorToHexString(test.arg)
|
||||
result := ColorGoToHexString(test.arg)
|
||||
|
||||
c.Assert(result, qt.Equals, test.expect)
|
||||
})
|
||||
@@ -91,9 +97,9 @@ func TestAddColorToPalette(t *testing.T) {
|
||||
|
||||
c.Assert(AddColorToPalette(color.White, palette), qt.HasLen, 2)
|
||||
|
||||
blue1, _ := hexStringToColor("34c3eb")
|
||||
blue2, _ := hexStringToColor("34c3eb")
|
||||
white, _ := hexStringToColor("fff")
|
||||
blue1, _ := hexStringToColorGo("34c3eb")
|
||||
blue2, _ := hexStringToColorGo("34c3eb")
|
||||
white, _ := hexStringToColorGo("fff")
|
||||
|
||||
c.Assert(AddColorToPalette(white, palette), qt.HasLen, 2)
|
||||
c.Assert(AddColorToPalette(blue1, palette), qt.HasLen, 3)
|
||||
@@ -104,10 +110,18 @@ func TestReplaceColorInPalette(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
palette := color.Palette{color.White, color.Black}
|
||||
offWhite, _ := hexStringToColor("fcfcfc")
|
||||
offWhite, _ := hexStringToColorGo("fcfcfc")
|
||||
|
||||
ReplaceColorInPalette(offWhite, palette)
|
||||
|
||||
c.Assert(palette, qt.HasLen, 2)
|
||||
c.Assert(palette[0], qt.Equals, offWhite)
|
||||
}
|
||||
|
||||
func TestColorLuminance(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
c.Assert(hexStringToColor("#000000").Luminance(), hqt.IsSameFloat64, 0.0)
|
||||
c.Assert(hexStringToColor("#768a9a").Luminance(), hqt.IsSameFloat64, 0.24361603589088263)
|
||||
c.Assert(hexStringToColor("#d5bc9f").Luminance(), hqt.IsSameFloat64, 0.5261577672685374)
|
||||
c.Assert(hexStringToColor("#ffffff").Luminance(), hqt.IsSameFloat64, 1.0)
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ func DecodeConfig(in map[string]any) (*config.ConfigNamespace[ImagingConfig, Ima
|
||||
return i, nil, err
|
||||
}
|
||||
|
||||
i.BgColor, err = hexStringToColor(i.Imaging.BgColor)
|
||||
i.BgColor, err = hexStringToColorGo(i.Imaging.BgColor)
|
||||
if err != nil {
|
||||
return i, nil, err
|
||||
}
|
||||
@@ -230,7 +230,7 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN
|
||||
c.Hint = hint
|
||||
} else if part[0] == '#' {
|
||||
c.BgColorStr = part[1:]
|
||||
c.BgColor, err = hexStringToColor(c.BgColorStr)
|
||||
c.BgColor, err = hexStringToColorGo(c.BgColorStr)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
@@ -424,7 +424,7 @@ type ImagingConfigInternal struct {
|
||||
|
||||
func (i *ImagingConfigInternal) Compile(externalCfg *ImagingConfig) error {
|
||||
var err error
|
||||
i.BgColor, err = hexStringToColor(externalCfg.BgColor)
|
||||
i.BgColor, err = hexStringToColorGo(externalCfg.BgColor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ func newImageConfig(action string, width, height, quality, rotate int, filter, a
|
||||
c.qualitySetForImage = quality != 75
|
||||
c.Rotate = rotate
|
||||
c.BgColorStr = bgColor
|
||||
c.BgColor, _ = hexStringToColor(bgColor)
|
||||
c.BgColor, _ = hexStringToColorGo(bgColor)
|
||||
|
||||
if filter != "" {
|
||||
filter = strings.ToLower(filter)
|
||||
|
||||
+14
-10
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -65,7 +65,7 @@ func (*Filters) Opacity(opacity any) gift.Filter {
|
||||
func (*Filters) Text(text string, options ...any) gift.Filter {
|
||||
tf := textFilter{
|
||||
text: text,
|
||||
color: "#ffffff",
|
||||
color: color.White,
|
||||
size: 20,
|
||||
x: 10,
|
||||
y: 10,
|
||||
@@ -78,7 +78,9 @@ func (*Filters) Text(text string, options ...any) gift.Filter {
|
||||
for option, v := range opt {
|
||||
switch option {
|
||||
case "color":
|
||||
tf.color = cast.ToString(v)
|
||||
if color, ok, _ := toColorGo(v); ok {
|
||||
tf.color = color
|
||||
}
|
||||
case "size":
|
||||
tf.size = cast.ToFloat64(v)
|
||||
case "x":
|
||||
@@ -128,15 +130,14 @@ func (*Filters) Padding(args ...any) gift.Filter {
|
||||
|
||||
var top, right, bottom, left int
|
||||
var ccolor color.Color = color.White // canvas color
|
||||
var err error
|
||||
|
||||
_args := args // preserve original args for most stable hash
|
||||
|
||||
if vcs, ok := (args[len(args)-1]).(string); ok {
|
||||
ccolor, err = hexStringToColor(vcs)
|
||||
if vcs, ok, err := toColorGo(args[len(args)-1]); ok || err != nil {
|
||||
if err != nil {
|
||||
panic("invalid canvas color: specify RGB or RGBA using hex notation")
|
||||
}
|
||||
ccolor = vcs
|
||||
args = args[:len(args)-1]
|
||||
if len(args) == 0 {
|
||||
panic("not enough arguments: provide one or more padding values using the CSS shorthand property syntax")
|
||||
@@ -180,12 +181,11 @@ func (*Filters) Padding(args ...any) gift.Filter {
|
||||
// Dither creates a filter that dithers an image.
|
||||
func (*Filters) Dither(options ...any) gift.Filter {
|
||||
ditherOptions := struct {
|
||||
Colors []string
|
||||
Colors []any
|
||||
Method string
|
||||
Serpentine bool
|
||||
Strength float32
|
||||
}{
|
||||
Colors: []string{"000000ff", "ffffffff"},
|
||||
Method: "floydsteinberg",
|
||||
Serpentine: true,
|
||||
Strength: 1.0,
|
||||
@@ -198,14 +198,18 @@ func (*Filters) Dither(options ...any) gift.Filter {
|
||||
}
|
||||
}
|
||||
|
||||
if len(ditherOptions.Colors) == 0 {
|
||||
ditherOptions.Colors = []any{"000000ff", "ffffffff"}
|
||||
}
|
||||
|
||||
if len(ditherOptions.Colors) < 2 {
|
||||
panic("palette must have at least two colors")
|
||||
}
|
||||
|
||||
var palette []color.Color
|
||||
for _, c := range ditherOptions.Colors {
|
||||
cc, err := hexStringToColor(c)
|
||||
if err != nil {
|
||||
cc, ok, err := toColorGo(c)
|
||||
if !ok || err != nil {
|
||||
panic(fmt.Sprintf("%q is an invalid color: specify RGB or RGBA using hexadecimal notation", c))
|
||||
}
|
||||
palette = append(palette, cc)
|
||||
|
||||
@@ -63,7 +63,7 @@ type ImageResourceOps interface {
|
||||
|
||||
// Colors returns a slice of the most dominant colors in an image
|
||||
// using a simple histogram method.
|
||||
Colors() ([]string, error)
|
||||
Colors() ([]Color, error)
|
||||
|
||||
// For internal use.
|
||||
DecodeImage() (image.Image, error)
|
||||
|
||||
@@ -15,6 +15,7 @@ package images
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -31,7 +32,8 @@ import (
|
||||
var _ gift.Filter = (*textFilter)(nil)
|
||||
|
||||
type textFilter struct {
|
||||
text, color string
|
||||
text string
|
||||
color color.Color
|
||||
x, y int
|
||||
size float64
|
||||
linespacing int
|
||||
@@ -39,11 +41,6 @@ type textFilter struct {
|
||||
}
|
||||
|
||||
func (f textFilter) Draw(dst draw.Image, src image.Image, options *gift.Options) {
|
||||
color, err := hexStringToColor(f.color)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Load and parse font
|
||||
ttf := goregular.TTF
|
||||
if f.fontSource != nil {
|
||||
@@ -74,7 +71,7 @@ func (f textFilter) Draw(dst draw.Image, src image.Image, options *gift.Options)
|
||||
|
||||
d := font.Drawer{
|
||||
Dst: dst,
|
||||
Src: image.NewUniform(color),
|
||||
Src: image.NewUniform(f.color),
|
||||
Face: face,
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,13 @@ func CreateTargetPaths(d TargetPathDescriptor) (tp TargetPaths) {
|
||||
tp.SubResourceBaseTarget = pb.PathDir()
|
||||
tp.SubResourceBaseLink = pb.LinkDir()
|
||||
}
|
||||
|
||||
// paths.{URL,Path}Escape rely on url.Parse which
|
||||
// will consider # a fragment identifier, so it and
|
||||
// and everything after it will be stripped from
|
||||
// `link`, so we need to escape it first.
|
||||
link = strings.ReplaceAll(link, "#", "%23")
|
||||
|
||||
if d.URL != "" {
|
||||
tp.Link = paths.URLEscape(link)
|
||||
} else {
|
||||
|
||||
@@ -88,6 +88,9 @@ type PageConfig struct {
|
||||
|
||||
// User defined params.
|
||||
Params maps.Params
|
||||
|
||||
// Compiled values.
|
||||
IsGoldmark bool `json:"-"`
|
||||
}
|
||||
|
||||
// FrontMatterHandler maps front matter into Page fields and .Params.
|
||||
|
||||
@@ -71,7 +71,7 @@ func searchPageBinary(p Page, pages Pages, less func(p1, p2 Page) bool) int {
|
||||
return searchPageLinear(p, pages, i)
|
||||
}
|
||||
|
||||
// isProbablySorted tests if the pages slice is probably sorted.
|
||||
// isPagesProbablySorted tests if the pages slice is probably sorted.
|
||||
func isPagesProbablySorted(pages Pages, lessFuncs ...func(p1, p2 Page) bool) func(p1, p2 Page) bool {
|
||||
n := len(pages)
|
||||
step := 1
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// 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 page_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
// Issue 4926
|
||||
// Issue 8232
|
||||
// Issue 12342
|
||||
func TestHashSignInPermalink(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['section','rss','sitemap','taxonomy']
|
||||
[permalinks]
|
||||
s1 = '/:section/:slug'
|
||||
-- layouts/_default/list.html --
|
||||
{{ range site.Pages }}{{ .RelPermalink }}|{{ end }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
-- content/s1/p1.md --
|
||||
---
|
||||
title: p#1
|
||||
tags: test#tag#
|
||||
---
|
||||
-- content/s2/p#2.md --
|
||||
---
|
||||
title: p#2
|
||||
---
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/s1/p#1/index.html", true)
|
||||
b.AssertFileExists("public/s2/p#2/index.html", true)
|
||||
b.AssertFileExists("public/tags/test#tag#/index.html", true)
|
||||
|
||||
b.AssertFileContentExact("public/index.html", "/|/s1/p%231/|/s2/p%232/|/tags/test%23tag%23/|")
|
||||
}
|
||||
@@ -112,6 +112,14 @@ func (i Taxonomy) ByCount() OrderedTaxonomy {
|
||||
return ia
|
||||
}
|
||||
|
||||
// Page returns the taxonomy page or nil if the taxonomy has no terms.
|
||||
func (i Taxonomy) Page() Page {
|
||||
for _, v := range i {
|
||||
return v.Page().Parent()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pages returns the Pages for this taxonomy.
|
||||
func (ie OrderedTaxonomyEntry) Pages() Pages {
|
||||
return ie.WeightedPages.Pages()
|
||||
|
||||
@@ -53,3 +53,29 @@ authors: [John Smith]
|
||||
"Robert Jones count: 1",
|
||||
)
|
||||
}
|
||||
|
||||
func TestTaxonomiesPage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['rss','section','sitemap']
|
||||
[taxonomies]
|
||||
tag = 'tags'
|
||||
category = 'categories'
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
tags: [tag-a]
|
||||
---
|
||||
-- layouts/_default/list.html --
|
||||
{{- with site.Taxonomies.tags.Page }}{{ .RelPermalink }}{{ end }}|
|
||||
{{- with site.Taxonomies.categories.Page }}{{ .RelPermalink }}{{ end }}|
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "/tags/||")
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func New(rs *resources.Spec) *Client {
|
||||
|
||||
// Copy copies r to the new targetPath.
|
||||
func (c *Client) Copy(r resource.Resource, targetPath string) (resource.Resource, error) {
|
||||
key := dynacache.CleanKey(targetPath)
|
||||
key := dynacache.CleanKey(targetPath) + "__copy"
|
||||
return c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
|
||||
return resources.Copy(r, targetPath), nil
|
||||
})
|
||||
@@ -66,7 +66,7 @@ func (c *Client) Copy(r resource.Resource, targetPath string) (resource.Resource
|
||||
// Get creates a new Resource by opening the given pathname in the assets filesystem.
|
||||
func (c *Client) Get(pathname string) (resource.Resource, error) {
|
||||
pathname = path.Clean(pathname)
|
||||
key := dynacache.CleanKey(pathname)
|
||||
key := dynacache.CleanKey(pathname) + "__get"
|
||||
|
||||
return c.rs.ResourceCache.GetOrCreate(key, func() (resource.Resource, error) {
|
||||
// The resource file will not be read before it gets used (e.g. in .Content),
|
||||
|
||||
@@ -172,7 +172,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||
cmdArgs = append(cmdArgs, hexec.WithStdout(stderr))
|
||||
cmdArgs = append(cmdArgs, hexec.WithEnviron(hugo.GetExecEnviron(t.rs.Cfg.BaseConfig().WorkingDir, t.rs.Cfg, t.rs.BaseFs.Assets.Fs)))
|
||||
|
||||
defer os.Remove(compileOutput.Name())
|
||||
defer func() {
|
||||
compileOutput.Close()
|
||||
os.Remove(compileOutput.Name())
|
||||
}()
|
||||
|
||||
// ARGA [--no-install babel --config-file /private/var/folders/_g/j3j21hts4fn7__h04w2x8gb40000gn/T/hugo-test-babel812882892/babel.config.js --source-maps --filename=js/main2.js --out-file=/var/folders/_g/j3j21hts4fn7__h04w2x8gb40000gn/T/compileOut-2237820197.js]
|
||||
// [--no-install babel --config-file /private/var/folders/_g/j3j21hts4fn7__h04w2x8gb40000gn/T/hugo-test-babel332846848/babel.config.js --filename=js/main.js --out-file=/var/folders/_g/j3j21hts4fn7__h04w2x8gb40000gn/T/compileOut-1451390834.js 0x10304ee60 0x10304ed60 0x10304f060]
|
||||
|
||||
@@ -327,3 +327,34 @@ Styles: {{ $r.RelPermalink }}
|
||||
|
||||
b.AssertFileContent("public/index.html", "Styles: /scss/main.css")
|
||||
}
|
||||
|
||||
func TestRebuildAssetGetMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !scss.Supports() {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
files := `
|
||||
-- assets/scss/main.scss --
|
||||
b {
|
||||
color: red;
|
||||
}
|
||||
-- layouts/index.html --
|
||||
{{ $r := resources.GetMatch "scss/main.scss" | toCSS }}
|
||||
T1: {{ $r.Content }}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
NeedsOsFS: true,
|
||||
Running: true,
|
||||
}).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", `color: red`)
|
||||
|
||||
b.EditFiles("assets/scss/main.scss", `b { color: blue; }`).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", `color: blue`)
|
||||
}
|
||||
|
||||
@@ -227,3 +227,50 @@ eventDate: 2023-11-01T07:00:00-08:00
|
||||
|
||||
b.AssertFileContent("public/index.html", "2023-11|p9|p8|p7|2023-10|p6|p5|p4|2023-09|p3|p2|p1|")
|
||||
}
|
||||
|
||||
// Issue 10412
|
||||
func TestImageTransformThenCopy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- assets/pixel.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- layouts/index.html --
|
||||
{{- with resources.Get "pixel.png" }}
|
||||
{{- with .Resize "200x" | resources.Copy "pixel.png" }}
|
||||
<img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}">|{{ .Key }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/pixel.png", true)
|
||||
b.AssertFileContent("public/index.html",
|
||||
`<img src="/pixel.png" width="200" height="200">|/pixel.png`,
|
||||
)
|
||||
}
|
||||
|
||||
// Issue 12310
|
||||
func TestUseDifferentCacheKeyForResourceCopy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
-- assets/a.txt --
|
||||
This was assets/a.txt
|
||||
-- layouts/index.html --
|
||||
{{ $nilResource := resources.Get "/p1/b.txt" }}
|
||||
{{ $r := resources.Get "a.txt" }}
|
||||
{{ $r = resources.Copy "/p1/b.txt" $r }}
|
||||
{{ $r.RelPermalink }}
|
||||
`
|
||||
|
||||
b, err := hugolib.TestE(t, files)
|
||||
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.AssertFileContent("public/p1/b.txt", "This was assets/a.txt")
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ func (r *resourceAdapter) Exif() *exif.ExifInfo {
|
||||
return r.getImageOps().Exif()
|
||||
}
|
||||
|
||||
func (r *resourceAdapter) Colors() ([]string, error) {
|
||||
func (r *resourceAdapter) Colors() ([]images.Color, error) {
|
||||
return r.getImageOps().Colors()
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ httpget $HUGOTEST_BASEURL_0 'Title: Hugo Server Test' $HUGOTEST_BASEURL_0 'Serve
|
||||
httpget ${HUGOTEST_BASEURL_0}doesnotexist 'custom 404'
|
||||
httpget ${HUGOTEST_BASEURL_0}livereload.js 'function'
|
||||
|
||||
# By defauilt, the server renders to memory.
|
||||
# By default, the server renders to memory.
|
||||
! exists public/index.html
|
||||
|
||||
stopServer
|
||||
|
||||
+6
-2
@@ -15,12 +15,12 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/sanity-io/litter"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/yuin/goldmark/util"
|
||||
|
||||
@@ -108,7 +108,11 @@ type Namespace struct {
|
||||
// Also note that the output from Dump may change from Hugo version to the next,
|
||||
// so don't depend on a specific output.
|
||||
func (ns *Namespace) Dump(val any) string {
|
||||
return litter.Sdump(val)
|
||||
b, err := json.MarshalIndent(val, "", " ")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// VisualizeSpaces returns a string with spaces replaced by a visible string.
|
||||
|
||||
@@ -43,3 +43,33 @@ disableKinds = ["taxonomy", "term"]
|
||||
|
||||
b.AssertLogContains("timer: name foo count 5 duration")
|
||||
}
|
||||
|
||||
func TestDebugDumpPage(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableLiveReload = true
|
||||
[taxonomies]
|
||||
tag = "tags"
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: "The Index"
|
||||
date: 2012-03-15
|
||||
---
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "The First"
|
||||
tags: ["a", "b"]
|
||||
---
|
||||
-- layouts/_default/list.html --
|
||||
Dump: {{ debug.Dump . | safeHTML }}
|
||||
Dump Site: {{ debug.Dump site }}
|
||||
Dum site.Taxonomies: {{ debug.Dump site.Taxonomies | safeHTML }}
|
||||
-- layouts/_default/single.html --
|
||||
Dump: {{ debug.Dump . | safeHTML }}
|
||||
|
||||
|
||||
`
|
||||
b := hugolib.TestRunning(t, files)
|
||||
b.AssertFileContent("public/index.html", "Dump: {\n \"Date\": \"2012-03-15T00:00:00Z\"")
|
||||
}
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ func init() {
|
||||
[][2]string{
|
||||
{`{{ $m := newScratch }}
|
||||
{{ $m.Set "Hugo" "Rocks!" }}
|
||||
{{ $m.Values | debug.Dump | safeHTML }}`, "map[string]interface {}{\n \"Hugo\": \"Rocks!\",\n}"},
|
||||
{{ $m.Values | debug.Dump | safeHTML }}`, "{\n \"Hugo\": \"Rocks!\"\n}"},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+3
-3
@@ -58,7 +58,7 @@ func (ns *Namespace) Translate(ctx context.Context, id any, args ...any) (string
|
||||
|
||||
sid, err := cast.ToStringE(id)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ns.deps.Translate(ctx, sid, templateData), nil
|
||||
@@ -165,7 +165,7 @@ func (ns *Namespace) FormatNumberCustom(precision, number any, options ...any) (
|
||||
// custom delimiter
|
||||
s, err := cast.ToStringE(options[1])
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
delim = s
|
||||
@@ -173,7 +173,7 @@ func (ns *Namespace) FormatNumberCustom(precision, number any, options ...any) (
|
||||
|
||||
s, err := cast.ToStringE(options[0])
|
||||
if err != nil {
|
||||
return "", nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
rs := strings.Split(s, delim)
|
||||
|
||||
+22
-4
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
"github.com/rogpeppe/go-internal/diff"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -161,15 +162,32 @@ func (ns *Namespace) ContainsAny(s, chars any) (bool, error) {
|
||||
// ContainsNonSpace reports whether s contains any non-space characters as defined
|
||||
// by Unicode's White Space property,
|
||||
// <docsmeta>{"newIn": "0.111.0" }</docsmeta>
|
||||
func (ns *Namespace) ContainsNonSpace(s any) bool {
|
||||
ss := cast.ToString(s)
|
||||
func (ns *Namespace) ContainsNonSpace(s any) (bool, error) {
|
||||
ss, err := cast.ToStringE(s)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, r := range ss {
|
||||
if !unicode.IsSpace(r) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Diff returns an anchored diff of the two texts old and new in the “unified
|
||||
// diff” format. If old and new are identical, Diff returns an empty string.
|
||||
func (ns *Namespace) Diff(oldname string, old any, newname string, new any) (string, error) {
|
||||
olds, err := cast.ToStringE(old)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
news, err := cast.ToStringE(new)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(diff.Diff(oldname, []byte(olds), newname, []byte(news))), nil
|
||||
}
|
||||
|
||||
// HasPrefix tests whether the input s begins with prefix.
|
||||
|
||||
+57
-11
@@ -17,10 +17,9 @@ import (
|
||||
"html/template"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config/testconfig"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
@@ -154,17 +153,30 @@ func TestContainsNonSpace(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
s any
|
||||
expect bool
|
||||
isErr bool
|
||||
}{
|
||||
{"", false},
|
||||
{" ", false},
|
||||
{" ", false},
|
||||
{"\t", false},
|
||||
{"\r", false},
|
||||
{"a", true},
|
||||
{" a", true},
|
||||
{"a\n", true},
|
||||
{"", false, false},
|
||||
{" ", false, false},
|
||||
{" ", false, false},
|
||||
{"\t", false, false},
|
||||
{"\r", false, false},
|
||||
{"a", true, false},
|
||||
{" a", true, false},
|
||||
{"a\n", true, false},
|
||||
// error
|
||||
{tstNoStringer{}, false, true},
|
||||
} {
|
||||
c.Assert(ns.ContainsNonSpace(test.s), qt.Equals, test.expect)
|
||||
|
||||
result, err := ns.ContainsNonSpace(test.s)
|
||||
|
||||
if test.isErr {
|
||||
c.Assert(err, qt.IsNotNil)
|
||||
continue
|
||||
}
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(result, qt.Equals, test.expect)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,3 +820,37 @@ func TestRepeat(t *testing.T) {
|
||||
c.Assert(result, qt.Equals, test.expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
oldname string
|
||||
old any
|
||||
newname string
|
||||
new any
|
||||
expect any
|
||||
}{
|
||||
{"old", "foo\n", "new", "bar\n", "diff old new\n--- old\n+++ new\n@@ -1,1 +1,1 @@\n-foo\n+bar\n"},
|
||||
{"old", "foo\n", "new", "foo\n", ""},
|
||||
{"old", "foo\n", "new", "", "diff old new\n--- old\n+++ new\n@@ -1,1 +0,0 @@\n-foo\n"},
|
||||
{"old", "foo\n", "new", nil, "diff old new\n--- old\n+++ new\n@@ -1,1 +0,0 @@\n-foo\n"},
|
||||
{"old", "", "new", "", ""},
|
||||
// errors
|
||||
{"old", tstNoStringer{}, "new", "foo", false},
|
||||
{"old", "foo", "new", tstNoStringer{}, false},
|
||||
} {
|
||||
|
||||
result, err := ns.Diff(test.oldname, test.old, test.newname, test.new)
|
||||
|
||||
if b, ok := test.expect.(bool); ok && !b {
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
continue
|
||||
}
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(result, qt.Equals, test.expect)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,10 +165,12 @@ var Context = struct {
|
||||
SetDependencyManagerInCurrentScope func(context.Context, identity.Manager) context.Context
|
||||
DependencyScope hcontext.ContextDispatcher[int]
|
||||
Page hcontext.ContextDispatcher[page]
|
||||
IsInGoldmark hcontext.ContextDispatcher[bool]
|
||||
}{
|
||||
DependencyManagerScopedProvider: hcontext.NewContextDispatcher[identity.DependencyManagerScopedProvider](contextKey("DependencyManagerScopedProvider")),
|
||||
DependencyScope: hcontext.NewContextDispatcher[int](contextKey("DependencyScope")),
|
||||
Page: hcontext.NewContextDispatcher[page](contextKey("Page")),
|
||||
IsInGoldmark: hcontext.NewContextDispatcher[bool](contextKey("IsInGoldmark")),
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user