mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-31 18:52:38 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c3347a77f | |||
| a3a00016fb | |||
| ffb41d1111 | |||
| 5b0b663ec3 | |||
| 3f68309148 | |||
| ab03588db9 | |||
| 0450d69fc6 | |||
| 1158e63072 | |||
| d1ba52f3c3 | |||
| d0dca65625 | |||
| f30603c47f | |||
| 12c9ce34bb | |||
| 47b055589c | |||
| e56ea40666 | |||
| 4c02a52f7c | |||
| 578442f892 | |||
| 0d390d7eb8 | |||
| da72ac2db9 | |||
| 0ea796dad1 | |||
| 1e690c0f23 | |||
| a619deec18 | |||
| 5fb333b9f5 | |||
| e07028cb90 | |||
| 22a9f3fc98 | |||
| e363964f2f | |||
| c260cb28a9 | |||
| e079145373 | |||
| 5b442b3cce | |||
| 2bc27657d8 | |||
| 28f621d4a7 | |||
| fe7e137e28 | |||
| 1c74abd260 | |||
| 3d6baedaec | |||
| 84ee00bbc2 | |||
| 2f89169baa | |||
| 66a3a11001 | |||
| 8f2eac0195 | |||
| b570a5f118 | |||
| 39fd3b5570 | |||
| aacff7aff4 |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22300.20000
|
||||
- image: bepsays/ci-hugoreleaser:1.22300.20200
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20000
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20200
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
+10
-79
@@ -1,10 +1,9 @@
|
||||
name: Build Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
pull_request:
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
@@ -14,18 +13,8 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
@@ -35,9 +24,6 @@ jobs:
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
|
||||
|
||||
@@ -49,69 +35,14 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push by digest
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # v6.6.1
|
||||
with:
|
||||
context: .
|
||||
push: ${{ startsWith(github.ref, 'refs/tags') }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
provenance: mode=max
|
||||
sbom: true
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
if: ${{ startsWith(github.ref, 'refs/tags') }}
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
if: ${{ startsWith(github.ref, 'refs/tags') }}
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
+74
-24
@@ -2,44 +2,94 @@
|
||||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
FROM golang:1.22.6-alpine AS build
|
||||
ARG GO_VERSION="1.23.2"
|
||||
ARG ALPINE_VERSION=3.20
|
||||
|
||||
# Optionally set HUGO_BUILD_TAGS to "extended" or "nodeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=extended .
|
||||
ARG HUGO_BUILD_TAGS
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS gobuild
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS gorun
|
||||
|
||||
ARG CGO=1
|
||||
ENV CGO_ENABLED=${CGO}
|
||||
ENV GOOS=linux
|
||||
ENV GO111MODULE=on
|
||||
|
||||
FROM gobuild AS build
|
||||
|
||||
RUN apk add clang lld
|
||||
|
||||
# Set up cross-compilation helpers
|
||||
COPY --from=xx / /
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apk add musl-dev gcc g++
|
||||
|
||||
# Optionally set HUGO_BUILD_TAGS to "none" or "nodeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=nodeploy .
|
||||
#
|
||||
# We build the extended version by default.
|
||||
ARG HUGO_BUILD_TAGS="extended"
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GOPROXY=https://proxy.golang.org
|
||||
ENV GOCACHE=/root/.cache/go-build
|
||||
ENV GOMODCACHE=/go/pkg/mod
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
WORKDIR /go/src/github.com/gohugoio/hugo
|
||||
|
||||
COPY . /go/src/github.com/gohugoio/hugo/
|
||||
# For --mount=type=cache the value of target is the default cache id, so
|
||||
# for the go mod cache it would be good if we could share it with other Go images using the same setup,
|
||||
# but the go build cache needs to be per platform.
|
||||
# See this comment: https://github.com/moby/buildkit/issues/1706#issuecomment-702238282
|
||||
RUN --mount=target=. \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build,id=go-build-$TARGETPLATFORM <<EOT
|
||||
set -ex
|
||||
xx-go build -tags "$HUGO_BUILD_TAGS" -ldflags "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=docker" -o /usr/bin/hugo
|
||||
xx-verify /usr/bin/hugo
|
||||
EOT
|
||||
|
||||
# gcc/g++ are required to build SASS libraries for extended version
|
||||
RUN apk update && \
|
||||
apk add --no-cache gcc g++ musl-dev git && \
|
||||
go install github.com/magefile/mage
|
||||
FROM gorun AS final
|
||||
|
||||
RUN mage hugo && mage install
|
||||
COPY --from=build /usr/bin/hugo /usr/bin/hugo
|
||||
|
||||
# ---
|
||||
# libc6-compat are required for extended libraries (libsass, libwebp).
|
||||
RUN apk add --no-cache \
|
||||
libc6-compat \
|
||||
git \
|
||||
runuser \
|
||||
curl \
|
||||
nodejs \
|
||||
npm
|
||||
|
||||
FROM alpine:3.18
|
||||
RUN mkdir -p /var/hugo/bin && \
|
||||
addgroup -Sg 1000 hugo && \
|
||||
adduser -Sg hugo -u 1000 -h /var/hugo hugo && \
|
||||
chown -R hugo: /var/hugo && \
|
||||
# For the Hugo's Git integration to work.
|
||||
runuser -u hugo -- git config --global --add safe.directory /project && \
|
||||
# See https://github.com/gohugoio/hugo/issues/9810
|
||||
runuser -u hugo -- git config --global core.quotepath false
|
||||
|
||||
COPY --from=build /go/bin/hugo /usr/bin/hugo
|
||||
VOLUME /project
|
||||
WORKDIR /project
|
||||
USER hugo:hugo
|
||||
ENV HUGO_CACHEDIR=/cache
|
||||
ARG BUILDARCH
|
||||
ENV BUILDARCH=${BUILDARCH}
|
||||
ENV PATH="/var/hugo/bin:$PATH"
|
||||
|
||||
# libc6-compat & libstdc++ are required for extended SASS libraries
|
||||
# ca-certificates are required to fetch outside resources (like Twitter oEmbeds)
|
||||
RUN apk update && \
|
||||
apk add --no-cache ca-certificates libc6-compat libstdc++ git
|
||||
COPY scripts/docker scripts/docker
|
||||
COPY scripts/docker/entrypoint.sh /entrypoint.sh
|
||||
|
||||
VOLUME /site
|
||||
WORKDIR /site
|
||||
# Install default dependencies.
|
||||
RUN scripts/docker/install_runtimedeps_default.sh
|
||||
# Update PATH to reflect the new dependencies.
|
||||
# For more complex setups, we should probably find a way to
|
||||
# delegate this to the script itself, but this will have to do for now.
|
||||
# Also, the dart-sass binary is a little special, other binaries can be put/linked
|
||||
# directly in /var/hugo/bin.
|
||||
ENV PATH="/var/hugo/bin/dart-sass:$PATH"
|
||||
|
||||
# Expose port for live server
|
||||
EXPOSE 1313
|
||||
|
||||
ENTRYPOINT ["hugo"]
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["--help"]
|
||||
|
||||
|
||||
+49
-26
@@ -88,6 +88,11 @@ type commonConfig struct {
|
||||
fs *hugofs.Fs
|
||||
}
|
||||
|
||||
type configKey struct {
|
||||
counter int32
|
||||
ignoreModulesDoesNotExists bool
|
||||
}
|
||||
|
||||
// This is the root command.
|
||||
type rootCommand struct {
|
||||
Printf func(format string, v ...interface{})
|
||||
@@ -101,8 +106,8 @@ type rootCommand struct {
|
||||
|
||||
// Some, but not all commands need access to these.
|
||||
// Some needs more than one, so keep them in a small cache.
|
||||
commonConfigs *lazycache.Cache[int32, *commonConfig]
|
||||
hugoSites *lazycache.Cache[int32, *hugolib.HugoSites]
|
||||
commonConfigs *lazycache.Cache[configKey, *commonConfig]
|
||||
hugoSites *lazycache.Cache[configKey, *hugolib.HugoSites]
|
||||
|
||||
// changesFromBuild received from Hugo in watch mode.
|
||||
changesFromBuild chan []identity.Identity
|
||||
@@ -160,17 +165,18 @@ func (r *rootCommand) Commands() []simplecobra.Commander {
|
||||
return r.commands
|
||||
}
|
||||
|
||||
func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commonConfig, error) {
|
||||
cc, _, err := r.commonConfigs.GetOrCreate(key, func(key int32) (*commonConfig, error) {
|
||||
func (r *rootCommand) ConfigFromConfig(key configKey, oldConf *commonConfig) (*commonConfig, error) {
|
||||
cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) {
|
||||
fs := oldConf.fs
|
||||
configs, err := allconfig.LoadConfig(
|
||||
allconfig.ConfigSourceDescriptor{
|
||||
Flags: oldConf.cfg,
|
||||
Fs: fs.Source,
|
||||
Filename: r.cfgFile,
|
||||
ConfigDir: r.cfgDir,
|
||||
Logger: r.logger,
|
||||
Environment: r.environment,
|
||||
Flags: oldConf.cfg,
|
||||
Fs: fs.Source,
|
||||
Filename: r.cfgFile,
|
||||
ConfigDir: r.cfgDir,
|
||||
Logger: r.logger,
|
||||
Environment: r.environment,
|
||||
IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -193,11 +199,11 @@ func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commo
|
||||
return cc, err
|
||||
}
|
||||
|
||||
func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commonConfig, error) {
|
||||
func (r *rootCommand) ConfigFromProvider(key configKey, cfg config.Provider) (*commonConfig, error) {
|
||||
if cfg == nil {
|
||||
panic("cfg must be set")
|
||||
}
|
||||
cc, _, err := r.commonConfigs.GetOrCreate(key, func(key int32) (*commonConfig, error) {
|
||||
cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) {
|
||||
var dir string
|
||||
if r.source != "" {
|
||||
dir, _ = filepath.Abs(r.source)
|
||||
@@ -220,12 +226,13 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
// Load the config first to allow publishDir to be configured in config file.
|
||||
configs, err := allconfig.LoadConfig(
|
||||
allconfig.ConfigSourceDescriptor{
|
||||
Flags: cfg,
|
||||
Fs: hugofs.Os,
|
||||
Filename: r.cfgFile,
|
||||
ConfigDir: r.cfgDir,
|
||||
Environment: r.environment,
|
||||
Logger: r.logger,
|
||||
Flags: cfg,
|
||||
Fs: hugofs.Os,
|
||||
Filename: r.cfgFile,
|
||||
ConfigDir: r.cfgDir,
|
||||
Environment: r.environment,
|
||||
Logger: r.logger,
|
||||
IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -307,7 +314,8 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
}
|
||||
|
||||
func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) {
|
||||
h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) {
|
||||
k := configKey{counter: r.configVersionID.Load()}
|
||||
h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) {
|
||||
depsCfg := r.newDepsConfig(conf)
|
||||
return hugolib.NewHugoSites(depsCfg)
|
||||
})
|
||||
@@ -315,7 +323,12 @@ func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, err
|
||||
}
|
||||
|
||||
func (r *rootCommand) Hugo(cfg config.Provider) (*hugolib.HugoSites, error) {
|
||||
h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) {
|
||||
return r.getOrCreateHugo(cfg, false)
|
||||
}
|
||||
|
||||
func (r *rootCommand) getOrCreateHugo(cfg config.Provider, ignoreModuleDoesNotExist bool) (*hugolib.HugoSites, error) {
|
||||
k := configKey{counter: r.configVersionID.Load(), ignoreModulesDoesNotExists: ignoreModuleDoesNotExist}
|
||||
h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) {
|
||||
conf, err := r.ConfigFromProvider(key, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -418,11 +431,11 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
|
||||
r.changesFromBuild = make(chan []identity.Identity, 10)
|
||||
|
||||
r.commonConfigs = lazycache.New(lazycache.Options[int32, *commonConfig]{MaxEntries: 5})
|
||||
r.commonConfigs = lazycache.New(lazycache.Options[configKey, *commonConfig]{MaxEntries: 5})
|
||||
// We don't want to keep stale HugoSites in memory longer than needed.
|
||||
r.hugoSites = lazycache.New(lazycache.Options[int32, *hugolib.HugoSites]{
|
||||
r.hugoSites = lazycache.New(lazycache.Options[configKey, *hugolib.HugoSites]{
|
||||
MaxEntries: 1,
|
||||
OnEvict: func(key int32, value *hugolib.HugoSites) {
|
||||
OnEvict: func(key configKey, value *hugolib.HugoSites) {
|
||||
value.Close()
|
||||
runtime.GC()
|
||||
},
|
||||
@@ -486,16 +499,26 @@ func (r *rootCommand) IsTestRun() bool {
|
||||
}
|
||||
|
||||
func (r *rootCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
return r.initRootCommand("", cd)
|
||||
}
|
||||
|
||||
func (r *rootCommand) initRootCommand(subCommandName string, cd *simplecobra.Commandeer) error {
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Use = "hugo [flags]"
|
||||
cmd.Short = "hugo builds your site"
|
||||
cmd.Long = `hugo is the main command, used to build your Hugo site.
|
||||
commandName := "hugo"
|
||||
if subCommandName != "" {
|
||||
commandName = subCommandName
|
||||
}
|
||||
cmd.Use = fmt.Sprintf("%s [flags]", commandName)
|
||||
cmd.Short = fmt.Sprintf("%s builds your site", commandName)
|
||||
cmd.Long = `COMMAND_NAME is the main command, used to build your Hugo site.
|
||||
|
||||
Hugo is a Fast and Flexible Static Site Generator
|
||||
built with love by spf13 and friends in Go.
|
||||
|
||||
Complete documentation is available at https://gohugo.io/.`
|
||||
|
||||
cmd.Long = strings.ReplaceAll(cmd.Long, "COMMAND_NAME", commandName)
|
||||
|
||||
// Configure persistent flags
|
||||
cmd.PersistentFlags().StringVarP(&r.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
_ = cmd.MarkFlagDirname("source")
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
)
|
||||
|
||||
@@ -21,6 +23,7 @@ import (
|
||||
func newExec() (*simplecobra.Exec, error) {
|
||||
rootCmd := &rootCommand{
|
||||
commands: []simplecobra.Commander{
|
||||
newHugoBuildCmd(),
|
||||
newVersionCmd(),
|
||||
newEnvCommand(),
|
||||
newServerCommand(),
|
||||
@@ -38,3 +41,33 @@ func newExec() (*simplecobra.Exec, error) {
|
||||
|
||||
return simplecobra.New(rootCmd)
|
||||
}
|
||||
|
||||
func newHugoBuildCmd() simplecobra.Commander {
|
||||
return &hugoBuildCommand{}
|
||||
}
|
||||
|
||||
// hugoBuildCommand just delegates to the rootCommand.
|
||||
type hugoBuildCommand struct {
|
||||
rootCmd *rootCommand
|
||||
}
|
||||
|
||||
func (c *hugoBuildCommand) Commands() []simplecobra.Commander {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *hugoBuildCommand) Name() string {
|
||||
return "build"
|
||||
}
|
||||
|
||||
func (c *hugoBuildCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
c.rootCmd = cd.Root.Command.(*rootCommand)
|
||||
return c.rootCmd.initRootCommand("build", cd)
|
||||
}
|
||||
|
||||
func (c *hugoBuildCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
return c.rootCmd.PreRun(cd, runner)
|
||||
}
|
||||
|
||||
func (c *hugoBuildCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
return c.rootCmd.Run(ctx, cd, args)
|
||||
}
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ func (c *configCommand) Name() string {
|
||||
}
|
||||
|
||||
func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, nil))
|
||||
conf, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func (c *configMountsCommand) Name() string {
|
||||
|
||||
func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
r := c.configCmd.r
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -779,6 +779,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
||||
istemp := strings.HasSuffix(ext, "~") ||
|
||||
(ext == ".swp") || // vim
|
||||
(ext == ".swx") || // vim
|
||||
(ext == ".bck") || // helix
|
||||
(ext == ".tmp") || // generic temp file
|
||||
(ext == ".DS_Store") || // OSX Thumbnail
|
||||
baseName == "4913" || // vim
|
||||
@@ -1046,7 +1047,7 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
|
||||
"fastRenderMode": c.fastRenderMode,
|
||||
})
|
||||
|
||||
conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
conf, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1115,7 +1116,7 @@ func (c *hugoBuilder) reloadConfig() error {
|
||||
|
||||
if err := c.withConfE(func(conf *commonConfig) error {
|
||||
oldConf := conf
|
||||
newConf, err := c.r.ConfigFromConfig(c.r.configVersionID.Load(), conf)
|
||||
newConf, err := c.r.ConfigFromConfig(configKey{counter: c.r.configVersionID.Load()}, conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+11
-7
@@ -94,7 +94,7 @@ so this may/will change in future versions of Hugo.
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
h, err := r.getOrCreateHugo(flagsToCfg(cd, nil), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -102,7 +102,11 @@ so this may/will change in future versions of Hugo.
|
||||
if len(args) >= 1 {
|
||||
initPath = args[0]
|
||||
}
|
||||
return h.Configs.ModulesClient.Init(initPath)
|
||||
c := h.Configs.ModulesClient
|
||||
if err := c.Init(initPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -115,7 +119,7 @@ so this may/will change in future versions of Hugo.
|
||||
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,7 +139,7 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -271,7 +275,7 @@ Run "go help get" for more information. All flags available for "go get" is also
|
||||
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", dir)
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Add(1), flagsToCfg(cd, cfg))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Add(1)}, flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -284,7 +288,7 @@ Run "go help get" for more information. All flags available for "go get" is also
|
||||
})
|
||||
return nil
|
||||
} else {
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -313,7 +317,7 @@ func (c *modCommands) Name() string {
|
||||
}
|
||||
|
||||
func (c *modCommands) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
_, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), nil)
|
||||
_, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+2
-2
@@ -93,7 +93,7 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
cfg.Set("workingDir", createpath)
|
||||
cfg.Set("publishDir", "public")
|
||||
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -136,7 +136,7 @@ according to your needs.`,
|
||||
cfg := config.New()
|
||||
cfg.Set("publishDir", "public")
|
||||
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 134,
|
||||
Minor: 136,
|
||||
PatchLevel: 0,
|
||||
Suffix: "",
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -809,7 +809,7 @@ func (c *Configs) Init() error {
|
||||
}
|
||||
|
||||
if len(c.Modules) == 0 {
|
||||
return errors.New("no modules loaded (ned at least the main module)")
|
||||
return errors.New("no modules loaded (need at least the main module)")
|
||||
}
|
||||
|
||||
// Apply default project mounts.
|
||||
|
||||
@@ -103,31 +103,7 @@ suffixes = ["html", "xhtml"]
|
||||
b.Assert(contentTypes.Markdown.Suffixes(), qt.DeepEquals, []string{"md", "mdown", "markdown"})
|
||||
}
|
||||
|
||||
func TestPaginationConfigOld(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
[languages.en]
|
||||
weight = 1
|
||||
paginatePath = "page-en"
|
||||
|
||||
[languages.de]
|
||||
weight = 2
|
||||
paginatePath = "page-de"
|
||||
paginate = 20
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
confEn := b.H.Sites[0].Conf.Pagination()
|
||||
confDe := b.H.Sites[1].Conf.Pagination()
|
||||
|
||||
b.Assert(confEn.Path, qt.Equals, "page-en")
|
||||
b.Assert(confEn.PagerSize, qt.Equals, 10)
|
||||
b.Assert(confDe.Path, qt.Equals, "page-de")
|
||||
b.Assert(confDe.PagerSize, qt.Equals, 20)
|
||||
}
|
||||
|
||||
func TestPaginationConfigNew(t *testing.T) {
|
||||
func TestPaginationConfig(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
[languages.en]
|
||||
|
||||
+17
-13
@@ -64,7 +64,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
|
||||
return nil, fmt.Errorf("failed to create config from result: %w", err)
|
||||
}
|
||||
|
||||
moduleConfig, modulesClient, err := l.loadModules(configs)
|
||||
moduleConfig, modulesClient, err := l.loadModules(configs, d.IgnoreModuleDoesNotExist)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load modules: %w", err)
|
||||
}
|
||||
@@ -116,6 +116,9 @@ type ConfigSourceDescriptor struct {
|
||||
|
||||
// Defaults to os.Environ if not set.
|
||||
Environ []string
|
||||
|
||||
// If set, this will be used to ignore the module does not exist error.
|
||||
IgnoreModuleDoesNotExist bool
|
||||
}
|
||||
|
||||
func (d ConfigSourceDescriptor) configFilenames() []string {
|
||||
@@ -453,7 +456,7 @@ func (l *configLoader) loadConfigMain(d ConfigSourceDescriptor) (config.LoadConf
|
||||
return res, l.ModulesConfig, err
|
||||
}
|
||||
|
||||
func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *modules.Client, error) {
|
||||
func (l *configLoader) loadModules(configs *Configs, ignoreModuleDoesNotExist bool) (modules.ModulesConfig, *modules.Client, error) {
|
||||
bcfg := configs.LoadingInfo.BaseConfig
|
||||
conf := configs.Base
|
||||
workingDir := bcfg.WorkingDir
|
||||
@@ -487,17 +490,18 @@ func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *mo
|
||||
}
|
||||
|
||||
modulesClient := modules.NewClient(modules.ClientConfig{
|
||||
Fs: l.Fs,
|
||||
Logger: l.Logger,
|
||||
Exec: ex,
|
||||
HookBeforeFinalize: hook,
|
||||
WorkingDir: workingDir,
|
||||
ThemesDir: themesDir,
|
||||
PublishDir: publishDir,
|
||||
Environment: l.Environment,
|
||||
CacheDir: conf.Caches.CacheDirModules(),
|
||||
ModuleConfig: conf.Module,
|
||||
IgnoreVendor: ignoreVendor,
|
||||
Fs: l.Fs,
|
||||
Logger: l.Logger,
|
||||
Exec: ex,
|
||||
HookBeforeFinalize: hook,
|
||||
WorkingDir: workingDir,
|
||||
ThemesDir: themesDir,
|
||||
PublishDir: publishDir,
|
||||
Environment: l.Environment,
|
||||
CacheDir: conf.Caches.CacheDirModules(),
|
||||
ModuleConfig: conf.Module,
|
||||
IgnoreVendor: ignoreVendor,
|
||||
IgnoreModuleDoesNotExist: ignoreModuleDoesNotExist,
|
||||
})
|
||||
|
||||
moduleConfig, err := modulesClient.Collect()
|
||||
|
||||
@@ -49,7 +49,7 @@ toc: true
|
||||
: Leverage the embedded Markdown extensions to create tables, definition lists, footnotes, task lists, inserted text, mark text, subscripts, superscripts, and more.
|
||||
|
||||
[Markdown render hooks]
|
||||
: Override the conversion of Markdown to HTML when rendering blockquotes, fenced code blocks, headings, images, and links. For example, render every standalone image as an HTML `figure` element.
|
||||
: Override the conversion of Markdown to HTML when rendering blockquotes, fenced code blocks, headings, images, links, and tables. For example, render every standalone image as an HTML `figure` element.
|
||||
|
||||
[Diagrams]
|
||||
: Use fenced code blocks and Markdown render hooks to include diagrams in your content.
|
||||
|
||||
@@ -70,6 +70,7 @@ hugo [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo build](/commands/hugo_build/) - build builds your site
|
||||
* [hugo completion](/commands/hugo_completion/) - Generate the autocompletion script for the specified shell
|
||||
* [hugo config](/commands/hugo_config/) - Print the site configuration
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: "hugo build"
|
||||
slug: hugo_build
|
||||
url: /commands/hugo_build/
|
||||
---
|
||||
## hugo build
|
||||
|
||||
build builds your site
|
||||
|
||||
### Synopsis
|
||||
|
||||
build is the main command, used to build your Hugo site.
|
||||
|
||||
Hugo is a Fast and Flexible Static Site Generator
|
||||
built with love by spf13 and friends in Go.
|
||||
|
||||
Complete documentation is available at https://gohugo.io/.
|
||||
|
||||
```
|
||||
hugo build [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
-D, --buildDrafts include content marked as draft
|
||||
-E, --buildExpired include expired content
|
||||
-F, --buildFuture include content with publishdate in the future
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
|
||||
--config string config file (default is hugo.yaml|json|toml)
|
||||
--configDir string config dir (default "config")
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--debug debug output
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableKinds strings disable different kind of pages (home, RSS etc.)
|
||||
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
|
||||
-e, --environment string build environment
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for build
|
||||
--ignoreCache ignores the cache directory
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--minify minify any supported output format (HTML, XML etc.)
|
||||
--noBuildLock don't create .hugo_build.lock file
|
||||
--noChmod don't sync permission mode of files
|
||||
--noTimes don't sync modification time of files
|
||||
--panicOnWarning panic on first WARNING log
|
||||
--poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes
|
||||
--printI18nWarnings print missing translations
|
||||
--printMemoryUsage print memory usage to screen at intervals
|
||||
--printPathWarnings print warnings on duplicate target paths etc.
|
||||
--printUnusedTemplates print warnings on unused templates.
|
||||
--quiet build in quiet mode
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-M, --renderToMemory render to memory (mostly useful when running the server)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
--trace file write trace to file (not useful in general)
|
||||
-v, --verbose verbose output
|
||||
-w, --watch watch filesystem for changes and recreate as needed
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
@@ -92,11 +92,11 @@ Note that the `summaryLength` is an approximate number of words.
|
||||
|
||||
Each summary type has different characteristics:
|
||||
|
||||
Type|Precedence|Renders markdown|Renders shortcodes|Strips HTML tags|Wraps single lines with `<p>`
|
||||
:--|:-:|:-:|:-:|:-:|:-:
|
||||
Manual|1|:heavy_check_mark:|:heavy_check_mark:|:x:|:heavy_check_mark:
|
||||
Front matter|2|:heavy_check_mark:|:x:|:x:|:x:
|
||||
Automatic|3|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:|:x:
|
||||
Type|Precedence|Renders markdown|Renders shortcodes|Wraps single lines with `<p>`
|
||||
:--|:-:|:-:|:-:|:-:
|
||||
Manual|1|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:
|
||||
Front matter|2|:heavy_check_mark:|:x:|:x:
|
||||
Automatic|3|:heavy_check_mark:|:heavy_check_mark:|:heavy_check_mark:
|
||||
|
||||
## Rendering
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ categories: []
|
||||
keywords: []
|
||||
action:
|
||||
related:
|
||||
- methods/page/Summary
|
||||
- methods/page/ContentWithoutSummary
|
||||
- methods/page/RawContent
|
||||
- methods/page/Plain
|
||||
- methods/page/PlainWords
|
||||
@@ -13,9 +15,7 @@ action:
|
||||
signatures: [PAGE.Content]
|
||||
---
|
||||
|
||||
The `Content` method on a `Page` object renders Markdown and shortcodes to HTML. The content does not include front matter.
|
||||
|
||||
[shortcodes]: /getting-started/glossary/#shortcode
|
||||
The `Content` method on a `Page` object renders Markdown and shortcodes to HTML.
|
||||
|
||||
```go-html-template
|
||||
{{ .Content }}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: ContentWithoutSummary
|
||||
description: Returns the rendered content of the given page, excluding the content summary.
|
||||
categories: []
|
||||
keywords: []
|
||||
action:
|
||||
related:
|
||||
- methods/page/Content
|
||||
- methods/page/Summary
|
||||
- methods/page/RawContent
|
||||
- methods/page/Plain
|
||||
- methods/page/PlainWords
|
||||
- methods/page/RenderShortcodes
|
||||
returnType: template.HTML
|
||||
signatures: [PAGE.ContentWithoutSummary]
|
||||
---
|
||||
|
||||
{{< new-in 0.134.0 >}}
|
||||
|
||||
Applicable when using manual or automatic [content summaries], the `ContentWithoutSummary` method on a `Page` object renders Markdown and shortcodes to HTML, excluding the content summary from the result.
|
||||
|
||||
[content summaries]: /content-management/summaries/#manual-summary
|
||||
|
||||
```go-html-template
|
||||
{{ .ContentWithoutSummary }}
|
||||
```
|
||||
|
||||
The `ContentWithoutSummary` method returns an empty string if you define the content summary in front matter.
|
||||
@@ -6,6 +6,8 @@ keywords: []
|
||||
action:
|
||||
related:
|
||||
- methods/page/Content
|
||||
- methods/page/Summary
|
||||
- methods/page/ContentWithoutSummary
|
||||
- methods/page/RawContent
|
||||
- methods/page/PlainWords
|
||||
- methods/page/RenderShortcodes
|
||||
@@ -13,7 +15,7 @@ action:
|
||||
signatures: [PAGE.Plain]
|
||||
---
|
||||
|
||||
The `Plain` method on a `Page` object renders Markdown and [shortcodes] to HTML, then strips the HTML [tags]. It does not strip HTML [entities]. The plain content does not include front matter.
|
||||
The `Plain` method on a `Page` object renders Markdown and [shortcodes] to HTML, then strips the HTML [tags]. It does not strip HTML [entities].
|
||||
|
||||
To prevent Go's [html/template] package from escaping HTML entities, pass the result through the [`htmlUnescape`] function.
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@ keywords: []
|
||||
action:
|
||||
related:
|
||||
- methods/page/Content
|
||||
- methods/page/Summary
|
||||
- methods/page/ContentWithoutSummary
|
||||
- methods/page/RawContent
|
||||
- methods/page/Plain
|
||||
- methods/page/RenderShortcodes
|
||||
returnType: '[]string'
|
||||
signatures: [PAGE.PlainWords]
|
||||
---
|
||||
|
||||
@@ -6,6 +6,8 @@ keywords: []
|
||||
action:
|
||||
related:
|
||||
- methods/page/Content
|
||||
- methods/page/Summary
|
||||
- methods/page/ContentWithoutSummary
|
||||
- methods/page/Plain
|
||||
- methods/page/PlainWords
|
||||
- methods/page/RenderShortcodes
|
||||
|
||||
@@ -5,11 +5,13 @@ categories: []
|
||||
keywords: []
|
||||
action:
|
||||
related:
|
||||
- methods/page/RenderString
|
||||
- methods/page/Content
|
||||
- methods/page/Summary
|
||||
- methods/page/ContentWithoutSummary
|
||||
- methods/page/RawContent
|
||||
- methods/page/Plain
|
||||
- methods/page/PlainWords
|
||||
- methods/page/RenderString
|
||||
returnType: template.HTML
|
||||
signatures: [PAGE.RenderShortcodes]
|
||||
toc: true
|
||||
|
||||
@@ -28,15 +28,13 @@ Blockquote render hook templates receive the following [context]:
|
||||
|
||||
{{< new-in 0.134.0 >}}
|
||||
|
||||
(`hstring.HTML`) Applicable when [`Type`](#type) is `alert` when using [Obsidian callouts] syntax, this is the alert title converted to HTML.
|
||||
(`template.HTML`) Applicable when [`Type`](#type) is `alert`, this is the alert title. See the [alerts](#alerts) section below.
|
||||
|
||||
###### AlertSign
|
||||
|
||||
{{< new-in 0.134.0 >}}
|
||||
|
||||
(`string`) Applicable when [`Type`](#type) is `alert` when using [Obsidian callouts] syntax, this is one of "+", "-" or "" (empty string) to indicate the presence of a foldable sign.
|
||||
|
||||
[Obsidian callouts]: https://help.obsidian.md/Editing+and+formatting/Callouts
|
||||
(`string`) Applicable when [`Type`](#type) is `alert`, this is the alert sign. Typically used to indicate whether an alert is graphically foldable, this is one of `+`, `-`, or an empty string. See the [alerts](#alerts) section below.
|
||||
|
||||
###### Attributes
|
||||
|
||||
@@ -59,7 +57,7 @@ block = true
|
||||
|
||||
###### PageInner
|
||||
|
||||
(`page`) A reference to a page nested via the [`RenderShortcodes`] method.
|
||||
(`page`) A reference to a page nested via the [`RenderShortcodes`] method. [See details](#pageinner-details).
|
||||
|
||||
[`RenderShortcodes`]: /methods/page/rendershortcodes
|
||||
|
||||
@@ -68,7 +66,7 @@ block = true
|
||||
(`string`) The position of the blockquote within the page content.
|
||||
|
||||
###### Text
|
||||
(`string`) The blockquote text, excluding the alert designator if present. See the [alerts](#alerts) section below.
|
||||
(`template.HTML`) The blockquote text, excluding the first line if [`Type`](#type) is `alert`. See the [alerts](#alerts) section below.
|
||||
|
||||
###### Type
|
||||
|
||||
@@ -82,7 +80,7 @@ In its default configuration, Hugo renders Markdown blockquotes according to the
|
||||
|
||||
{{< code file=layouts/_default/_markup/render-blockquote.html copy=true >}}
|
||||
<blockquote>
|
||||
{{ .Text | safeHTML }}
|
||||
{{ .Text }}
|
||||
</blockquote>
|
||||
{{< /code >}}
|
||||
|
||||
@@ -91,7 +89,7 @@ To render a blockquote as an HTML `figure` element with an optional citation and
|
||||
{{< code file=layouts/_default/_markup/render-blockquote.html copy=true >}}
|
||||
<figure>
|
||||
<blockquote {{ with .Attributes.cite }}cite="{{ . }}"{{ end }}>
|
||||
{{ .Text | safeHTML }}
|
||||
{{ .Text }}
|
||||
</blockquote>
|
||||
{{ with .Attributes.caption }}
|
||||
<figcaption class="blockquote-caption">
|
||||
@@ -110,7 +108,11 @@ Then in your markdown:
|
||||
|
||||
## Alerts
|
||||
|
||||
Also known as _callouts_ or _admonitions_, alerts are blockquotes used to emphasize critical information. For example:
|
||||
Also known as _callouts_ or _admonitions_, alerts are blockquotes used to emphasize critical information.
|
||||
|
||||
### Basic syntax
|
||||
|
||||
With the basic Markdown syntax, the first line of each alert is an alert designator consisting of an exclamation point followed by the alert type, wrapped within brackets. For example:
|
||||
|
||||
{{< code file=content/example.md lang=text >}}
|
||||
> [!NOTE]
|
||||
@@ -129,15 +131,30 @@ Also known as _callouts_ or _admonitions_, alerts are blockquotes used to emphas
|
||||
> Advises about risks or negative outcomes of certain actions.
|
||||
{{< /code >}}
|
||||
|
||||
The basic syntax is compatible with [GitHub], [Obsidian], and [Typora].
|
||||
|
||||
[GitHub]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
|
||||
[Obsidian]: https://help.obsidian.md/Editing+and+formatting/Callouts
|
||||
[Typora]: https://support.typora.io/Markdown-Reference/#callouts--github-style-alerts
|
||||
|
||||
### Extended syntax
|
||||
|
||||
With the extended Markdown syntax, you may optionally include an alert sign and/or an alert title. The alert sign is one of `+` or `-`, typically used to indicate whether an alert is graphically foldable. For example:
|
||||
|
||||
{{< code file=content/example.md lang=text >}}
|
||||
> [!WARNING]+ Radiation hazard
|
||||
> Do not approach or handle without protective gear.
|
||||
{{< /code >}}
|
||||
|
||||
The extended syntax is compatible with [Obsidian].
|
||||
|
||||
{{% note %}}
|
||||
This syntax is compatible with both the GitHub Alert Markdown extension and Obsidian's callout syntax.
|
||||
But note that GitHub will not recognize callouts with one of Obsidian's extensions (e.g. callout title or the foldable sign).
|
||||
The extended syntax is not compatible with GitHub or Typora. If you include an alert sign or an alert title, these applications render the Markdown as a blockquote.
|
||||
{{% /note %}}
|
||||
|
||||
The first line of each alert is an alert designator consisting of an exclamation point followed by the alert type, wrapped within brackets.
|
||||
### Example
|
||||
|
||||
The blockquote render hook below renders a multilingual alert if an alert designator is present, otherwise it renders a blockquote according to the CommonMark specification.
|
||||
This blockquote render hook renders a multilingual alert if an alert designator is present, otherwise it renders a blockquote according to the CommonMark specification.
|
||||
|
||||
{{< code file=layouts/_default/_markup/render-blockquote.html copy=true >}}
|
||||
{{ $emojis := dict
|
||||
@@ -152,13 +169,17 @@ The blockquote render hook below renders a multilingual alert if an alert design
|
||||
<blockquote class="alert alert-{{ .AlertType }}">
|
||||
<p class="alert-heading">
|
||||
{{ transform.Emojify (index $emojis .AlertType) }}
|
||||
{{ or (i18n .AlertType) (title .AlertType) }}
|
||||
{{ with .AlertTitle }}
|
||||
{{ . }}
|
||||
{{ else }}
|
||||
{{ or (i18n .AlertType) (title .AlertType) }}
|
||||
{{ end }}
|
||||
</p>
|
||||
{{ .Text | safeHTML }}
|
||||
{{ .Text }}
|
||||
</blockquote>
|
||||
{{ else }}
|
||||
<blockquote>
|
||||
{{ .Text | safeHTML }}
|
||||
{{ .Text }}
|
||||
</blockquote>
|
||||
{{ end }}
|
||||
{{< /code >}}
|
||||
@@ -173,7 +194,6 @@ tip = 'Tip'
|
||||
warning = 'Warning'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
|
||||
Although you can use one template with conditional logic as shown above, you can also create separate templates for each [`Type`](#type) of blockquote:
|
||||
|
||||
```text
|
||||
@@ -183,3 +203,5 @@ layouts/
|
||||
├── render-blockquote-alert.html
|
||||
└── render-blockquote-regular.html
|
||||
```
|
||||
|
||||
{{% include "/render-hooks/_common/pageinner.md" %}}
|
||||
|
||||
@@ -55,7 +55,7 @@ title = true
|
||||
|
||||
###### Text
|
||||
|
||||
(`string`) The heading text.
|
||||
(`template.HTML`) The heading text.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -65,7 +65,7 @@ In its default configuration, Hugo renders Markdown headings according to the [C
|
||||
|
||||
{{< code file=layouts/_default/_markup/render-heading.html copy=true >}}
|
||||
<h{{ .Level }} id="{{ .Anchor }}">
|
||||
{{- .Text | safeHTML -}}
|
||||
{{- .Text -}}
|
||||
</h{{ .Level }}>
|
||||
{{< /code >}}
|
||||
|
||||
@@ -73,7 +73,7 @@ To add an anchor link to the right of each heading:
|
||||
|
||||
{{< code file=layouts/_default/_markup/render-heading.html copy=true >}}
|
||||
<h{{ .Level }} id="{{ .Anchor }}">
|
||||
{{ .Text | safeHTML }}
|
||||
{{ .Text }}
|
||||
<a href="#{{ .Anchor }}">#</a>
|
||||
</h{{ .Level }}>
|
||||
{{< /code >}}
|
||||
|
||||
@@ -73,7 +73,7 @@ block = true
|
||||
|
||||
###### Text
|
||||
|
||||
(`string`) The image description.
|
||||
(`template.HTML`) The image description.
|
||||
|
||||
###### Title
|
||||
|
||||
@@ -143,7 +143,7 @@ The embedded image render hook is automatically enabled for multilingual single-
|
||||
[duplication of shared page resources]: /getting-started/configuration-markup/#duplicateresourcefiles
|
||||
{{% /note %}}
|
||||
|
||||
The embedded image render hook resolves internal Markdown destinations by looking for a matching [page resource], falling back to a matching [global resource]. Remote destinations are passed through, and the render hook will not throw an error or warning if it is unable to resolve a destination.
|
||||
The embedded image render hook resolves internal Markdown destinations by looking for a matching [page resource], falling back to a matching [global resource]. Remote destinations are passed through, and the render hook will not throw an error or warning if unable to resolve a destination.
|
||||
|
||||
[page resource]: /getting-started/glossary/#page-resource
|
||||
[global resource]: /getting-started/glossary/#global-resource
|
||||
|
||||
@@ -19,6 +19,7 @@ When rendering Markdown to HTML, render hooks override the conversion. Each rend
|
||||
- [Images](/render-hooks/images)
|
||||
- [Links](/render-hooks/links)
|
||||
- [Passthrough elements](/render-hooks/passthrough)
|
||||
- [Tables](/render-hooks/tables)
|
||||
|
||||
{{% note %}}
|
||||
Hugo supports multiple [content formats] including Markdown, HTML, AsciiDoc, Emacs Org Mode, Pandoc, and reStructuredText.
|
||||
@@ -60,7 +61,9 @@ layouts/
|
||||
├── render-codeblock.html
|
||||
├── render-heading.html
|
||||
├── render-image.html
|
||||
└── render-link.html
|
||||
├── render-link.html
|
||||
├── render-passthrough.html
|
||||
└── render-table.html
|
||||
```
|
||||
|
||||
The template lookup order allows you to create different render hooks for each page [type], [kind], language, and [output format]. For example:
|
||||
|
||||
@@ -54,7 +54,7 @@ Link render hook templates receive the following context:
|
||||
|
||||
###### Text
|
||||
|
||||
(`string`) The link description.
|
||||
(`template.HTML`) The link description.
|
||||
|
||||
###### Title
|
||||
|
||||
@@ -74,7 +74,7 @@ In its default configuration, Hugo renders Markdown links according to the [Comm
|
||||
<a href="{{ .Destination | safeURL }}"
|
||||
{{- with .Title }} title="{{ . }}"{{ end -}}
|
||||
>
|
||||
{{- with .Text | safeHTML }}{{ . }}{{ end -}}
|
||||
{{- with .Text }}{{ . }}{{ end -}}
|
||||
</a>
|
||||
{{- /* chomp trailing newline */ -}}
|
||||
{{< /code >}}
|
||||
@@ -87,7 +87,7 @@ To include a `rel` attribute set to `external` for external links:
|
||||
{{- with .Title }} title="{{ . }}"{{ end -}}
|
||||
{{- if $u.IsAbs }} rel="external"{{ end -}}
|
||||
>
|
||||
{{- with .Text | safeHTML }}{{ . }}{{ end -}}
|
||||
{{- with .Text }}{{ . }}{{ end -}}
|
||||
</a>
|
||||
{{- /* chomp trailing newline */ -}}
|
||||
{{< /code >}}
|
||||
@@ -113,7 +113,7 @@ The embedded link render hook is automatically enabled for multilingual single-h
|
||||
[duplication of shared page resources]: /getting-started/configuration-markup/#duplicateresourcefiles
|
||||
{{% /note %}}
|
||||
|
||||
The embedded link render hook resolves internal Markdown destinations by looking for a matching page, falling back to a matching [page resource], then falling back to a matching [global resource]. Remote destinations are passed through, and the render hook will not throw an error or warning if it is unable to resolve a destination.
|
||||
The embedded link render hook resolves internal Markdown destinations by looking for a matching page, falling back to a matching [page resource], then falling back to a matching [global resource]. Remote destinations are passed through, and the render hook will not throw an error or warning if unable to resolve a destination.
|
||||
|
||||
[page resource]: /getting-started/glossary/#page-resource
|
||||
[global resource]: /getting-started/glossary/#global-resource
|
||||
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Table render hooks
|
||||
linkTitle: Tables
|
||||
description: Create a table render hook to override the rendering of Markdown tables to HTML.
|
||||
categories: [render hooks]
|
||||
keywords: []
|
||||
menu:
|
||||
docs:
|
||||
parent: render-hooks
|
||||
weight: 90
|
||||
weight: 90
|
||||
toc: true
|
||||
---
|
||||
|
||||
{{< new-in 0.134.0 >}}
|
||||
|
||||
## Context
|
||||
|
||||
Table render hook templates receive the following [context]:
|
||||
|
||||
[context]: /getting-started/glossary/#context
|
||||
|
||||
###### Attributes
|
||||
|
||||
(`map`) The [Markdown attributes], available if you configure your site as follows:
|
||||
|
||||
[Markdown attributes]: /content-management/markdown-attributes/
|
||||
|
||||
{{< code-toggle file=hugo >}}
|
||||
[markup.goldmark.parser.attribute]
|
||||
block = true
|
||||
{{< /code-toggle >}}
|
||||
|
||||
###### Ordinal
|
||||
|
||||
(`int`) The zero-based ordinal of the table on the page.
|
||||
|
||||
###### Page
|
||||
|
||||
(`page`) A reference to the current page.
|
||||
|
||||
###### PageInner
|
||||
|
||||
(`page`) A reference to a page nested via the [`RenderShortcodes`] method. [See details](#pageinner-details).
|
||||
|
||||
[`RenderShortcodes`]: /methods/page/rendershortcodes
|
||||
|
||||
###### Position
|
||||
|
||||
(`string`) The position of the table within the page content.
|
||||
|
||||
###### THead
|
||||
(`slice`) A slice of table header rows, where each element is a slice of table cells.
|
||||
|
||||
###### TBody
|
||||
(`slice`) A slice of table body rows, where each element is a slice of table cells.
|
||||
|
||||
## Table cells
|
||||
|
||||
Each table cell within the slice of slices returned by the `THead` and `TBody` methods has the following fields:
|
||||
|
||||
###### Alignment
|
||||
(`string`) The alignment of the text within the table cell, one of `left`, `center`, or `right`.
|
||||
|
||||
###### Text
|
||||
(`template.HTML`) The text within the table cell.
|
||||
|
||||
## Example
|
||||
|
||||
In its default configuration, Hugo renders Markdown tables according to the [GitHub Flavored Markdown specification]. To create a render hook that does the same thing:
|
||||
|
||||
[GitHub Flavored Markdown specification]: https://github.github.com/gfm/#tables-extension-
|
||||
|
||||
{{< code file=layouts/_default/_markup/render-table.html copy=true >}}
|
||||
<table
|
||||
{{- range $k, $v := .Attributes }}
|
||||
{{- if $v }}
|
||||
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
|
||||
{{- end }}
|
||||
{{- end }}>
|
||||
<thead>
|
||||
{{- range .THead }}
|
||||
<tr>
|
||||
{{- range . }}
|
||||
<th {{ printf "style=%q" (printf "text-align: %s" .Alignment) | safeHTMLAttr }}>
|
||||
{{- .Text -}}
|
||||
</th>
|
||||
{{- end }}
|
||||
</tr>
|
||||
{{- end }}
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- range .TBody }}
|
||||
<tr>
|
||||
{{- range . }}
|
||||
<td {{ printf "style=%q" (printf "text-align: %s" .Alignment) | safeHTMLAttr }}>
|
||||
{{- .Text -}}
|
||||
</td>
|
||||
{{- end }}
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{< /code >}}
|
||||
|
||||
{{% include "/render-hooks/_common/pageinner.md" %}}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
command = "hugo --gc --minify"
|
||||
|
||||
[build.environment]
|
||||
HUGO_VERSION = "0.133.1"
|
||||
HUGO_VERSION = "0.134.0"
|
||||
|
||||
[context.production.environment]
|
||||
HUGO_ENV = "production"
|
||||
|
||||
@@ -14,7 +14,7 @@ require (
|
||||
github.com/bep/godartsass/v2 v2.1.0
|
||||
github.com/bep/golibsass v1.2.0
|
||||
github.com/bep/gowebp v0.3.0
|
||||
github.com/bep/helpers v0.4.0
|
||||
github.com/bep/helpers v0.5.0
|
||||
github.com/bep/imagemeta v0.8.1
|
||||
github.com/bep/lazycache v0.4.0
|
||||
github.com/bep/logg v0.4.0
|
||||
@@ -34,7 +34,7 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/getkin/kin-openapi v0.123.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gobuffalo/flect v1.0.2
|
||||
github.com/gobuffalo/flect v1.0.3
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e
|
||||
github.com/gohugoio/hashstructure v0.1.0
|
||||
@@ -46,7 +46,7 @@ require (
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hairyhenderson/go-codeowners v0.5.0
|
||||
github.com/hairyhenderson/go-codeowners v0.6.0
|
||||
github.com/jdkato/prose v1.2.1
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/kyokomi/emoji/v2 v2.2.13
|
||||
@@ -75,11 +75,11 @@ require (
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.39.0
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/image v0.19.0
|
||||
golang.org/x/mod v0.19.0
|
||||
golang.org/x/net v0.28.0
|
||||
golang.org/x/image v0.20.0
|
||||
golang.org/x/mod v0.21.0
|
||||
golang.org/x/net v0.29.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/text v0.17.0
|
||||
golang.org/x/text v0.18.0
|
||||
golang.org/x/tools v0.23.0
|
||||
google.golang.org/api v0.191.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -154,9 +154,9 @@ require (
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.22.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect
|
||||
|
||||
@@ -133,14 +133,12 @@ github.com/bep/godartsass v1.2.0 h1:E2VvQrxAHAFwbjyOIExAMmogTItSKodoKuijNrGm5yU=
|
||||
github.com/bep/godartsass v1.2.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8=
|
||||
github.com/bep/godartsass/v2 v2.1.0 h1:fq5Y1xYf4diu4tXABiekZUCA+5l/dmNjGKCeQwdy+s0=
|
||||
github.com/bep/godartsass/v2 v2.1.0/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo=
|
||||
github.com/bep/golibsass v1.1.1 h1:xkaet75ygImMYjM+FnHIT3xJn7H0xBA9UxSOJjk8Khw=
|
||||
github.com/bep/golibsass v1.1.1/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
||||
github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI=
|
||||
github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
||||
github.com/bep/gowebp v0.3.0 h1:MhmMrcf88pUY7/PsEhMgEP0T6fDUnRTMpN8OclDrbrY=
|
||||
github.com/bep/gowebp v0.3.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
|
||||
github.com/bep/helpers v0.4.0 h1:ab9veaAiWY4ST48Oxp5usaqivDmYdB744fz+tcZ3Ifs=
|
||||
github.com/bep/helpers v0.4.0/go.mod h1:/QpHdmcPagDw7+RjkLFCvnlUc8lQ5kg4KDrEkb2Yyco=
|
||||
github.com/bep/helpers v0.5.0 h1:rneezhnG7GzLFlsEWO/EnleaBRuluBDGFimalO6Y50o=
|
||||
github.com/bep/helpers v0.5.0/go.mod h1:dSqCzIvHbzsk5YOesp1M7sKAq5xUcvANsRoKdawxH4Q=
|
||||
github.com/bep/imagemeta v0.8.1 h1:tjZLPRftjxU7PTI87o5e5WKOFQ4S9S0engiP1OTpJTI=
|
||||
github.com/bep/imagemeta v0.8.1/go.mod h1:5piPAq5Qomh07m/dPPCLN3mDJyFusvUG7VwdRD/vX0s=
|
||||
github.com/bep/lazycache v0.4.0 h1:X8yVyWNVupPd4e1jV7efi3zb7ZV/qcjKQgIQ5aPbkYI=
|
||||
@@ -225,8 +223,8 @@ github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicb
|
||||
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
|
||||
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
|
||||
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
|
||||
@@ -331,8 +329,8 @@ github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hairyhenderson/go-codeowners v0.5.0 h1:dpQB+hVHiRc2VVvc2BHxkuM+tmu9Qej/as3apqUbsWc=
|
||||
github.com/hairyhenderson/go-codeowners v0.5.0/go.mod h1:R3uW1OQXEj2Gu6/OvZ7bt6hr0qdkLvUWPiqNaWnexpo=
|
||||
github.com/hairyhenderson/go-codeowners v0.6.0 h1:cRCtmNf9Ni1GIeiAAlHX5IEEB2gr61813Kx5JmXxAAk=
|
||||
github.com/hairyhenderson/go-codeowners v0.6.0/go.mod h1:RFWbGcjlXhRKNezt7AQHmJucY0alk4osN0+RKOsIAa8=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
@@ -511,8 +509,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
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=
|
||||
@@ -528,8 +526,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
|
||||
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
|
||||
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
|
||||
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -556,8 +554,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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=
|
||||
@@ -597,8 +595,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
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=
|
||||
@@ -674,8 +672,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -694,8 +692,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
+8
-18
@@ -276,11 +276,13 @@ func TestLoadMultiConfig(t *testing.T) {
|
||||
// Add a random config variable for testing.
|
||||
// side = page in Norwegian.
|
||||
configContentBase := `
|
||||
Paginate = 32
|
||||
PaginatePath = "side"
|
||||
[pagination]
|
||||
pagerSize = 32
|
||||
path = "side"
|
||||
`
|
||||
configContentSub := `
|
||||
PaginatePath = "top"
|
||||
[pagination]
|
||||
path = "top"
|
||||
`
|
||||
mm := afero.NewMemMapFs()
|
||||
|
||||
@@ -292,8 +294,8 @@ func TestLoadMultiConfig(t *testing.T) {
|
||||
c.Assert(err, qt.IsNil)
|
||||
cfg := all.Base
|
||||
|
||||
c.Assert(cfg.PaginatePath, qt.Equals, "top")
|
||||
c.Assert(cfg.Paginate, qt.Equals, 32)
|
||||
c.Assert(cfg.Pagination.Path, qt.Equals, "top")
|
||||
c.Assert(cfg.Pagination.PagerSize, qt.Equals, 32)
|
||||
}
|
||||
|
||||
func TestLoadConfigFromThemes(t *testing.T) {
|
||||
@@ -698,10 +700,6 @@ func TestHugoConfig(t *testing.T) {
|
||||
filesTemplate := `
|
||||
-- hugo.toml --
|
||||
theme = "mytheme"
|
||||
[social]
|
||||
twitter = "bepsays"
|
||||
[author]
|
||||
name = "bep"
|
||||
[params]
|
||||
rootparam = "rootvalue"
|
||||
-- config/_default/hugo.toml --
|
||||
@@ -718,9 +716,6 @@ rootparam: {{ site.Params.rootparam }}
|
||||
rootconfigparam: {{ site.Params.rootconfigparam }}
|
||||
themeparam: {{ site.Params.themeparam }}
|
||||
themeconfigdirparam: {{ site.Params.themeconfigdirparam }}
|
||||
social: {{ site.Social }}
|
||||
author: {{ site.Author }}
|
||||
|
||||
|
||||
`
|
||||
|
||||
@@ -744,8 +739,6 @@ author: {{ site.Author }}
|
||||
"rootconfigparam: rootconfigvalue",
|
||||
"themeparam: themevalue",
|
||||
"themeconfigdirparam: themeconfigdirvalue",
|
||||
"social: map[twitter:bepsays]",
|
||||
"author: map[name:bep]",
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -918,11 +911,9 @@ title: "My Swedish Section"
|
||||
-- layouts/index.html --
|
||||
LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Language.LanguageCode }}|
|
||||
{{ range $i, $e := (slice site .Site) }}
|
||||
{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultilingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
|
||||
{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}|BuildDrafts: {{ .BuildDrafts }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
@@ -940,7 +931,6 @@ AllPages: 4|
|
||||
Sections: true|
|
||||
Param: enParamValue
|
||||
Param: enParamValue
|
||||
IsMultilingual: true
|
||||
LanguageCode: true|en-US|
|
||||
`)
|
||||
|
||||
|
||||
+22
-23
@@ -28,8 +28,8 @@ func TestContentMapSite(t *testing.T) {
|
||||
pageTempl := `
|
||||
---
|
||||
title: "Page %d"
|
||||
date: "2019-06-0%d"
|
||||
lastMod: "2019-06-0%d"
|
||||
date: "2019-06-%02d"
|
||||
lastMod: "2019-06-%02d"
|
||||
categories: [%q]
|
||||
---
|
||||
|
||||
@@ -55,7 +55,6 @@ draft: true
|
||||
title: "Hugo Home"
|
||||
cascade:
|
||||
description: "Common Description"
|
||||
|
||||
---
|
||||
|
||||
Home Content.
|
||||
@@ -64,24 +63,24 @@ Home Content.
|
||||
b.WithContent("blog/page1.md", createPage(1))
|
||||
b.WithContent("blog/page2.md", createPage(2))
|
||||
b.WithContent("blog/page3.md", createPage(3))
|
||||
b.WithContent("blog/bundle/index.md", createPage(12))
|
||||
b.WithContent("blog/bundle/index.md", createPage(4))
|
||||
b.WithContent("blog/bundle/data.json", "data")
|
||||
b.WithContent("blog/bundle/page.md", createPage(99))
|
||||
b.WithContent("blog/subsection/_index.md", createPage(3))
|
||||
b.WithContent("blog/bundle/page.md", createPage(5))
|
||||
b.WithContent("blog/subsection/_index.md", createPage(6))
|
||||
b.WithContent("blog/subsection/subdata.json", "data")
|
||||
b.WithContent("blog/subsection/page4.md", createPage(8))
|
||||
b.WithContent("blog/subsection/page5.md", createPage(10))
|
||||
b.WithContent("blog/subsection/page4.md", createPage(7))
|
||||
b.WithContent("blog/subsection/page5.md", createPage(8))
|
||||
b.WithContent("blog/subsection/draft/index.md", draftTemplate)
|
||||
b.WithContent("blog/subsection/draft/data.json", "data")
|
||||
b.WithContent("blog/draftsection/_index.md", draftTemplate)
|
||||
b.WithContent("blog/draftsection/page/index.md", createPage(12))
|
||||
b.WithContent("blog/draftsection/page/index.md", createPage(9))
|
||||
b.WithContent("blog/draftsection/page/folder/data.json", "data")
|
||||
b.WithContent("blog/draftsection/sub/_index.md", createPage(12))
|
||||
b.WithContent("blog/draftsection/sub/page.md", createPage(13))
|
||||
b.WithContent("docs/page6.md", createPage(11))
|
||||
b.WithContent("tags/_index.md", createPageInCategory(32, "sad"))
|
||||
b.WithContent("overlap/_index.md", createPageInCategory(33, "sad"))
|
||||
b.WithContent("overlap2/_index.md", createPage(34))
|
||||
b.WithContent("blog/draftsection/sub/_index.md", createPage(10))
|
||||
b.WithContent("blog/draftsection/sub/page.md", createPage(11))
|
||||
b.WithContent("docs/page6.md", createPage(12))
|
||||
b.WithContent("tags/_index.md", createPageInCategory(13, "sad"))
|
||||
b.WithContent("overlap/_index.md", createPageInCategory(14, "sad"))
|
||||
b.WithContent("overlap2/_index.md", createPage(15))
|
||||
|
||||
b.WithTemplatesAdded("layouts/index.html", `
|
||||
Num Regular: {{ len .Site.RegularPages }}|{{ range .Site.RegularPages }}{{ .RelPermalink }}|{{ end }}$
|
||||
@@ -118,7 +117,7 @@ Pages: {{ range $blog.Pages }}{{ .RelPermalink }}|{{ end }}
|
||||
Sections: {{ range $home.Sections }}{{ .RelPermalink }}|{{ end }}:END
|
||||
Categories: {{ range .Site.Taxonomies.categories }}{{ .Page.RelPermalink }}; {{ .Page.Title }}; {{ .Count }}|{{ end }}:END
|
||||
Category Terms: {{ $categories.Kind}}: {{ range $categories.Data.Terms.Alphabetical }}{{ .Page.RelPermalink }}; {{ .Page.Title }}; {{ .Count }}|{{ end }}:END
|
||||
Category Funny: {{ $funny.Kind}}; {{ $funny.Data.Term }}: {{ range $funny.Pages }}{{ .RelPermalink }};|{{ end }}:END
|
||||
Category Funny: {{ $funny.Kind}}; {{ $funny.Data.Term }}: {{ range $funny.Pages }}{{ .RelPermalink }}|{{ end }}:END
|
||||
Pag Num Pages: {{ len .Paginator.Pages }}
|
||||
Pag Blog Num Pages: {{ len $blog.Paginator.Pages }}
|
||||
Blog Num RegularPages: {{ len $blog.RegularPages }}|{{ range $blog.RegularPages }}P: {{ .RelPermalink }}|{{ end }}
|
||||
@@ -142,11 +141,11 @@ Draft5: {{ if (.Site.GetPage "blog/draftsection/sub/page") }}FOUND{{ end }}|
|
||||
Main Sections: [blog]
|
||||
Pag Num Pages: 9
|
||||
|
||||
Home: Hugo Home|/|2019-06-08|Current Section: /|Resources:
|
||||
Blog Section: Blogs|/blog/|2019-06-08|Current Section: /blog|Resources:
|
||||
Blog Sub Section: Page 3|/blog/subsection/|2019-06-03|Current Section: /blog/subsection|Resources: application: /blog/subsection/subdata.json|
|
||||
Home: Hugo Home|/|2019-06-15|Current Section: /|Resources:
|
||||
Blog Section: Blogs|/blog/|2019-06-11|Current Section: /blog|Resources:
|
||||
Blog Sub Section: Page 6|/blog/subsection/|2019-06-06|Current Section: /blog/subsection|Resources: application: /blog/subsection/subdata.json|
|
||||
Page: Page 1|/blog/page1/|2019-06-01|Current Section: /blog|Resources:
|
||||
Bundle: Page 12|/blog/bundle/|0001-01-01|Current Section: /blog|Resources: application: /blog/bundle/data.json|page: |
|
||||
Bundle: Page 4|/blog/bundle/|2019-06-04|Current Section: /blog|Resources: application: /blog/bundle/data.json|page: |
|
||||
IsDescendant: true: true true: true true: true true: true true: true false: false false: false
|
||||
IsAncestor: true: true true: true true: true true: true true: true true: true false: false false: false false: false false: false
|
||||
IsDescendant overlap1: false: false
|
||||
@@ -157,11 +156,11 @@ Draft5: {{ if (.Site.GetPage "blog/draftsection/sub/page") }}FOUND{{ end }}|
|
||||
InSection: true: true false: false
|
||||
Next: /blog/page3/
|
||||
NextInSection: /blog/page3/
|
||||
Pages: /blog/page3/|/blog/subsection/|/blog/page2/|/blog/page1/|/blog/bundle/|
|
||||
Sections: /blog/|/docs/|/overlap/|/overlap2/|:END
|
||||
Pages: /blog/subsection/|/blog/bundle/|/blog/page3/|/blog/page2/|/blog/page1/|
|
||||
Sections: /overlap2/|/overlap/|/docs/|/blog/|:END
|
||||
Categories: /categories/funny/; Funny; 12|/categories/sad/; Sad; 2|:END
|
||||
Category Terms: taxonomy: /categories/funny/; Funny; 12|/categories/sad/; Sad; 2|:END
|
||||
Category Funny: term; funny: /blog/subsection/page4/;|/blog/page3/;|/blog/subsection/;|/blog/page2/;|/blog/page1/;|/blog/subsection/page5/;|/docs/page6/;|/blog/bundle/;|/blog/draftsection/page/;|/blog/draftsection/sub/;|/blog/draftsection/sub/page/;|/overlap2/;|:END
|
||||
Category Funny: term; funny: /overlap2/|/docs/page6/|/blog/draftsection/sub/page/|/blog/draftsection/sub/|/blog/draftsection/page/|/blog/subsection/page5/|/blog/subsection/page4/|/blog/subsection/|/blog/bundle/|/blog/page3/|/blog/page2/|/blog/page1/|:END
|
||||
Pag Num Pages: 9
|
||||
Pag Blog Num Pages: 4
|
||||
Blog Num RegularPages: 4
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestEmbeddedPaginationTemplate(t *testing.T) {
|
||||
|
||||
test := func(variant string, expectedOutput string) {
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithConfigFile("toml", `paginate = 1`)
|
||||
b.WithConfigFile("toml", `pagination.pagerSize = 1`)
|
||||
b.WithContent(
|
||||
"s1/p01.md", "---\ntitle: p01\n---",
|
||||
"s1/p02.md", "---\ntitle: p02\n---",
|
||||
|
||||
@@ -11,12 +11,14 @@ func TestMultihost(t *testing.T) {
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
paginate = 1
|
||||
defaultContentLanguage = "fr"
|
||||
defaultContentLanguageInSubdir = false
|
||||
staticDir = ["s1", "s2"]
|
||||
enableRobotsTXT = true
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[permalinks]
|
||||
other = "/somewhere/else/:filename"
|
||||
|
||||
|
||||
@@ -107,10 +107,13 @@ func TestSmoke(t *testing.T) {
|
||||
baseURL = "https://example.com"
|
||||
title = "Smoke Site"
|
||||
rssLimit = 3
|
||||
paginate = 1
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
enableRobotsTXT = true
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[taxonomies]
|
||||
category = 'categories'
|
||||
tag = 'tags'
|
||||
@@ -434,10 +437,10 @@ func TestDataRace(t *testing.T) {
|
||||
---
|
||||
title: "The Page"
|
||||
outputs: ["HTML", "JSON"]
|
||||
---
|
||||
---
|
||||
|
||||
The content.
|
||||
|
||||
|
||||
|
||||
`
|
||||
|
||||
@@ -450,10 +453,10 @@ The content.
|
||||
---
|
||||
title: "The Home"
|
||||
outputs: ["HTML", "JSON", "CSV", "RSS"]
|
||||
---
|
||||
---
|
||||
|
||||
The content.
|
||||
|
||||
|
||||
|
||||
`)
|
||||
|
||||
|
||||
@@ -504,7 +504,7 @@ type contentTableOfContents struct {
|
||||
}
|
||||
|
||||
type contentSummary struct {
|
||||
content string
|
||||
content template.HTML
|
||||
contentWithoutSummary template.HTML
|
||||
summary page.Summary
|
||||
}
|
||||
@@ -612,9 +612,9 @@ func (c *cachedContentScope) contentRendered(ctx context.Context) (contentSummar
|
||||
Truncated: summarized.Truncated(),
|
||||
}
|
||||
result.contentWithoutSummary = template.HTML(summarized.ContentWithoutSummary())
|
||||
result.content = summarized.Content()
|
||||
result.content = template.HTML(summarized.Content())
|
||||
} else {
|
||||
result.content = string(b)
|
||||
result.content = template.HTML(string(b))
|
||||
}
|
||||
|
||||
if !c.pi.hasSummaryDivider && cp.po.p.m.pageConfig.Summary == "" {
|
||||
@@ -646,6 +646,7 @@ func (c *cachedContentScope) contentRendered(ctx context.Context) (contentSummar
|
||||
Text: helpers.BytesToHTML(html),
|
||||
Type: page.SummaryTypeFrontMatter,
|
||||
}
|
||||
rs.Value.contentWithoutSummary = rs.Value.content
|
||||
}
|
||||
|
||||
return rs, err
|
||||
@@ -879,7 +880,7 @@ func (c *cachedContentScope) Content(ctx context.Context) (template.HTML, error)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return template.HTML(cr.content), nil
|
||||
return cr.content, nil
|
||||
}
|
||||
|
||||
func (c *cachedContentScope) ContentWithoutSummary(ctx context.Context) (template.HTML, error) {
|
||||
|
||||
@@ -425,6 +425,7 @@ func (p *pageState) setMetaPostParams() error {
|
||||
ModTime: mtime,
|
||||
GitAuthorDate: gitAuthorDate,
|
||||
Location: langs.GetLocation(pm.s.Language()),
|
||||
PathOrTitle: p.pathOrTitle(),
|
||||
}
|
||||
|
||||
// Handle the date separately
|
||||
|
||||
@@ -523,7 +523,7 @@ categories: ["cool stuff"]
|
||||
for _, p := range s.Pages() {
|
||||
checkDated(p, p.Kind())
|
||||
}
|
||||
checkDate(s.LastChange(), "site")
|
||||
checkDate(s.Lastmod(), "site")
|
||||
}
|
||||
|
||||
func TestPageDatesSections(t *testing.T) {
|
||||
@@ -593,7 +593,7 @@ func TestPageSummary(t *testing.T) {
|
||||
// Source is not Asciidoctor- or RST-compatible so don't test them
|
||||
if ext != "ad" && ext != "rst" {
|
||||
checkPageContent(t, p, normalizeExpected(ext, "<p><a href=\"https://lipsum.com/\">Lorem ipsum</a> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n\n<p>Additional text.</p>\n\n<p>Further text.</p>\n"), ext)
|
||||
checkPageSummary(t, p, normalizeExpected(ext, "<p><a href=\"https://lipsum.com/\">Lorem ipsum</a> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>"), ext)
|
||||
checkPageSummary(t, p, normalizeExpected(ext, "<p><a href=\"https://lipsum.com/\">Lorem ipsum</a> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p><p>Additional text.</p>"), ext)
|
||||
}
|
||||
checkPageType(t, p, "page")
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@ import (
|
||||
func TestPaginator(t *testing.T) {
|
||||
configFile := `
|
||||
baseURL = "https://example.com/foo/"
|
||||
paginate = 3
|
||||
paginatepath = "thepage"
|
||||
|
||||
[pagination]
|
||||
pagerSize = 3
|
||||
path = "thepage"
|
||||
|
||||
[languages.en]
|
||||
weight = 1
|
||||
@@ -161,10 +163,11 @@ Len Pag: {{ len $pag.Pages }}
|
||||
func TestPaginatorNodePagesOnly(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
paginate = 1
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
-- content/p1.md --
|
||||
-- layouts/_default/single.html --
|
||||
Paginator: {{ .Paginator }}
|
||||
Paginator: {{ .Paginator }}
|
||||
`
|
||||
b, err := TestE(t, files)
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
|
||||
@@ -559,7 +559,8 @@ baseURL = "https://example.com"
|
||||
disableKinds = ["term", "taxonomy"]
|
||||
disableLiveReload = true
|
||||
defaultContentLanguage = "nn"
|
||||
paginate = 20
|
||||
[pagination]
|
||||
pagerSize = 20
|
||||
[security]
|
||||
enableInlineShortcodes = true
|
||||
[languages]
|
||||
@@ -1431,7 +1432,7 @@ title: "My Sect"
|
||||
---
|
||||
title: "P%d"
|
||||
---
|
||||
P%d Content.
|
||||
P%d Content.
|
||||
`
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
|
||||
@@ -123,7 +123,7 @@ func TestSecurityPolicies(t *testing.T) {
|
||||
c.Skip()
|
||||
}
|
||||
cb := func(b *sitesBuilder) {
|
||||
b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss" | resources.ToCSS (dict "transpiler" "dartsass") }}`)
|
||||
b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss" | css.Sass (dict "transpiler" "dartsass") }}`)
|
||||
}
|
||||
testVariant(c, cb, "")
|
||||
})
|
||||
@@ -137,10 +137,10 @@ func TestSecurityPolicies(t *testing.T) {
|
||||
b.WithConfigFile("toml", `
|
||||
[security]
|
||||
[security.exec]
|
||||
allow="none"
|
||||
|
||||
allow="none"
|
||||
|
||||
`)
|
||||
b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss" | resources.ToCSS (dict "transpiler" "dartsass") }}`)
|
||||
b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss" | css.Sass (dict "transpiler" "dartsass") }}`)
|
||||
}
|
||||
testVariant(c, cb, `(?s).*sass(-embedded)?" is not whitelisted in policy "security\.exec\.allow".*`)
|
||||
})
|
||||
@@ -160,7 +160,7 @@ allow="none"
|
||||
httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fruits.json" }}{{ $json.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
|
||||
func(b *sitesBuilder) {
|
||||
b.WithConfigFile("toml", `
|
||||
[security]
|
||||
[security]
|
||||
[security.http]
|
||||
urls="none"
|
||||
`)
|
||||
@@ -181,39 +181,10 @@ urls="none"
|
||||
httpTestVariant(c, `{{ $json := resources.GetRemote "%[1]s/fakejson.json" }}{{ $json.Content }}`, ``,
|
||||
func(b *sitesBuilder) {
|
||||
b.WithConfigFile("toml", `
|
||||
[security]
|
||||
[security]
|
||||
[security.http]
|
||||
mediaTypes=["application/json"]
|
||||
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
c.Run("getJSON, OK", func(c *qt.C) {
|
||||
c.Parallel()
|
||||
httpTestVariant(c, `{{ $json := getJSON "%[1]s/fruits.json" }}{{ $json.Content }}`, "", nil)
|
||||
})
|
||||
|
||||
c.Run("getJSON, denied URL", func(c *qt.C) {
|
||||
c.Parallel()
|
||||
httpTestVariant(c, `{{ $json := getJSON "%[1]s/fruits.json" }}{{ $json.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
|
||||
func(b *sitesBuilder) {
|
||||
b.WithConfigFile("toml", `
|
||||
[security]
|
||||
[security.http]
|
||||
urls="none"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
c.Run("getCSV, denied URL", func(c *qt.C) {
|
||||
c.Parallel()
|
||||
httpTestVariant(c, `{{ $d := getCSV ";" "%[1]s/cities.csv" }}{{ $d.Content }}`, `(?s).*is not whitelisted in policy "security\.http\.urls".*`,
|
||||
func(b *sitesBuilder) {
|
||||
b.WithConfigFile("toml", `
|
||||
[security]
|
||||
[security.http]
|
||||
urls="none"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -126,10 +126,11 @@ func TestShortcodeMultipleOutputFormats(t *testing.T) {
|
||||
siteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
paginate = 1
|
||||
|
||||
disableKinds = ["section", "term", "taxonomy", "RSS", "sitemap", "robotsTXT", "404"]
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[outputs]
|
||||
home = [ "HTML", "AMP", "Calendar" ]
|
||||
page = [ "HTML", "AMP", "JSON" ]
|
||||
|
||||
+569
@@ -15,7 +15,9 @@ package hugolib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"mime"
|
||||
"net/url"
|
||||
@@ -28,10 +30,24 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/cache/dynacache"
|
||||
"github.com/gohugoio/hugo/common/htime"
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/para"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugolib/doctree"
|
||||
"github.com/gohugoio/hugo/hugolib/pagesfromdata"
|
||||
"github.com/gohugoio/hugo/internal/warpc"
|
||||
"github.com/gohugoio/hugo/langs/i18n"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/tpl/tplimpl"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
@@ -50,6 +66,9 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
"github.com/gohugoio/hugo/resources/page/siteidentities"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
|
||||
@@ -61,6 +80,556 @@ import (
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
)
|
||||
|
||||
var _ page.Site = (*Site)(nil)
|
||||
|
||||
type siteState int
|
||||
|
||||
const (
|
||||
siteStateInit siteState = iota
|
||||
siteStateReady
|
||||
)
|
||||
|
||||
type Site struct {
|
||||
state siteState
|
||||
conf *allconfig.Config
|
||||
language *langs.Language
|
||||
languagei int
|
||||
pageMap *pageMap
|
||||
|
||||
// The owning container.
|
||||
h *HugoSites
|
||||
|
||||
*deps.Deps
|
||||
|
||||
// Page navigation.
|
||||
*pageFinder
|
||||
taxonomies page.TaxonomyList
|
||||
menus navigation.Menus
|
||||
|
||||
// Shortcut to the home page. Note that this may be nil if
|
||||
// home page, for some odd reason, is disabled.
|
||||
home *pageState
|
||||
|
||||
// The last modification date of this site.
|
||||
lastmod time.Time
|
||||
|
||||
relatedDocsHandler *page.RelatedDocsHandler
|
||||
siteRefLinker
|
||||
publisher publisher.Publisher
|
||||
frontmatterHandler pagemeta.FrontMatterHandler
|
||||
|
||||
// The output formats that we need to render this site in. This slice
|
||||
// will be fixed once set.
|
||||
// This will be the union of Site.Pages' outputFormats.
|
||||
// This slice will be sorted.
|
||||
renderFormats output.Formats
|
||||
|
||||
// Lazily loaded site dependencies
|
||||
init *siteInit
|
||||
}
|
||||
|
||||
func (s *Site) Debug() {
|
||||
fmt.Println("Debugging site", s.Lang(), "=>")
|
||||
// fmt.Println(s.pageMap.testDump())
|
||||
}
|
||||
|
||||
// NewHugoSites creates HugoSites from the given config.
|
||||
func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
|
||||
conf := cfg.Configs.GetFirstLanguageConfig()
|
||||
|
||||
var logger loggers.Logger
|
||||
if cfg.TestLogger != nil {
|
||||
logger = cfg.TestLogger
|
||||
} else {
|
||||
var logHookLast func(e *logg.Entry) error
|
||||
if cfg.Configs.Base.PanicOnWarning {
|
||||
logHookLast = loggers.PanicOnWarningHook
|
||||
}
|
||||
if cfg.LogOut == nil {
|
||||
cfg.LogOut = os.Stdout
|
||||
}
|
||||
if cfg.LogLevel == 0 {
|
||||
cfg.LogLevel = logg.LevelWarn
|
||||
}
|
||||
|
||||
logOpts := loggers.Options{
|
||||
Level: cfg.LogLevel,
|
||||
DistinctLevel: logg.LevelWarn, // This will drop duplicate log warning and errors.
|
||||
HandlerPost: logHookLast,
|
||||
Stdout: cfg.LogOut,
|
||||
Stderr: cfg.LogOut,
|
||||
StoreErrors: conf.Watching(),
|
||||
SuppressStatements: conf.IgnoredLogs(),
|
||||
}
|
||||
logger = loggers.New(logOpts)
|
||||
|
||||
}
|
||||
|
||||
memCache := dynacache.New(dynacache.Options{Watching: conf.Watching(), Log: logger})
|
||||
|
||||
var h *HugoSites
|
||||
onSignalRebuild := func(ids ...identity.Identity) {
|
||||
// This channel is buffered, but make sure we do this in a non-blocking way.
|
||||
if cfg.ChangesFromBuild != nil {
|
||||
go func() {
|
||||
cfg.ChangesFromBuild <- ids
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
firstSiteDeps := &deps.Deps{
|
||||
Fs: cfg.Fs,
|
||||
Log: logger,
|
||||
Conf: conf,
|
||||
BuildState: &deps.BuildState{
|
||||
OnSignalRebuild: onSignalRebuild,
|
||||
},
|
||||
MemCache: memCache,
|
||||
TemplateProvider: tplimpl.DefaultTemplateProvider,
|
||||
TranslationProvider: i18n.NewTranslationProvider(),
|
||||
WasmDispatchers: warpc.AllDispatchers(
|
||||
warpc.Options{
|
||||
CompilationCacheDir: filepath.Join(conf.Dirs().CacheDir, "_warpc"),
|
||||
|
||||
// Katex is relatively slow.
|
||||
PoolSize: 8,
|
||||
Infof: logger.InfoCommand("wasm").Logf,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
if err := firstSiteDeps.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
confm := cfg.Configs
|
||||
if err := confm.Validate(logger); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sites []*Site
|
||||
|
||||
ns := &contentNodeShifter{
|
||||
numLanguages: len(confm.Languages),
|
||||
}
|
||||
|
||||
treeConfig := doctree.Config[contentNodeI]{
|
||||
Shifter: ns,
|
||||
}
|
||||
|
||||
pageTrees := &pageTrees{
|
||||
treePages: doctree.New(
|
||||
treeConfig,
|
||||
),
|
||||
treeResources: doctree.New(
|
||||
treeConfig,
|
||||
),
|
||||
treeTaxonomyEntries: doctree.NewTreeShiftTree[*weightedContentNode](doctree.DimensionLanguage.Index(), len(confm.Languages)),
|
||||
treePagesFromTemplateAdapters: doctree.NewTreeShiftTree[*pagesfromdata.PagesFromTemplate](doctree.DimensionLanguage.Index(), len(confm.Languages)),
|
||||
}
|
||||
|
||||
pageTrees.createMutableTrees()
|
||||
|
||||
for i, confp := range confm.ConfigLangs() {
|
||||
language := confp.Language()
|
||||
if language.Disabled {
|
||||
continue
|
||||
}
|
||||
k := language.Lang
|
||||
conf := confm.LanguageConfigMap[k]
|
||||
frontmatterHandler, err := pagemeta.NewFrontmatterHandler(firstSiteDeps.Log, conf.Frontmatter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
langs.SetParams(language, conf.Params)
|
||||
|
||||
s := &Site{
|
||||
conf: conf,
|
||||
language: language,
|
||||
languagei: i,
|
||||
frontmatterHandler: frontmatterHandler,
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
firstSiteDeps.Site = s
|
||||
s.Deps = firstSiteDeps
|
||||
} else {
|
||||
d, err := firstSiteDeps.Clone(s, confp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Deps = d
|
||||
}
|
||||
|
||||
s.pageMap = newPageMap(i, s, memCache, pageTrees)
|
||||
|
||||
s.pageFinder = newPageFinder(s.pageMap)
|
||||
s.siteRefLinker, err = newSiteRefLinker(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Set up the main publishing chain.
|
||||
pub, err := publisher.NewDestinationPublisher(
|
||||
firstSiteDeps.ResourceSpec,
|
||||
s.conf.OutputFormats.Config,
|
||||
s.conf.MediaTypes.Config,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.publisher = pub
|
||||
s.relatedDocsHandler = page.NewRelatedDocsHandler(s.conf.Related)
|
||||
// Site deps end.
|
||||
|
||||
s.prepareInits()
|
||||
sites = append(sites, s)
|
||||
}
|
||||
|
||||
if len(sites) == 0 {
|
||||
return nil, errors.New("no sites to build")
|
||||
}
|
||||
|
||||
// Pull the default content language to the top, then sort the sites by language weight (if set) or lang.
|
||||
defaultContentLanguage := confm.Base.DefaultContentLanguage
|
||||
sort.Slice(sites, func(i, j int) bool {
|
||||
li := sites[i].language
|
||||
lj := sites[j].language
|
||||
if li.Lang == defaultContentLanguage {
|
||||
return true
|
||||
}
|
||||
|
||||
if lj.Lang == defaultContentLanguage {
|
||||
return false
|
||||
}
|
||||
|
||||
if li.Weight != lj.Weight {
|
||||
return li.Weight < lj.Weight
|
||||
}
|
||||
return li.Lang < lj.Lang
|
||||
})
|
||||
|
||||
var err error
|
||||
h, err = newHugoSites(cfg, firstSiteDeps, pageTrees, sites)
|
||||
if err == nil && h == nil {
|
||||
panic("hugo: newHugoSitesNew returned nil error and nil HugoSites")
|
||||
}
|
||||
|
||||
return h, err
|
||||
}
|
||||
|
||||
func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []*Site) (*HugoSites, error) {
|
||||
numWorkers := config.GetNumWorkerMultiplier()
|
||||
numWorkersSite := numWorkers
|
||||
if numWorkersSite > len(sites) {
|
||||
numWorkersSite = len(sites)
|
||||
}
|
||||
workersSite := para.New(numWorkersSite)
|
||||
|
||||
h := &HugoSites{
|
||||
Sites: sites,
|
||||
Deps: sites[0].Deps,
|
||||
Configs: cfg.Configs,
|
||||
workersSite: workersSite,
|
||||
numWorkersSites: numWorkers,
|
||||
numWorkers: numWorkers,
|
||||
pageTrees: pageTrees,
|
||||
cachePages: dynacache.GetOrCreatePartition[string,
|
||||
page.Pages](d.MemCache, "/pags/all",
|
||||
dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild},
|
||||
),
|
||||
cacheContentSource: dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](d.MemCache, "/cont/src", dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
translationKeyPages: maps.NewSliceCache[page.Page](),
|
||||
currentSite: sites[0],
|
||||
skipRebuildForFilenames: make(map[string]bool),
|
||||
init: &hugoSitesInit{
|
||||
data: lazy.New(),
|
||||
layouts: lazy.New(),
|
||||
gitInfo: lazy.New(),
|
||||
},
|
||||
}
|
||||
|
||||
// Assemble dependencies to be used in hugo.Deps.
|
||||
var dependencies []*hugo.Dependency
|
||||
var depFromMod func(m modules.Module) *hugo.Dependency
|
||||
depFromMod = func(m modules.Module) *hugo.Dependency {
|
||||
dep := &hugo.Dependency{
|
||||
Path: m.Path(),
|
||||
Version: m.Version(),
|
||||
Time: m.Time(),
|
||||
Vendor: m.Vendor(),
|
||||
}
|
||||
|
||||
// These are pointers, but this all came from JSON so there's no recursive navigation,
|
||||
// so just create new values.
|
||||
if m.Replace() != nil {
|
||||
dep.Replace = depFromMod(m.Replace())
|
||||
}
|
||||
if m.Owner() != nil {
|
||||
dep.Owner = depFromMod(m.Owner())
|
||||
}
|
||||
return dep
|
||||
}
|
||||
for _, m := range d.Paths.AllModules() {
|
||||
dependencies = append(dependencies, depFromMod(m))
|
||||
}
|
||||
|
||||
h.hugoInfo = hugo.NewInfo(h.Configs.GetFirstLanguageConfig(), dependencies)
|
||||
|
||||
var prototype *deps.Deps
|
||||
for i, s := range sites {
|
||||
s.h = h
|
||||
if err := s.Deps.Compile(prototype); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i == 0 {
|
||||
prototype = s.Deps
|
||||
}
|
||||
}
|
||||
|
||||
h.fatalErrorHandler = &fatalErrorHandler{
|
||||
h: h,
|
||||
donec: make(chan bool),
|
||||
}
|
||||
|
||||
h.init.data.Add(func(context.Context) (any, error) {
|
||||
err := h.loadData()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load data: %w", err)
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
h.init.layouts.Add(func(context.Context) (any, error) {
|
||||
for _, s := range h.Sites {
|
||||
if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
h.init.gitInfo.Add(func(context.Context) (any, error) {
|
||||
err := h.loadGitInfo()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load Git info: %w", err)
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsServer instead.
|
||||
func (s *Site) IsServer() bool {
|
||||
hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
|
||||
return s.conf.Internal.Running
|
||||
}
|
||||
|
||||
// Returns the server port.
|
||||
func (s *Site) ServerPort() int {
|
||||
return s.conf.C.BaseURL.Port()
|
||||
}
|
||||
|
||||
// Returns the configured title for this Site.
|
||||
func (s *Site) Title() string {
|
||||
return s.conf.Title
|
||||
}
|
||||
|
||||
func (s *Site) Copyright() string {
|
||||
return s.conf.Copyright
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
|
||||
func (s *Site) RSSLink() template.URL {
|
||||
hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
|
||||
rssOutputFormat := s.home.OutputFormats().Get("rss")
|
||||
return template.URL(rssOutputFormat.Permalink())
|
||||
}
|
||||
|
||||
func (s *Site) Config() page.SiteConfig {
|
||||
return page.SiteConfig{
|
||||
Privacy: s.conf.Privacy,
|
||||
Services: s.conf.Services,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Site) LanguageCode() string {
|
||||
return s.Language().LanguageCode()
|
||||
}
|
||||
|
||||
// Returns all Sites for all languages.
|
||||
func (s *Site) Sites() page.Sites {
|
||||
sites := make(page.Sites, len(s.h.Sites))
|
||||
for i, s := range s.h.Sites {
|
||||
sites[i] = s.Site()
|
||||
}
|
||||
return sites
|
||||
}
|
||||
|
||||
// Returns Site currently rendering.
|
||||
func (s *Site) Current() page.Site {
|
||||
return s.h.currentSite
|
||||
}
|
||||
|
||||
// MainSections returns the list of main sections.
|
||||
func (s *Site) MainSections() []string {
|
||||
s.CheckReady()
|
||||
return s.conf.C.MainSections
|
||||
}
|
||||
|
||||
// Returns a struct with some information about the build.
|
||||
func (s *Site) Hugo() hugo.HugoInfo {
|
||||
if s.h == nil || s.h.hugoInfo.Environment == "" {
|
||||
panic("site: hugo: hugoInfo not initialized")
|
||||
}
|
||||
return s.h.hugoInfo
|
||||
}
|
||||
|
||||
// Returns the BaseURL for this Site.
|
||||
func (s *Site) BaseURL() string {
|
||||
return s.conf.C.BaseURL.WithPath
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Lastmod instead.
|
||||
func (s *Site) LastChange() time.Time {
|
||||
s.CheckReady()
|
||||
hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
|
||||
return s.lastmod
|
||||
}
|
||||
|
||||
// Returns the last modification date of the content.
|
||||
func (s *Site) Lastmod() time.Time {
|
||||
return s.lastmod
|
||||
}
|
||||
|
||||
// Returns the Params configured for this site.
|
||||
func (s *Site) Params() maps.Params {
|
||||
return s.conf.Params
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Author() map[string]any {
|
||||
if len(s.conf.Author) != 0 {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
}
|
||||
return s.conf.Author
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Authors() page.AuthorList {
|
||||
hugo.Deprecate(".Site.Authors", "Use taxonomies instead.", "v0.124.0")
|
||||
return page.AuthorList{}
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Params instead.
|
||||
func (s *Site) Social() map[string]string {
|
||||
hugo.Deprecate(".Site.Social", "Use .Site.Params instead.", "v0.124.0")
|
||||
return s.conf.Social
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
|
||||
func (s *Site) DisqusShortname() string {
|
||||
hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
|
||||
return s.Config().Services.Disqus.Shortname
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
|
||||
func (s *Site) GoogleAnalytics() string {
|
||||
hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
|
||||
return s.Config().Services.GoogleAnalytics.ID
|
||||
}
|
||||
|
||||
func (s *Site) Param(key any) (any, error) {
|
||||
return resource.Param(s, nil, key)
|
||||
}
|
||||
|
||||
// Returns a map of all the data inside /data.
|
||||
func (s *Site) Data() map[string]any {
|
||||
return s.s.h.Data()
|
||||
}
|
||||
|
||||
func (s *Site) BuildDrafts() bool {
|
||||
return s.conf.BuildDrafts
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsMultilingual instead.
|
||||
func (s *Site) IsMultiLingual() bool {
|
||||
hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultilingual instead.", "v0.124.0")
|
||||
return s.h.isMultilingual()
|
||||
}
|
||||
|
||||
func (s *Site) LanguagePrefix() string {
|
||||
prefix := s.GetLanguagePrefix()
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
return "/" + prefix
|
||||
}
|
||||
|
||||
func (s *Site) Site() page.Site {
|
||||
return page.WrapSite(s)
|
||||
}
|
||||
|
||||
func (s *Site) ForEeachIdentityByName(name string, f func(identity.Identity) bool) {
|
||||
if id, found := siteidentities.FromString(name); found {
|
||||
if f(id) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pages returns all pages.
|
||||
// This is for the current language only.
|
||||
func (s *Site) Pages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal,
|
||||
},
|
||||
Recursive: true,
|
||||
IncludeSelf: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// RegularPages returns all the regular pages.
|
||||
// This is for the current language only.
|
||||
func (s *Site) RegularPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal.And(pagePredicates.KindPage),
|
||||
},
|
||||
Recursive: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// AllPages returns all pages for all sites.
|
||||
func (s *Site) AllPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.h.Pages()
|
||||
}
|
||||
|
||||
// AllRegularPages returns all regular pages for all sites.
|
||||
func (s *Site) AllRegularPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.h.RegularPages()
|
||||
}
|
||||
|
||||
func (s *Site) CheckReady() {
|
||||
if s.state != siteStateReady {
|
||||
panic("this method cannot be called before the site is fully initialized")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Site) Taxonomies() page.TaxonomyList {
|
||||
s.CheckReady()
|
||||
s.init.taxonomies.Do(context.Background())
|
||||
|
||||
@@ -1,603 +0,0 @@
|
||||
// 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 hugolib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/cache/dynacache"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/para"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugolib/doctree"
|
||||
"github.com/gohugoio/hugo/hugolib/pagesfromdata"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/internal/warpc"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/langs/i18n"
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/navigation"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/publisher"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
"github.com/gohugoio/hugo/resources/page/siteidentities"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
"github.com/gohugoio/hugo/tpl/tplimpl"
|
||||
)
|
||||
|
||||
var _ page.Site = (*Site)(nil)
|
||||
|
||||
type siteState int
|
||||
|
||||
const (
|
||||
siteStateInit siteState = iota
|
||||
siteStateReady
|
||||
)
|
||||
|
||||
type Site struct {
|
||||
state siteState
|
||||
conf *allconfig.Config
|
||||
language *langs.Language
|
||||
languagei int
|
||||
pageMap *pageMap
|
||||
|
||||
// The owning container.
|
||||
h *HugoSites
|
||||
|
||||
*deps.Deps
|
||||
|
||||
// Page navigation.
|
||||
*pageFinder
|
||||
taxonomies page.TaxonomyList
|
||||
menus navigation.Menus
|
||||
|
||||
// Shortcut to the home page. Note that this may be nil if
|
||||
// home page, for some odd reason, is disabled.
|
||||
home *pageState
|
||||
|
||||
// The last modification date of this site.
|
||||
lastmod time.Time
|
||||
|
||||
relatedDocsHandler *page.RelatedDocsHandler
|
||||
siteRefLinker
|
||||
publisher publisher.Publisher
|
||||
frontmatterHandler pagemeta.FrontMatterHandler
|
||||
|
||||
// The output formats that we need to render this site in. This slice
|
||||
// will be fixed once set.
|
||||
// This will be the union of Site.Pages' outputFormats.
|
||||
// This slice will be sorted.
|
||||
renderFormats output.Formats
|
||||
|
||||
// Lazily loaded site dependencies
|
||||
init *siteInit
|
||||
}
|
||||
|
||||
func (s *Site) Debug() {
|
||||
fmt.Println("Debugging site", s.Lang(), "=>")
|
||||
// fmt.Println(s.pageMap.testDump())
|
||||
}
|
||||
|
||||
// NewHugoSites creates HugoSites from the given config.
|
||||
func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
|
||||
conf := cfg.Configs.GetFirstLanguageConfig()
|
||||
|
||||
var logger loggers.Logger
|
||||
if cfg.TestLogger != nil {
|
||||
logger = cfg.TestLogger
|
||||
} else {
|
||||
var logHookLast func(e *logg.Entry) error
|
||||
if cfg.Configs.Base.PanicOnWarning {
|
||||
logHookLast = loggers.PanicOnWarningHook
|
||||
}
|
||||
if cfg.LogOut == nil {
|
||||
cfg.LogOut = os.Stdout
|
||||
}
|
||||
if cfg.LogLevel == 0 {
|
||||
cfg.LogLevel = logg.LevelWarn
|
||||
}
|
||||
|
||||
logOpts := loggers.Options{
|
||||
Level: cfg.LogLevel,
|
||||
DistinctLevel: logg.LevelWarn, // This will drop duplicate log warning and errors.
|
||||
HandlerPost: logHookLast,
|
||||
Stdout: cfg.LogOut,
|
||||
Stderr: cfg.LogOut,
|
||||
StoreErrors: conf.Watching(),
|
||||
SuppressStatements: conf.IgnoredLogs(),
|
||||
}
|
||||
logger = loggers.New(logOpts)
|
||||
|
||||
}
|
||||
|
||||
memCache := dynacache.New(dynacache.Options{Watching: conf.Watching(), Log: logger})
|
||||
|
||||
var h *HugoSites
|
||||
onSignalRebuild := func(ids ...identity.Identity) {
|
||||
// This channel is buffered, but make sure we do this in a non-blocking way.
|
||||
if cfg.ChangesFromBuild != nil {
|
||||
go func() {
|
||||
cfg.ChangesFromBuild <- ids
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
firstSiteDeps := &deps.Deps{
|
||||
Fs: cfg.Fs,
|
||||
Log: logger,
|
||||
Conf: conf,
|
||||
BuildState: &deps.BuildState{
|
||||
OnSignalRebuild: onSignalRebuild,
|
||||
},
|
||||
MemCache: memCache,
|
||||
TemplateProvider: tplimpl.DefaultTemplateProvider,
|
||||
TranslationProvider: i18n.NewTranslationProvider(),
|
||||
WasmDispatchers: warpc.AllDispatchers(
|
||||
warpc.Options{
|
||||
CompilationCacheDir: filepath.Join(conf.Dirs().CacheDir, "_warpc"),
|
||||
|
||||
// Katex is relatively slow.
|
||||
PoolSize: 8,
|
||||
Infof: logger.InfoCommand("wasm").Logf,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
if err := firstSiteDeps.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
confm := cfg.Configs
|
||||
if err := confm.Validate(logger); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sites []*Site
|
||||
|
||||
ns := &contentNodeShifter{
|
||||
numLanguages: len(confm.Languages),
|
||||
}
|
||||
|
||||
treeConfig := doctree.Config[contentNodeI]{
|
||||
Shifter: ns,
|
||||
}
|
||||
|
||||
pageTrees := &pageTrees{
|
||||
treePages: doctree.New(
|
||||
treeConfig,
|
||||
),
|
||||
treeResources: doctree.New(
|
||||
treeConfig,
|
||||
),
|
||||
treeTaxonomyEntries: doctree.NewTreeShiftTree[*weightedContentNode](doctree.DimensionLanguage.Index(), len(confm.Languages)),
|
||||
treePagesFromTemplateAdapters: doctree.NewTreeShiftTree[*pagesfromdata.PagesFromTemplate](doctree.DimensionLanguage.Index(), len(confm.Languages)),
|
||||
}
|
||||
|
||||
pageTrees.createMutableTrees()
|
||||
|
||||
for i, confp := range confm.ConfigLangs() {
|
||||
language := confp.Language()
|
||||
if language.Disabled {
|
||||
continue
|
||||
}
|
||||
k := language.Lang
|
||||
conf := confm.LanguageConfigMap[k]
|
||||
frontmatterHandler, err := pagemeta.NewFrontmatterHandler(firstSiteDeps.Log, conf.Frontmatter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
langs.SetParams(language, conf.Params)
|
||||
|
||||
s := &Site{
|
||||
conf: conf,
|
||||
language: language,
|
||||
languagei: i,
|
||||
frontmatterHandler: frontmatterHandler,
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
firstSiteDeps.Site = s
|
||||
s.Deps = firstSiteDeps
|
||||
} else {
|
||||
d, err := firstSiteDeps.Clone(s, confp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Deps = d
|
||||
}
|
||||
|
||||
s.pageMap = newPageMap(i, s, memCache, pageTrees)
|
||||
|
||||
s.pageFinder = newPageFinder(s.pageMap)
|
||||
s.siteRefLinker, err = newSiteRefLinker(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Set up the main publishing chain.
|
||||
pub, err := publisher.NewDestinationPublisher(
|
||||
firstSiteDeps.ResourceSpec,
|
||||
s.conf.OutputFormats.Config,
|
||||
s.conf.MediaTypes.Config,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.publisher = pub
|
||||
s.relatedDocsHandler = page.NewRelatedDocsHandler(s.conf.Related)
|
||||
// Site deps end.
|
||||
|
||||
s.prepareInits()
|
||||
sites = append(sites, s)
|
||||
}
|
||||
|
||||
if len(sites) == 0 {
|
||||
return nil, errors.New("no sites to build")
|
||||
}
|
||||
|
||||
// Pull the default content language to the top, then sort the sites by language weight (if set) or lang.
|
||||
defaultContentLanguage := confm.Base.DefaultContentLanguage
|
||||
sort.Slice(sites, func(i, j int) bool {
|
||||
li := sites[i].language
|
||||
lj := sites[j].language
|
||||
if li.Lang == defaultContentLanguage {
|
||||
return true
|
||||
}
|
||||
|
||||
if lj.Lang == defaultContentLanguage {
|
||||
return false
|
||||
}
|
||||
|
||||
if li.Weight != lj.Weight {
|
||||
return li.Weight < lj.Weight
|
||||
}
|
||||
return li.Lang < lj.Lang
|
||||
})
|
||||
|
||||
var err error
|
||||
h, err = newHugoSites(cfg, firstSiteDeps, pageTrees, sites)
|
||||
if err == nil && h == nil {
|
||||
panic("hugo: newHugoSitesNew returned nil error and nil HugoSites")
|
||||
}
|
||||
|
||||
return h, err
|
||||
}
|
||||
|
||||
func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []*Site) (*HugoSites, error) {
|
||||
numWorkers := config.GetNumWorkerMultiplier()
|
||||
numWorkersSite := numWorkers
|
||||
if numWorkersSite > len(sites) {
|
||||
numWorkersSite = len(sites)
|
||||
}
|
||||
workersSite := para.New(numWorkersSite)
|
||||
|
||||
h := &HugoSites{
|
||||
Sites: sites,
|
||||
Deps: sites[0].Deps,
|
||||
Configs: cfg.Configs,
|
||||
workersSite: workersSite,
|
||||
numWorkersSites: numWorkers,
|
||||
numWorkers: numWorkers,
|
||||
pageTrees: pageTrees,
|
||||
cachePages: dynacache.GetOrCreatePartition[string,
|
||||
page.Pages](d.MemCache, "/pags/all",
|
||||
dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild},
|
||||
),
|
||||
cacheContentSource: dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](d.MemCache, "/cont/src", dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
translationKeyPages: maps.NewSliceCache[page.Page](),
|
||||
currentSite: sites[0],
|
||||
skipRebuildForFilenames: make(map[string]bool),
|
||||
init: &hugoSitesInit{
|
||||
data: lazy.New(),
|
||||
layouts: lazy.New(),
|
||||
gitInfo: lazy.New(),
|
||||
},
|
||||
}
|
||||
|
||||
// Assemble dependencies to be used in hugo.Deps.
|
||||
var dependencies []*hugo.Dependency
|
||||
var depFromMod func(m modules.Module) *hugo.Dependency
|
||||
depFromMod = func(m modules.Module) *hugo.Dependency {
|
||||
dep := &hugo.Dependency{
|
||||
Path: m.Path(),
|
||||
Version: m.Version(),
|
||||
Time: m.Time(),
|
||||
Vendor: m.Vendor(),
|
||||
}
|
||||
|
||||
// These are pointers, but this all came from JSON so there's no recursive navigation,
|
||||
// so just create new values.
|
||||
if m.Replace() != nil {
|
||||
dep.Replace = depFromMod(m.Replace())
|
||||
}
|
||||
if m.Owner() != nil {
|
||||
dep.Owner = depFromMod(m.Owner())
|
||||
}
|
||||
return dep
|
||||
}
|
||||
for _, m := range d.Paths.AllModules() {
|
||||
dependencies = append(dependencies, depFromMod(m))
|
||||
}
|
||||
|
||||
h.hugoInfo = hugo.NewInfo(h.Configs.GetFirstLanguageConfig(), dependencies)
|
||||
|
||||
var prototype *deps.Deps
|
||||
for i, s := range sites {
|
||||
s.h = h
|
||||
if err := s.Deps.Compile(prototype); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if i == 0 {
|
||||
prototype = s.Deps
|
||||
}
|
||||
}
|
||||
|
||||
h.fatalErrorHandler = &fatalErrorHandler{
|
||||
h: h,
|
||||
donec: make(chan bool),
|
||||
}
|
||||
|
||||
h.init.data.Add(func(context.Context) (any, error) {
|
||||
err := h.loadData()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load data: %w", err)
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
h.init.layouts.Add(func(context.Context) (any, error) {
|
||||
for _, s := range h.Sites {
|
||||
if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
h.init.gitInfo.Add(func(context.Context) (any, error) {
|
||||
err := h.loadGitInfo()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load Git info: %w", err)
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsServer instead.
|
||||
func (s *Site) IsServer() bool {
|
||||
hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
|
||||
return s.conf.Internal.Running
|
||||
}
|
||||
|
||||
// Returns the server port.
|
||||
func (s *Site) ServerPort() int {
|
||||
return s.conf.C.BaseURL.Port()
|
||||
}
|
||||
|
||||
// Returns the configured title for this Site.
|
||||
func (s *Site) Title() string {
|
||||
return s.conf.Title
|
||||
}
|
||||
|
||||
func (s *Site) Copyright() string {
|
||||
return s.conf.Copyright
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
|
||||
func (s *Site) RSSLink() template.URL {
|
||||
hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
|
||||
rssOutputFormat := s.home.OutputFormats().Get("rss")
|
||||
return template.URL(rssOutputFormat.Permalink())
|
||||
}
|
||||
|
||||
func (s *Site) Config() page.SiteConfig {
|
||||
return page.SiteConfig{
|
||||
Privacy: s.conf.Privacy,
|
||||
Services: s.conf.Services,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Site) LanguageCode() string {
|
||||
return s.Language().LanguageCode()
|
||||
}
|
||||
|
||||
// Returns all Sites for all languages.
|
||||
func (s *Site) Sites() page.Sites {
|
||||
sites := make(page.Sites, len(s.h.Sites))
|
||||
for i, s := range s.h.Sites {
|
||||
sites[i] = s.Site()
|
||||
}
|
||||
return sites
|
||||
}
|
||||
|
||||
// Returns Site currently rendering.
|
||||
func (s *Site) Current() page.Site {
|
||||
return s.h.currentSite
|
||||
}
|
||||
|
||||
// MainSections returns the list of main sections.
|
||||
func (s *Site) MainSections() []string {
|
||||
s.CheckReady()
|
||||
return s.conf.C.MainSections
|
||||
}
|
||||
|
||||
// Returns a struct with some information about the build.
|
||||
func (s *Site) Hugo() hugo.HugoInfo {
|
||||
if s.h == nil || s.h.hugoInfo.Environment == "" {
|
||||
panic("site: hugo: hugoInfo not initialized")
|
||||
}
|
||||
return s.h.hugoInfo
|
||||
}
|
||||
|
||||
// Returns the BaseURL for this Site.
|
||||
func (s *Site) BaseURL() string {
|
||||
return s.conf.C.BaseURL.WithPath
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Lastmod instead.
|
||||
func (s *Site) LastChange() time.Time {
|
||||
s.CheckReady()
|
||||
hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
|
||||
return s.lastmod
|
||||
}
|
||||
|
||||
// Returns the last modification date of the content.
|
||||
func (s *Site) Lastmod() time.Time {
|
||||
return s.lastmod
|
||||
}
|
||||
|
||||
// Returns the Params configured for this site.
|
||||
func (s *Site) Params() maps.Params {
|
||||
return s.conf.Params
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Author() map[string]any {
|
||||
if len(s.conf.Author) != 0 {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
}
|
||||
return s.conf.Author
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Authors() page.AuthorList {
|
||||
hugo.Deprecate(".Site.Authors", "Use taxonomies instead.", "v0.124.0")
|
||||
return page.AuthorList{}
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Params instead.
|
||||
func (s *Site) Social() map[string]string {
|
||||
hugo.Deprecate(".Site.Social", "Use .Site.Params instead.", "v0.124.0")
|
||||
return s.conf.Social
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
|
||||
func (s *Site) DisqusShortname() string {
|
||||
hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
|
||||
return s.Config().Services.Disqus.Shortname
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
|
||||
func (s *Site) GoogleAnalytics() string {
|
||||
hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
|
||||
return s.Config().Services.GoogleAnalytics.ID
|
||||
}
|
||||
|
||||
func (s *Site) Param(key any) (any, error) {
|
||||
return resource.Param(s, nil, key)
|
||||
}
|
||||
|
||||
// Returns a map of all the data inside /data.
|
||||
func (s *Site) Data() map[string]any {
|
||||
return s.s.h.Data()
|
||||
}
|
||||
|
||||
func (s *Site) BuildDrafts() bool {
|
||||
return s.conf.BuildDrafts
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsMultilingual instead.
|
||||
func (s *Site) IsMultiLingual() bool {
|
||||
hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultilingual instead.", "v0.124.0")
|
||||
return s.h.isMultilingual()
|
||||
}
|
||||
|
||||
func (s *Site) LanguagePrefix() string {
|
||||
prefix := s.GetLanguagePrefix()
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
return "/" + prefix
|
||||
}
|
||||
|
||||
func (s *Site) Site() page.Site {
|
||||
return page.WrapSite(s)
|
||||
}
|
||||
|
||||
func (s *Site) ForEeachIdentityByName(name string, f func(identity.Identity) bool) {
|
||||
if id, found := siteidentities.FromString(name); found {
|
||||
if f(id) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pages returns all pages.
|
||||
// This is for the current language only.
|
||||
func (s *Site) Pages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal,
|
||||
},
|
||||
Recursive: true,
|
||||
IncludeSelf: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// RegularPages returns all the regular pages.
|
||||
// This is for the current language only.
|
||||
func (s *Site) RegularPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.pageMap.getPagesInSection(
|
||||
pageMapQueryPagesInSection{
|
||||
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
|
||||
Path: "",
|
||||
KeyPart: "global",
|
||||
Include: pagePredicates.ShouldListGlobal.And(pagePredicates.KindPage),
|
||||
},
|
||||
Recursive: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// AllPages returns all pages for all sites.
|
||||
func (s *Site) AllPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.h.Pages()
|
||||
}
|
||||
|
||||
// AllRegularPages returns all regular pages for all sites.
|
||||
func (s *Site) AllRegularPages() page.Pages {
|
||||
s.CheckReady()
|
||||
return s.h.RegularPages()
|
||||
}
|
||||
|
||||
func (s *Site) CheckReady() {
|
||||
if s.state != siteStateReady {
|
||||
panic("this method cannot be called before the site is fully initialized")
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ package hugolib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -45,11 +44,13 @@ func doTestSiteWithPageOutputs(t *testing.T, outputs []string) {
|
||||
siteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
paginate = 1
|
||||
defaultContentLanguage = "en"
|
||||
|
||||
disableKinds = ["section", "term", "taxonomy", "RSS", "sitemap", "robotsTXT", "404"]
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[Taxonomies]
|
||||
tag = "tags"
|
||||
category = "categories"
|
||||
@@ -221,11 +222,13 @@ func TestRedefineRSSOutputFormat(t *testing.T) {
|
||||
siteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
paginate = 1
|
||||
defaultContentLanguage = "en"
|
||||
|
||||
disableKinds = ["page", "section", "term", "taxonomy", "sitemap", "robotsTXT", "404"]
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[outputFormats]
|
||||
[outputFormats.RSS]
|
||||
mediatype = "application/rss"
|
||||
@@ -249,7 +252,7 @@ baseName = "feed"
|
||||
s := h.Sites[0]
|
||||
|
||||
// Issue #3450
|
||||
c.Assert(s.RSSLink(), qt.Equals, template.URL("http://example.com/blog/feed.xml"))
|
||||
c.Assert(s.Home().OutputFormats().Get("rss").Permalink(), qt.Equals, "http://example.com/blog/feed.xml")
|
||||
}
|
||||
|
||||
// Issue #3614
|
||||
@@ -257,11 +260,13 @@ func TestDotLessOutputFormat(t *testing.T) {
|
||||
siteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
paginate = 1
|
||||
defaultContentLanguage = "en"
|
||||
|
||||
disableKinds = ["page", "section", "term", "taxonomy", "sitemap", "robotsTXT", "404"]
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."text/nodot"]
|
||||
delimiter = ""
|
||||
|
||||
@@ -115,7 +115,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
|
||||
{{ $sections := (.Site.GetPage "section" .Section).Sections.ByWeight }}
|
||||
</html>`)
|
||||
|
||||
cfg.Set("paginate", 2)
|
||||
cfg.Set("pagination.pagerSize", 2)
|
||||
|
||||
th, configs := newTestHelperFromProvider(cfg, fs, t)
|
||||
|
||||
|
||||
@@ -32,9 +32,11 @@ func TestSiteStats(t *testing.T) {
|
||||
siteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
paginate = 1
|
||||
defaultContentLanguage = "nn"
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[languages]
|
||||
[languages.nn]
|
||||
languageName = "Nynorsk"
|
||||
@@ -121,11 +123,10 @@ date: 2023-04-01
|
||||
---
|
||||
-- layouts/index.html --
|
||||
site.Lastmod: {{ .Site.Lastmod.Format "2006-01-02" }}
|
||||
site.LastChange: {{ .Site.LastChange.Format "2006-01-02" }}
|
||||
home.Lastmod: {{ site.Home.Lastmod.Format "2006-01-02" }}
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "site.Lastmod: 2023-04-01\nsite.LastChange: 2023-04-01\nhome.Lastmod: 2023-01-01")
|
||||
b.AssertFileContent("public/index.html", "site.Lastmod: 2023-04-01\nhome.Lastmod: 2023-01-01")
|
||||
}
|
||||
|
||||
@@ -147,8 +147,8 @@ func TestLastChange(t *testing.T) {
|
||||
|
||||
s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Configs: configs}, BuildCfg{SkipRender: true})
|
||||
|
||||
c.Assert(s.LastChange().IsZero(), qt.Equals, false)
|
||||
c.Assert(s.LastChange().Year(), qt.Equals, 2017)
|
||||
c.Assert(s.Lastmod().IsZero(), qt.Equals, false)
|
||||
c.Assert(s.Lastmod().Year(), qt.Equals, 2017)
|
||||
}
|
||||
|
||||
// Issue #_index
|
||||
|
||||
@@ -91,7 +91,7 @@ Do not go gentle into that good night.
|
||||
`
|
||||
|
||||
cfg, fs := newTestCfg()
|
||||
cfg.Set("paginate", 1)
|
||||
cfg.Set("pagination.pagerSize", 1)
|
||||
th, configs := newTestHelperFromProvider(cfg, fs, t)
|
||||
|
||||
writeSource(t, fs, filepath.Join("content", "sect1", "_index.md"), fmt.Sprintf(st, "/ss1/"))
|
||||
|
||||
@@ -80,8 +80,9 @@ func doTestTaxonomiesWithAndWithoutContentFile(t *testing.T, uglyURLs bool) {
|
||||
baseURL = "http://example.com/blog"
|
||||
titleCaseStyle = "firstupper"
|
||||
uglyURLs = %t
|
||||
paginate = 1
|
||||
defaultContentLanguage = "en"
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
[Taxonomies]
|
||||
tag = "tags"
|
||||
category = "categories"
|
||||
|
||||
@@ -377,7 +377,7 @@ func TestTemplateFuncs(t *testing.T) {
|
||||
b := newTestSitesBuilder(t).WithDefaultMultiSiteConfig()
|
||||
|
||||
homeTpl := `Site: {{ site.Language.Lang }} / {{ .Site.Language.Lang }} / {{ site.BaseURL }}
|
||||
Sites: {{ site.Sites.First.Home.Language.Lang }}
|
||||
Sites: {{ site.Sites.Default.Home.Language.Lang }}
|
||||
Hugo: {{ hugo.Generator }}
|
||||
`
|
||||
|
||||
@@ -460,7 +460,7 @@ complex: 80: 80
|
||||
func TestPartialWithZeroedArgs(t *testing.T) {
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithTemplatesAdded("index.html",
|
||||
`
|
||||
`
|
||||
X{{ partial "retval" dict }}X
|
||||
X{{ partial "retval" slice }}X
|
||||
X{{ partial "retval" "" }}X
|
||||
@@ -696,7 +696,7 @@ func TestApplyWithNamespace(t *testing.T) {
|
||||
|
||||
b.WithTemplates(
|
||||
"index.html", `
|
||||
{{ $b := slice " a " " b " " c" }}
|
||||
{{ $b := slice " a " " b " " c" }}
|
||||
{{ $a := apply $b "strings.Trim" "." " " }}
|
||||
a: {{ $a }}
|
||||
`,
|
||||
|
||||
@@ -292,11 +292,13 @@ func (s *sitesBuilder) WithDefaultMultiSiteConfig() *sitesBuilder {
|
||||
defaultMultiSiteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
paginate = 1
|
||||
disablePathToLower = true
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
|
||||
[permalinks]
|
||||
other = "/somewhere/else/:filename"
|
||||
|
||||
@@ -324,7 +326,8 @@ plaque = "plaques"
|
||||
weight = 30
|
||||
title = "På nynorsk"
|
||||
languageName = "Nynorsk"
|
||||
paginatePath = "side"
|
||||
[Languages.nn.pagination]
|
||||
path = "side"
|
||||
[Languages.nn.Taxonomies]
|
||||
lag = "lag"
|
||||
[[Languages.nn.menu.main]]
|
||||
@@ -336,7 +339,8 @@ weight = 1
|
||||
weight = 40
|
||||
title = "På bokmål"
|
||||
languageName = "Bokmål"
|
||||
paginatePath = "side"
|
||||
[Languages.nb.pagination]
|
||||
path = "side"
|
||||
[Languages.nb.Taxonomies]
|
||||
lag = "lag"
|
||||
` + commonConfigSections
|
||||
|
||||
+7
-2
@@ -1,7 +1,12 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.133.1
|
||||
HUGORELEASER_COMMITISH=47d00202e7e61769ce4d14691e43b27852c9cce4
|
||||
HUGORELEASER_TAG=v0.135.0
|
||||
HUGORELEASER_COMMITISH=f30603c47f5205e30ef83c70419f57d7eb7175ab
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -41,13 +41,21 @@ export function readInput(handle) {
|
||||
if (currentLine[i] === 10) {
|
||||
const chunk = currentLine.splice(j, i + 1);
|
||||
const arr = new Uint8Array(chunk);
|
||||
let json;
|
||||
let message;
|
||||
try {
|
||||
json = JSON.parse(new TextDecoder().decode(arr));
|
||||
message = JSON.parse(new TextDecoder().decode(arr));
|
||||
} catch (e) {
|
||||
throw new Error(`Error parsing JSON '${new TextDecoder().decode(arr)}' from stdin: ${e.message}`);
|
||||
}
|
||||
handle(json);
|
||||
|
||||
try {
|
||||
handle(message);
|
||||
} catch (e) {
|
||||
let header = message.header;
|
||||
header.err = e.message;
|
||||
writeOutput({ header: header });
|
||||
}
|
||||
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
(()=>{function s(r){let e=[],c=new Uint8Array(1024);for(;;){let n=0;try{n=Javy.IO.readSync(0,c)}catch(o){if(o.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(n<0)throw new Error("Error reading from stdin");if(n===0)break;if(e=[...e,...c.subarray(0,n)],!e.includes(10))continue;let t=0;for(let o=0;t<e.length;t++)if(e[t]===10){let u=e.splice(o,t+1),d=new Uint8Array(u),a;try{a=JSON.parse(new TextDecoder().decode(d))}catch(l){throw new Error(`Error parsing JSON '${new TextDecoder().decode(d)}' from stdin: ${l.message}`)}r(a),o=t+1}e=e.slice(t)}}function f(r){let i=new TextEncoder().encode(JSON.stringify(r)+`
|
||||
`),e=new Uint8Array(i);Javy.IO.writeSync(1,e)}var w=function(r){f({header:r.header,data:{greeting:"Hello "+r.data.name+"!"}})};console.log("Greet module loaded");s(w);})();
|
||||
(()=>{function l(r){let e=[],a=new Uint8Array(1024);for(;;){let n=0;try{n=Javy.IO.readSync(0,a)}catch(o){if(o.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(n<0)throw new Error("Error reading from stdin");if(n===0)break;if(e=[...e,...a.subarray(0,n)],!e.includes(10))continue;let t=0;for(let o=0;t<e.length;t++)if(e[t]===10){let w=e.splice(o,t+1),f=new Uint8Array(w),c;try{c=JSON.parse(new TextDecoder().decode(f))}catch(d){throw new Error(`Error parsing JSON '${new TextDecoder().decode(f)}' from stdin: ${d.message}`)}try{r(c)}catch(d){let u=c.header;u.err=d.message,i({header:u})}o=t+1}e=e.slice(t)}}function i(r){let s=new TextEncoder().encode(JSON.stringify(r)+`
|
||||
`),e=new Uint8Array(s);Javy.IO.writeSync(1,e)}var h=function(r){i({header:r.header,data:{greeting:"Hello "+r.data.name+"!"}})};console.log("Greet module loaded");l(h);})();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,13 +6,9 @@ const render = function (input) {
|
||||
const expression = data.expression;
|
||||
const options = data.options;
|
||||
const header = input.header;
|
||||
try {
|
||||
const output = katex.renderToString(expression, options);
|
||||
writeOutput({ header: header, data: { output: output } });
|
||||
} catch (e) {
|
||||
header.err = e.message;
|
||||
writeOutput({ header: header });
|
||||
}
|
||||
// Any error thrown here will be caught by the common.js readInput function.
|
||||
const output = katex.renderToString(expression, options);
|
||||
writeOutput({ header: header, data: { output: output } });
|
||||
};
|
||||
|
||||
readInput(render);
|
||||
|
||||
+12
-4
@@ -35,15 +35,19 @@ import (
|
||||
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
|
||||
)
|
||||
|
||||
const currentVersion = "v1"
|
||||
const currentVersion = 1
|
||||
|
||||
//go:embed wasm/quickjs.wasm
|
||||
var quickjsWasm []byte
|
||||
|
||||
// Header is in both the request and response.
|
||||
type Header struct {
|
||||
Version string `json:"version"`
|
||||
ID uint32 `json:"id"`
|
||||
// Major version of the protocol.
|
||||
Version uint16 `json:"version"`
|
||||
|
||||
// Unique ID for the request.
|
||||
// Note that this only needs to be unique within the current request set time window.
|
||||
ID uint32 `json:"id"`
|
||||
|
||||
// Set in the response if there was an error.
|
||||
Err string `json:"err"`
|
||||
@@ -150,7 +154,11 @@ func (p *dispatcherPool[Q, R]) Execute(ctx context.Context, q Message[Q]) (Messa
|
||||
return d.zero, call.err
|
||||
}
|
||||
|
||||
return call.response, p.Err()
|
||||
resp, err := call.response, p.Err()
|
||||
if err == nil && resp.Header.Err != "" {
|
||||
err = errors.New(resp.Header.Err)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (d *dispatcher[Q, R]) newCall(q Message[Q]) (*call[Q, R], error) {
|
||||
|
||||
@@ -45,28 +45,44 @@ func TestKatex(t *testing.T) {
|
||||
|
||||
defer d.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
runExpression := func(c *qt.C, id uint32, expression string) (Message[KatexOutput], error) {
|
||||
c.Helper()
|
||||
|
||||
input := KatexInput{
|
||||
Expression: "c = \\pm\\sqrt{a^2 + b^2}",
|
||||
Options: KatexOptions{
|
||||
Output: "html",
|
||||
DisplayMode: true,
|
||||
},
|
||||
ctx := context.Background()
|
||||
|
||||
input := KatexInput{
|
||||
Expression: expression,
|
||||
Options: KatexOptions{
|
||||
Output: "html",
|
||||
DisplayMode: true,
|
||||
ThrowOnError: true,
|
||||
},
|
||||
}
|
||||
|
||||
message := Message[KatexInput]{
|
||||
Header: Header{
|
||||
Version: currentVersion,
|
||||
ID: uint32(id),
|
||||
},
|
||||
Data: input,
|
||||
}
|
||||
|
||||
return d.Execute(ctx, message)
|
||||
}
|
||||
|
||||
message := Message[KatexInput]{
|
||||
Header: Header{
|
||||
Version: currentVersion,
|
||||
ID: uint32(32),
|
||||
},
|
||||
Data: input,
|
||||
}
|
||||
c.Run("Simple", func(c *qt.C) {
|
||||
id := uint32(32)
|
||||
result, err := runExpression(c, id, "c = \\pm\\sqrt{a^2 + b^2}")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(result.GetID(), qt.Equals, id)
|
||||
})
|
||||
|
||||
result, err := d.Execute(ctx, message)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
c.Assert(result.GetID(), qt.Equals, message.GetID())
|
||||
c.Run("Invalid expression", func(c *qt.C) {
|
||||
id := uint32(32)
|
||||
result, err := runExpression(c, id, "c & \\foo\\")
|
||||
c.Assert(err, qt.IsNotNil)
|
||||
c.Assert(result.GetID(), qt.Equals, id)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGreet(t *testing.T) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -164,10 +164,12 @@ var blockQuoteAlertRe = regexp.MustCompile(`^<p>\[!([a-zA-Z]+)\](-|\+)?[^\S\r\n]
|
||||
func resolveBlockQuoteAlert(s string) blockQuoteAlert {
|
||||
m := blockQuoteAlertRe.FindStringSubmatch(s)
|
||||
if len(m) == 4 {
|
||||
title := strings.TrimSpace(m[3])
|
||||
title = strings.TrimRight(title, "</p>")
|
||||
return blockQuoteAlert{
|
||||
typ: strings.ToLower(m[1]),
|
||||
sign: m[2],
|
||||
title: m[3],
|
||||
title: title,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,6 @@ title: "Home"
|
||||
> [!danger]
|
||||
> Do not approach or handle without protective gear.
|
||||
|
||||
|
||||
> [!tip] Callouts can have custom titles
|
||||
> Like this one.
|
||||
|
||||
@@ -135,6 +134,10 @@ title: "Home"
|
||||
> [!faq]+ Foldable callout
|
||||
> Yes! In a foldable callout, the contents are hidden when the callout is collapsed
|
||||
|
||||
> [!info] Can callouts be nested?
|
||||
> > [!important] Yes!, they can.
|
||||
> > > [!tip] You can even use multiple layers of nesting.
|
||||
|
||||
-- layouts/index.html --
|
||||
{{ .Content }}
|
||||
-- layouts/_default/_markup/render-blockquote.html --
|
||||
@@ -145,9 +148,11 @@ AlertType: {{ .AlertType }}|AlertTitle: {{ .AlertTitle }}|AlertSign: {{ .AlertSi
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContentExact("public/index.html",
|
||||
"AlertType: tip|AlertTitle: Callouts can have custom titles|AlertSign: |",
|
||||
"AlertType: tip|AlertTitle: Title-only callout</p>|AlertSign: |",
|
||||
"AlertType: faq|AlertTitle: Foldable negated callout|AlertSign: -|Text: <p>Yes!",
|
||||
"AlertType: faq|AlertTitle: Foldable callout|AlertSign: +|",
|
||||
"AlertType: tip|AlertTitle: Title-only callout|AlertSign: |",
|
||||
"AlertType: faq|AlertTitle: Foldable negated callout|AlertSign: -|Text: <p>Yes! In a foldable callout, the contents are hidden when the callout is collapsed</p>\n|",
|
||||
"AlertType: faq|AlertTitle: Foldable callout|AlertSign: +|Text: <p>Yes! In a foldable callout, the contents are hidden when the callout is collapsed</p>\n|",
|
||||
"AlertType: danger|AlertTitle: |AlertSign: |Text: <p>Do not approach or handle without protective gear.</p>\n|",
|
||||
"AlertTitle: Can callouts be nested?|",
|
||||
"AlertTitle: You can even use multiple layers of nesting.|",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,6 @@ func TestResolveBlockQuoteAlert(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c.Assert(resolveBlockQuoteAlert("<p>"+test.input), qt.Equals, test.expected, qt.Commentf("Test %d", i))
|
||||
c.Assert(resolveBlockQuoteAlert("<p>"+test.input+"</p>"), qt.Equals, test.expected, qt.Commentf("Test %d", i))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (r *htmlRenderer) renderCell(w util.BufWriter, source []byte, node ast.Node
|
||||
case gast.AlignCenter:
|
||||
alignment = "center"
|
||||
default:
|
||||
alignment = "left"
|
||||
alignment = ""
|
||||
}
|
||||
|
||||
cell := hooks.TableCell{Text: hstring.HTML(text), Alignment: alignment}
|
||||
|
||||
@@ -32,7 +32,7 @@ title = true
|
||||
| Item | In Stock | Price |
|
||||
| :---------------- | :------: | ----: |
|
||||
| Python Hat | True | 23.99 |
|
||||
| SQL **Hat** | True | 23.99 |
|
||||
| SQL **Hat** | True | 23.99 |
|
||||
| Codecademy Tee | False | 19.99 |
|
||||
| Codecademy Hoodie | False | 42.99 |
|
||||
{.foo foo="bar"}
|
||||
@@ -65,8 +65,8 @@ Attributes: {{ .Attributes }}|
|
||||
)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"table-1-thead: 0: 0: left: Month| 1: left: Savings|$",
|
||||
"table-1-tbody: 0: 0: left: January| 1: left: $250| 1: 0: left: February| 1: left: $80| 2: 0: left: March| 1: left: $420|$",
|
||||
"table-1-thead: 0: 0: : Month| 1: : Savings|$",
|
||||
"table-1-tbody: 0: 0: : January| 1: : $250| 1: 0: : February| 1: : $80| 2: 0: : March| 1: : $420|$",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+22
-1
@@ -633,7 +633,7 @@ func (c *Client) runGo(
|
||||
|
||||
argsv := collections.StringSliceToInterfaceSlice(args)
|
||||
argsv = append(argsv, hexec.WithEnviron(c.environ))
|
||||
argsv = append(argsv, hexec.WithStderr(io.MultiWriter(stderr, os.Stderr)))
|
||||
argsv = append(argsv, hexec.WithStderr(goOutputReplacerWriter{w: io.MultiWriter(stderr, os.Stderr)}))
|
||||
argsv = append(argsv, hexec.WithStdout(stdout))
|
||||
argsv = append(argsv, hexec.WithDir(c.ccfg.WorkingDir))
|
||||
argsv = append(argsv, hexec.WithContext(ctx))
|
||||
@@ -679,6 +679,24 @@ If you then run 'hugo mod graph' it should resolve itself to the most recent ver
|
||||
return nil
|
||||
}
|
||||
|
||||
var goOutputReplacer = strings.NewReplacer(
|
||||
"go: to add module requirements and sums:", "hugo: to add module requirements and sums:",
|
||||
"go mod tidy", "hugo mod tidy",
|
||||
)
|
||||
|
||||
type goOutputReplacerWriter struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (w goOutputReplacerWriter) Write(p []byte) (n int, err error) {
|
||||
s := goOutputReplacer.Replace(string(p))
|
||||
_, err = w.w.Write([]byte(s))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *Client) tidy(mods Modules, goModOnly bool) error {
|
||||
isGoMod := make(map[string]bool)
|
||||
for _, m := range mods {
|
||||
@@ -742,6 +760,9 @@ type ClientConfig struct {
|
||||
// This can be nil.
|
||||
IgnoreVendor glob.Glob
|
||||
|
||||
// Ignore any module not found errors.
|
||||
IgnoreModuleDoesNotExist bool
|
||||
|
||||
// Absolute path to the project dir.
|
||||
WorkingDir string
|
||||
|
||||
|
||||
@@ -699,6 +699,9 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
|
||||
}
|
||||
|
||||
func (c *collector) wrapModuleNotFound(err error) error {
|
||||
if c.Client.ccfg.IgnoreModuleDoesNotExist {
|
||||
return nil
|
||||
}
|
||||
err = fmt.Errorf(err.Error()+": %w", ErrNotExist)
|
||||
if c.GoModulesFilename == "" {
|
||||
return err
|
||||
|
||||
@@ -161,6 +161,16 @@ func (s *HtmlSummary) resolveParagraphTagAndSetWrapper(mt media.Type) tagReStart
|
||||
return ptag
|
||||
}
|
||||
|
||||
// Avoid counting words that are most likely HTML tokens.
|
||||
var (
|
||||
isProbablyHTMLTag = regexp.MustCompile(`^<\/?[A-Za-z]+>?$`)
|
||||
isProablyHTMLAttribute = regexp.MustCompile(`^[A-Za-z]+=["']`)
|
||||
)
|
||||
|
||||
func isProbablyHTMLToken(s string) bool {
|
||||
return s == ">" || isProbablyHTMLTag.MatchString(s) || isProablyHTMLAttribute.MatchString(s)
|
||||
}
|
||||
|
||||
// ExtractSummaryFromHTML extracts a summary from the given HTML content.
|
||||
func ExtractSummaryFromHTML(mt media.Type, input string, numWords int, isCJK bool) (result HtmlSummary) {
|
||||
result.source = input
|
||||
@@ -173,6 +183,14 @@ func ExtractSummaryFromHTML(mt media.Type, input string, numWords int, isCJK boo
|
||||
var count int
|
||||
|
||||
countWord := func(word string) int {
|
||||
word = strings.TrimSpace(word)
|
||||
if len(word) == 0 {
|
||||
return 0
|
||||
}
|
||||
if isProbablyHTMLToken(word) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if isCJK {
|
||||
word = tpl.StripHTML(word)
|
||||
runeCount := utf8.RuneCountInString(word)
|
||||
@@ -193,7 +211,7 @@ func ExtractSummaryFromHTML(mt media.Type, input string, numWords int, isCJK boo
|
||||
|
||||
for j := result.WrapperStart.High; j < high; {
|
||||
s := input[j:]
|
||||
closingIndex := strings.Index(s, "</"+ptag.tagName)
|
||||
closingIndex := strings.Index(s, "</"+ptag.tagName+">")
|
||||
|
||||
if closingIndex == -1 {
|
||||
break
|
||||
|
||||
@@ -171,7 +171,7 @@ includecontent: {{ hugo.Context.MarkupScope }}|{{ $p.Markup.Render.Content }}|
|
||||
)
|
||||
}
|
||||
|
||||
func TestPageMarkupWithoutSummary(t *testing.T) {
|
||||
func TestPageContentWithoutSummary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
@@ -195,6 +195,13 @@ This is some content about a summary and more.
|
||||
Another paragraph.
|
||||
|
||||
Third paragraph.
|
||||
-- content/p3.md --
|
||||
---
|
||||
title: "Post 3"
|
||||
date: "2020-01-01"
|
||||
summary: "This is summary in front matter."
|
||||
---
|
||||
This is content.
|
||||
-- layouts/_default/single.html --
|
||||
Single.
|
||||
Page.Summary: {{ .Summary }}|
|
||||
@@ -229,6 +236,11 @@ Summary Truncated: {{ .Truncated }}|
|
||||
"Summary Type: auto",
|
||||
"Summary Truncated: true",
|
||||
)
|
||||
|
||||
b.AssertFileContentExact("public/p3/index.html",
|
||||
"Summary: This is summary in front matter.|",
|
||||
"ContentWithoutSummary: <p>This is content.</p>\n|",
|
||||
)
|
||||
}
|
||||
|
||||
func TestPageMarkupWithoutSummaryRST(t *testing.T) {
|
||||
|
||||
@@ -49,6 +49,46 @@ func TestExtractSummaryFromHTML(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// See https://discourse.gohugo.io/t/automatic-summarys-summarylength-seems-broken-in-the-case-of-plainify/51466/4
|
||||
// Also issue 12837
|
||||
func TestExtractSummaryFromHTMLLotsOfHTMLInSummary(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
input := `
|
||||
<p>
|
||||
<div>
|
||||
<picture>
|
||||
<img src="imgs/1.jpg" alt="1"/>
|
||||
</picture>
|
||||
<picture>
|
||||
<img src="imgs/2.jpg" alt="2"/>
|
||||
</picture>
|
||||
<picture>
|
||||
<img src="imgs/3.jpg" alt="3"/>
|
||||
</picture>
|
||||
<picture>
|
||||
<img src="imgs/4.jpg" alt="4"/>
|
||||
</picture>
|
||||
<picture>
|
||||
<img src="imgs/5.jpg" alt="5"/>
|
||||
</picture>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
This is a story about a cat.
|
||||
</p>
|
||||
<p>
|
||||
The cat was white and fluffy.
|
||||
</p>
|
||||
<p>
|
||||
And it liked milk.
|
||||
</p>
|
||||
`
|
||||
|
||||
summary := ExtractSummaryFromHTML(media.Builtin.MarkdownType, input, 10, false)
|
||||
c.Assert(strings.HasSuffix(summary.Summary(), "<p>\nThis is a story about a cat.\n</p>\n<p>\nThe cat was white and fluffy.\n</p>"), qt.IsTrue)
|
||||
}
|
||||
|
||||
func TestExtractSummaryFromHTMLWithDivider(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
@@ -114,6 +154,23 @@ func TestExpandDivider(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsProbablyHTMLToken(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for i, test := range []struct {
|
||||
input string
|
||||
expect bool
|
||||
}{
|
||||
{"<p>", true},
|
||||
{"<p", true},
|
||||
{"width=\"32\"", true},
|
||||
{"width='32'", true},
|
||||
{"<p>Æøå", false},
|
||||
} {
|
||||
c.Assert(isProbablyHTMLToken(test.input), qt.Equals, test.expect, qt.Commentf("[%d] Test.expect %q", i, test.input))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSummaryFromHTML(b *testing.B) {
|
||||
b.StopTimer()
|
||||
input := "<p>First paragraph</p><p>Second paragraph</p>"
|
||||
|
||||
@@ -330,6 +330,9 @@ type FrontMatterDescriptor struct {
|
||||
// if page is a leaf bundle, the bundle folder name (ContentBaseName).
|
||||
BaseFilename string
|
||||
|
||||
// The Page's path if the page is backed by a file, else its title.
|
||||
PathOrTitle string
|
||||
|
||||
// The content file's mod time.
|
||||
ModTime time.Time
|
||||
|
||||
@@ -725,7 +728,7 @@ func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d
|
||||
return func(d *FrontMatterDescriptor) (bool, error) {
|
||||
v, found := d.PageConfig.Params[key]
|
||||
|
||||
if !found {
|
||||
if !found || v == "" || v == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -736,7 +739,7 @@ func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d
|
||||
var err error
|
||||
date, err = htime.ToTimeInDefaultLocationE(v, d.Location)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, fmt.Errorf("the %q front matter field is not a parsable date: see %s", key, d.PathOrTitle)
|
||||
}
|
||||
d.PageConfig.Params[key] = date
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package pagemeta_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
@@ -43,3 +44,99 @@ Lastmod: 2024-03-13 06:00:00 +0000 GMT
|
||||
Eq: true
|
||||
`)
|
||||
}
|
||||
|
||||
func TestDateValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- content/_index.md --
|
||||
FRONT_MATTER
|
||||
-- layouts/index.html --
|
||||
{{ .Date.UTC.Format "2006-01-02" }}
|
||||
--
|
||||
`
|
||||
errorMsg := `ERROR the "date" front matter field is not a parsable date`
|
||||
|
||||
// TOML: unquoted date/time (valid)
|
||||
f := strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = 2024-10-01
|
||||
+++
|
||||
`)
|
||||
b := hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "2024-10-01")
|
||||
|
||||
// TOML: string (valid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = "2024-10-01"
|
||||
+++
|
||||
`)
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "2024-10-01")
|
||||
|
||||
// TOML: empty string (valid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = ""
|
||||
+++
|
||||
`)
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "0001-01-01")
|
||||
|
||||
// TOML: int (valid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = 0
|
||||
+++
|
||||
`)
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "1970-01-01")
|
||||
|
||||
// TOML: string (invalid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = "2024-42-42"
|
||||
+++
|
||||
`)
|
||||
b, _ = hugolib.TestE(t, f)
|
||||
b.AssertLogContains(errorMsg)
|
||||
|
||||
// TOML: bool (invalid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = true
|
||||
+++
|
||||
`)
|
||||
b, _ = hugolib.TestE(t, f)
|
||||
b.AssertLogContains(errorMsg)
|
||||
|
||||
// TOML: float (invalid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
+++
|
||||
date = 6.7
|
||||
+++
|
||||
`)
|
||||
b, _ = hugolib.TestE(t, f)
|
||||
b.AssertLogContains(errorMsg)
|
||||
|
||||
// JSON: null (valid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
{
|
||||
"date": null
|
||||
}
|
||||
`)
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "0001-01-01")
|
||||
|
||||
// YAML: null (valid)
|
||||
f = strings.ReplaceAll(files, "FRONT_MATTER", `
|
||||
---
|
||||
date:
|
||||
---
|
||||
`)
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "0001-01-01")
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ func TestTailwindV4Basic(t *testing.T) {
|
||||
"url": "https://github.com/bep/hugo-starter-tailwind-basic.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.0.0-alpha.16",
|
||||
"tailwindcss": "^4.0.0-alpha.16"
|
||||
"@tailwindcss/cli": "^4.0.0-alpha.26",
|
||||
"tailwindcss": "^4.0.0-alpha.26"
|
||||
},
|
||||
"name": "hugo-starter-tailwind-basic",
|
||||
"version": "0.1.0"
|
||||
|
||||
@@ -562,3 +562,46 @@ Styles: {{ $r.RelPermalink }}
|
||||
|
||||
b.AssertFileContent("public/index.html", "Styles: /scss/main.css")
|
||||
}
|
||||
|
||||
// Issue 12849
|
||||
func TestDirectoryIndexes(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !dartsass.Supports() {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
|
||||
[[module.mounts]]
|
||||
source = 'assets'
|
||||
target = 'assets'
|
||||
|
||||
[[module.imports]]
|
||||
path = "github.com/gohugoio/hugoTestModule2"
|
||||
|
||||
[[module.imports.mounts]]
|
||||
source = "miscellaneous/sass"
|
||||
target = "assets/sass"
|
||||
-- go.mod --
|
||||
module hugo-github-issue-12849
|
||||
-- layouts/index.html --
|
||||
{{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }}
|
||||
{{ (resources.Get "sass/main.scss" | toCSS $opts).Content }}
|
||||
-- assets/sass/main.scss --
|
||||
@use "foo"; // directory with index file from OS file system
|
||||
@use "bar"; // directory with index file from module mount
|
||||
-- assets/sass/foo/_index.scss --
|
||||
.foo {color: red;}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
NeedsOsFS: true,
|
||||
TxtarString: files,
|
||||
}).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", ".foo{color:red}.bar{color:green}")
|
||||
}
|
||||
|
||||
@@ -165,7 +165,12 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) {
|
||||
} else if strings.HasPrefix(name, "_") {
|
||||
namePatterns = []string{"_%s.scss", "_%s.sass", "_%s.css"}
|
||||
} else {
|
||||
namePatterns = []string{"_%s.scss", "%s.scss", "_%s.sass", "%s.sass", "_%s.css", "%s.css"}
|
||||
namePatterns = []string{
|
||||
"_%s.scss", "%s.scss",
|
||||
"_%s.sass", "%s.sass",
|
||||
"_%s.css", "%s.css",
|
||||
"%s/_index.scss", "%s/_index.sass",
|
||||
}
|
||||
}
|
||||
|
||||
name = strings.TrimPrefix(name, "_")
|
||||
|
||||
@@ -111,7 +111,7 @@ moo {
|
||||
|
||||
@import "another.css";
|
||||
/* foo */
|
||||
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ body {
|
||||
body {
|
||||
background: url($image) no-repeat center/cover;
|
||||
font-family: $font;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@@ -396,7 +396,7 @@ h3 {
|
||||
|
||||
|
||||
{{ range $stylesheets }}
|
||||
{{ with . | resources.ToCSS | fingerprint }}
|
||||
{{ with . | css.Sass | fingerprint }}
|
||||
<link as="style" href="{{ .RelPermalink }}" rel="preload stylesheet">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -417,3 +417,46 @@ h3 {
|
||||
|
||||
b.AssertFileContent("public/index.html", `b.46b2d77c7ffe37ee191678f72df991ecb1319f849957151654362f09b0ef467f.css`)
|
||||
}
|
||||
|
||||
// Issue 12851
|
||||
func TestDirectoryIndexes(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !scss.Supports() {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
|
||||
[[module.mounts]]
|
||||
source = 'assets'
|
||||
target = 'assets'
|
||||
|
||||
[[module.imports]]
|
||||
path = "github.com/gohugoio/hugoTestModule2"
|
||||
|
||||
[[module.imports.mounts]]
|
||||
source = "miscellaneous/sass"
|
||||
target = "assets/sass"
|
||||
-- go.mod --
|
||||
module hugo-github-issue-12849
|
||||
-- layouts/index.html --
|
||||
{{ $opts := dict "transpiler" "libsass" "outputStyle" "compressed" }}
|
||||
{{ (resources.Get "sass/main.scss" | toCSS $opts).Content }}
|
||||
-- assets/sass/main.scss --
|
||||
@import "foo"; // directory with index file from OS file system
|
||||
@import "bar"; // directory with index file from module mount
|
||||
-- assets/sass/foo/_index.scss --
|
||||
.foo {color: red;}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
NeedsOsFS: true,
|
||||
TxtarString: files,
|
||||
}).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", ".foo{color:red}.bar{color:green}")
|
||||
}
|
||||
|
||||
@@ -105,7 +105,11 @@ func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||
} else if strings.HasPrefix(name, "_") {
|
||||
namePatterns = []string{"_%s.scss", "_%s.sass"}
|
||||
} else {
|
||||
namePatterns = []string{"_%s.scss", "%s.scss", "_%s.sass", "%s.sass"}
|
||||
namePatterns = []string{
|
||||
"_%s.scss", "%s.scss",
|
||||
"_%s.sass", "%s.sass",
|
||||
"%s/_index.scss", "%s/_index.sass",
|
||||
}
|
||||
}
|
||||
|
||||
name = strings.TrimPrefix(name, "_")
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check if a custom hugo-docker-entrypoint.sh file exists.
|
||||
if [ -f hugo-docker-entrypoint.sh ]; then
|
||||
# Execute the custom entrypoint file.
|
||||
sh hugo-docker-entrypoint.sh "$@"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# Check if a package.json file exists.
|
||||
if [ -f package.json ]; then
|
||||
# Check if node_modules exists.
|
||||
if [ ! -d node_modules ]; then
|
||||
# Install npm packages.
|
||||
# Note that we deliberately do not use `npm ci` here, as it would fail if the package-lock.json file is not up-to-date,
|
||||
# which would be the case if you run the container with a different OS or architecture than the one used to create the package-lock.json file.
|
||||
npm i
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "hugo" "$@"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
export DART_SASS_VERSION=1.79.3
|
||||
|
||||
# If $BUILDARCH=arm64, then we need to install the arm64 version of Dart Sass,
|
||||
# otherwise we install the x64 version.
|
||||
ARCH="x64"
|
||||
if [ "$BUILDARCH" = "arm64" ]; then
|
||||
ARCH="arm64"
|
||||
fi
|
||||
|
||||
cd /tmp
|
||||
curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz
|
||||
ls -ltr
|
||||
tar -xf dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz
|
||||
rm dart-sass-${DART_SASS_VERSION}-linux-${ARCH}.tar.gz && \
|
||||
# The dart-sass folder is added to the PATH by the caller.
|
||||
mv dart-sass /var/hugo/bin
|
||||
@@ -1,6 +1,6 @@
|
||||
# Test the gen commands.
|
||||
# Note that adding new commands will require updating the NUM_COMMANDS value.
|
||||
env NUM_COMMANDS=43
|
||||
env NUM_COMMANDS=44
|
||||
|
||||
hugo gen -h
|
||||
stdout 'A collection of several useful generators\.'
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Test the hugo build command (alias for hugo)
|
||||
|
||||
hugo build
|
||||
stdout 'Pages.*|1'
|
||||
stdout 'Total in'
|
||||
checkfile public/index.html
|
||||
checkfile public/p1/index.html
|
||||
grep 'IsServer: false;IsProduction: true' public/index.html
|
||||
|
||||
-- hugo.toml --
|
||||
baseURL = "http://example.org/"
|
||||
disableKinds = ["RSS", "sitemap", "robotsTXT", "404", "taxonomy", "term"]
|
||||
-- layouts/index.html --
|
||||
Home|IsServer: {{ hugo.IsServer }};IsProduction: {{ hugo.IsProduction }}|
|
||||
-- layouts/_default/single.html --
|
||||
Title: {{ .Title }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
# Test the hugo init command.
|
||||
dostounix golden/go.mod.testsubmod
|
||||
|
||||
hugo mod init testsubmod
|
||||
cmpenv go.mod $WORK/golden/go.mod.testsubmod
|
||||
|
||||
-- hugo.toml --
|
||||
title = "Hugo Modules Test"
|
||||
[module]
|
||||
[[module.imports]]
|
||||
path="github.com/bep/empty-hugo-module"
|
||||
-- golden/go.mod.testsubmod --
|
||||
module testsubmod
|
||||
|
||||
go ${GOVERSION}
|
||||
@@ -231,8 +231,8 @@ func (n *Namespace) checkComparisonArgCount(min int, others ...any) bool {
|
||||
// Conditional can be used as a ternary operator.
|
||||
//
|
||||
// It returns v1 if cond is true, else v2.
|
||||
func (n *Namespace) Conditional(cond bool, v1, v2 any) any {
|
||||
if cond {
|
||||
func (n *Namespace) Conditional(cond any, v1, v2 any) any {
|
||||
if hreflect.IsTruthful(cond) {
|
||||
return v1
|
||||
}
|
||||
return v2
|
||||
|
||||
@@ -21,10 +21,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
@@ -447,12 +446,35 @@ func TestTimeUnix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConditional(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
n := New(time.UTC, false)
|
||||
a, b := "a", "b"
|
||||
ns := New(time.UTC, false)
|
||||
|
||||
c.Assert(n.Conditional(true, a, b), qt.Equals, a)
|
||||
c.Assert(n.Conditional(false, a, b), qt.Equals, b)
|
||||
type args struct {
|
||||
cond any
|
||||
v1 any
|
||||
v2 any
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want any
|
||||
}{
|
||||
{"a", args{cond: true, v1: "true", v2: "false"}, "true"},
|
||||
{"b", args{cond: false, v1: "true", v2: "false"}, "false"},
|
||||
{"c", args{cond: 1, v1: "true", v2: "false"}, "true"},
|
||||
{"d", args{cond: 0, v1: "true", v2: "false"}, "false"},
|
||||
{"e", args{cond: "foo", v1: "true", v2: "false"}, "true"},
|
||||
{"f", args{cond: "", v1: "true", v2: "false"}, "false"},
|
||||
{"g", args{cond: []int{6, 7}, v1: "true", v2: "false"}, "true"},
|
||||
{"h", args{cond: []int{}, v1: "true", v2: "false"}, "false"},
|
||||
{"i", args{cond: nil, v1: "true", v2: "false"}, "false"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c.Assert(ns.Conditional(tt.args.cond, tt.args.v1, tt.args.v2), qt.Equals, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 9462
|
||||
|
||||
@@ -29,8 +29,9 @@ func TestThatPageIsAvailableEverywhere(t *testing.T) {
|
||||
baseURL = 'http://example.com/'
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
enableInlineShortcodes = true
|
||||
paginate = 1
|
||||
enableRobotsTXT = true
|
||||
[pagination]
|
||||
pagerSize = 1
|
||||
LANG_CONFIG
|
||||
-- content/_index.md --
|
||||
---
|
||||
@@ -191,7 +192,7 @@ title: "P1"
|
||||
|
||||
# Heading 1
|
||||
-- layouts/shortcodes/toc.html --
|
||||
{{ page.TableOfContents }}
|
||||
{{ page.TableOfContents }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
{{- range .THead }}
|
||||
<tr>
|
||||
{{- range . }}
|
||||
<th {{ printf "style=%q" (printf "text-align: %s" .Alignment) | safeHTMLAttr }}>
|
||||
<th
|
||||
{{- with .Alignment }}
|
||||
{{- printf " style=%q" (printf "text-align: %s" .) | safeHTMLAttr }}
|
||||
{{- end -}}
|
||||
>
|
||||
{{- .Text -}}
|
||||
</th>
|
||||
{{- end }}
|
||||
@@ -19,7 +23,11 @@
|
||||
{{- range .TBody }}
|
||||
<tr>
|
||||
{{- range . }}
|
||||
<td {{ printf "style=%q" (printf "text-align: %s" .Alignment) | safeHTMLAttr }}>
|
||||
<td
|
||||
{{- with .Alignment }}
|
||||
{{- printf " style=%q" (printf "text-align: %s" .) | safeHTMLAttr }}
|
||||
{{- end -}}
|
||||
>
|
||||
{{- .Text -}}
|
||||
</td>
|
||||
{{- end }}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{{- /* Deprecate site.Author.email in favor of site.Params.author.email */}}
|
||||
{{- $authorEmail := "" }}
|
||||
{{- with site.Params.author }}
|
||||
{{- if reflect.IsMap . }}
|
||||
@@ -6,14 +5,8 @@
|
||||
{{- $authorEmail = . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- with site.Author.email }}
|
||||
{{- $authorEmail = . }}
|
||||
{{- warnf "The author key in site configuration is deprecated. Use params.author.email instead." }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- /* Deprecate site.Author.name in favor of site.Params.author.name */}}
|
||||
{{- $authorName := "" }}
|
||||
{{- with site.Params.author }}
|
||||
{{- if reflect.IsMap . }}
|
||||
@@ -23,11 +16,6 @@
|
||||
{{- else }}
|
||||
{{- $authorName = . }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- with site.Author.name }}
|
||||
{{- $authorName = . }}
|
||||
{{- warnf "The author key in site configuration is deprecated. Use params.author.name instead." }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- $pctx := . }}
|
||||
|
||||
@@ -235,7 +235,7 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (types.Result[temp
|
||||
_, r, err := fileCache.GetOrCreate(key, func() (io.ReadCloser, error) {
|
||||
message := warpc.Message[warpc.KatexInput]{
|
||||
Header: warpc.Header{
|
||||
Version: "v1",
|
||||
Version: 1,
|
||||
ID: ns.id.Add(1),
|
||||
},
|
||||
Data: katexInput,
|
||||
@@ -249,9 +249,6 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (types.Result[temp
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Header.Err != "" {
|
||||
return nil, errors.New(result.Header.Err)
|
||||
}
|
||||
return hugio.NewReadSeekerNoOpCloserFromString(result.Data.Output), nil
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user