mirror of
https://github.com/gohugoio/hugo.git
synced 2026-09-02 03:32:38 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df8bd4af4f | |||
| 17e15b2148 | |||
| ca4fc587c3 | |||
| 4faaaf9c2f | |||
| 94b46c36b4 | |||
| 2b0b3b8584 | |||
| 801035bb7a | |||
| b7861e586e | |||
| 59c1158135 | |||
| fcdc454cc5 | |||
| 6cf23bf882 | |||
| 62a96cef7f | |||
| 30d9aea860 | |||
| 1f23b4949c | |||
| 5fc1639035 | |||
| 889308dd85 | |||
| 72352f205a | |||
| e3304db617 | |||
| 1ec6fa36c0 | |||
| 89bd025ebf | |||
| 62567d3820 | |||
| e10915f80a | |||
| ec3890affe | |||
| 46cccb021b | |||
| cb6e27b32a | |||
| 5bbe95f9c5 | |||
| 31d19b505d | |||
| bfa2fd683e | |||
| 88d598a049 | |||
| 352be5ba87 | |||
| d37606d2c2 | |||
| f5e54d9c7d | |||
| 42f37b4e98 | |||
| e971b7d866 |
@@ -6,10 +6,10 @@ name: Test
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
SASS_VERSION: 1.63.2
|
||||
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
|
||||
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
|
||||
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
|
||||
SASS_VERSION: 1.80.3
|
||||
DART_SASS_SHA_LINUX: 7c933edbad0a7d389192c5b79393485c088bd2c4398e32f5754c32af006a9ffd
|
||||
DART_SASS_SHA_MACOS: 79e060b0e131c3bb3c16926bafc371dc33feab122bfa8c01aa337a072097967b
|
||||
DART_SASS_SHA_WINDOWS: 0bc4708b37cd1bac4740e83ac5e3176e66b774f77fd5dd364da5b5cfc9bfb469
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
@@ -112,17 +112,17 @@ jobs:
|
||||
sass --version;
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
HUGO_BUILD_TAGS: extended,withdeploy
|
||||
- if: matrix.os == 'windows-latest'
|
||||
# See issue #11052. We limit the build to regular test (no -race flag) on Windows for now.
|
||||
name: Test
|
||||
run: |
|
||||
mage -v test;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
HUGO_BUILD_TAGS: extended,withdeploy
|
||||
- name: Build tags
|
||||
run: |
|
||||
go install -tags extended,nodeploy
|
||||
go install -tags extended
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Build for dragonfly
|
||||
run: |
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
|
||||
*.test
|
||||
imports.*
|
||||
imports.*
|
||||
dist/
|
||||
public/
|
||||
|
||||
+4
-4
@@ -21,8 +21,8 @@ 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 .
|
||||
# Optionally set HUGO_BUILD_TAGS to "none" or "withdeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=withdeploy .
|
||||
#
|
||||
# We build the extended version by default.
|
||||
ARG HUGO_BUILD_TAGS="extended"
|
||||
@@ -76,14 +76,14 @@ RUN mkdir -p /var/hugo/bin /cache && \
|
||||
# See https://github.com/gohugoio/hugo/issues/9810
|
||||
runuser -u hugo -- git config --global core.quotepath false
|
||||
|
||||
USER hugo:hugo
|
||||
VOLUME /project
|
||||
WORKDIR /project
|
||||
USER hugo:hugo
|
||||
ENV HUGO_CACHEDIR=/cache
|
||||
ENV PATH="/var/hugo/bin:$PATH"
|
||||
|
||||
COPY scripts/docker/entrypoint.sh /entrypoint.sh
|
||||
COPY --link --from=dart-sass /out/dart-sass /var/hugo/bin/dart-sass
|
||||
COPY --from=dart-sass /out/dart-sass /var/hugo/bin/dart-sass
|
||||
|
||||
# Update PATH to reflect the new dependencies.
|
||||
# For more complex setups, we should probably find a way to
|
||||
|
||||
@@ -33,8 +33,7 @@ A fast and flexible static site generator built with love by [bep], [spf13], and
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a> | <a rel="me" href="https://x.com/gohugoiov2">X</a>
|
||||
|
||||
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
|
||||
## Overview
|
||||
|
||||
Hugo is a [static site generator] written in [Go], optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.
|
||||
@@ -68,6 +67,8 @@ See the [features] section of the documentation for a comprehensive summary of H
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
|
||||
<a href="https://route4me.com/" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg" width="200" alt="Route Planning & Route Optimization Software"></a>
|
||||
|
||||
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
|
||||
</p>
|
||||
|
||||
## Installation
|
||||
|
||||
Vendored
+19
-6
@@ -430,12 +430,25 @@ func (p *Partition[K, V]) doGetOrCreateWitTimeout(key K, duration time.Duration,
|
||||
errch := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
v, _, err := p.c.GetOrCreate(key, create)
|
||||
if err != nil {
|
||||
errch <- err
|
||||
return
|
||||
}
|
||||
resultch <- v
|
||||
var (
|
||||
v V
|
||||
err error
|
||||
)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if rerr, ok := r.(error); ok {
|
||||
err = rerr
|
||||
} else {
|
||||
err = fmt.Errorf("panic: %v", r)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
errch <- err
|
||||
} else {
|
||||
resultch <- v
|
||||
}
|
||||
}()
|
||||
v, _, err = p.c.GetOrCreate(key, create)
|
||||
}()
|
||||
|
||||
select {
|
||||
|
||||
Vendored
+55
@@ -14,8 +14,11 @@
|
||||
package dynacache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
@@ -165,6 +168,58 @@ func TestClear(t *testing.T) {
|
||||
cache.adjustCurrentMaxSize()
|
||||
}
|
||||
|
||||
func TestPanicInCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
cache := newTestCache(t)
|
||||
|
||||
p1 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", OptionsPartition{Weight: 30, ClearWhen: ClearOnRebuild})
|
||||
|
||||
willPanic := func(i int) func() {
|
||||
return func() {
|
||||
p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) {
|
||||
panic(errors.New(key))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrCreateWitTimeout needs to recover from panics in the create func.
|
||||
willErr := func(i int) error {
|
||||
_, err := p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) {
|
||||
return testItem{}, errors.New(key)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
c.Assert(willPanic(i), qt.PanicMatches, fmt.Sprintf("panic-%d", i))
|
||||
c.Assert(willErr(i), qt.ErrorMatches, fmt.Sprintf("error-%d", i))
|
||||
}
|
||||
}
|
||||
|
||||
// Test the same keys again without the panic.
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
v, err := p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) {
|
||||
return testItem{
|
||||
name: key,
|
||||
}, nil
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(v.name, qt.Equals, fmt.Sprintf("panic-%d", i))
|
||||
|
||||
v, err = p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) {
|
||||
return testItem{
|
||||
name: key,
|
||||
}, nil
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(v.name, qt.Equals, fmt.Sprintf("error-%d", i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdjustCurrentMaxSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
+20
-1
@@ -42,6 +42,7 @@ import (
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
@@ -66,6 +67,12 @@ func Execute(args []string) error {
|
||||
}
|
||||
args = mapLegacyArgs(args)
|
||||
cd, err := x.Execute(context.Background(), args)
|
||||
if cd != nil {
|
||||
if closer, ok := cd.Root.Command.(types.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err == errHelp {
|
||||
cd.CobraCommand.Help()
|
||||
@@ -149,6 +156,18 @@ func (r *rootCommand) isVerbose() bool {
|
||||
return r.logger.Level() <= logg.LevelInfo
|
||||
}
|
||||
|
||||
func (r *rootCommand) Close() error {
|
||||
if r.hugoSites != nil {
|
||||
r.hugoSites.DeleteFunc(func(key configKey, value *hugolib.HugoSites) bool {
|
||||
if value != nil {
|
||||
value.Close()
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rootCommand) Build(cd *simplecobra.Commandeer, bcfg hugolib.BuildCfg, cfg config.Provider) (*hugolib.HugoSites, error) {
|
||||
h, err := r.Hugo(cfg)
|
||||
if err != nil {
|
||||
@@ -488,7 +507,7 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
|
||||
return loggers.New(optsLogger), nil
|
||||
}
|
||||
|
||||
func (r *rootCommand) Reset() {
|
||||
func (r *rootCommand) resetLogs() {
|
||||
r.logger.Reset()
|
||||
loggers.Log().Reset()
|
||||
}
|
||||
|
||||
+3
-27
@@ -11,21 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
|
||||
// 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.
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package commands
|
||||
|
||||
@@ -33,7 +20,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/gohugoio/hugo/deploy"
|
||||
"github.com/gohugoio/hugo/deploy/deployconfig"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -60,17 +46,7 @@ documentation.
|
||||
return deployer.Deploy(ctx)
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
|
||||
_ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions)
|
||||
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
|
||||
cmd.Flags().Bool("dryRun", false, "dry run")
|
||||
cmd.Flags().Bool("force", false, "force upload of all files")
|
||||
cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
|
||||
cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
|
||||
_ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions)
|
||||
cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
|
||||
_ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions)
|
||||
applyDeployFlags(cmd, r)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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 commands
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/deploy/deployconfig"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func applyDeployFlags(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
|
||||
_ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions)
|
||||
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
|
||||
cmd.Flags().Bool("dryRun", false, "dry run")
|
||||
cmd.Flags().Bool("force", false, "force upload of all files")
|
||||
cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
|
||||
cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
|
||||
_ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions)
|
||||
cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
|
||||
_ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions)
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build nodeploy
|
||||
// +build nodeploy
|
||||
//go:build !withdeploy
|
||||
// +build !withdeploy
|
||||
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
@@ -31,6 +31,7 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -40,9 +41,10 @@ func newDeployCommand() simplecobra.Commander {
|
||||
return &simpleCommand{
|
||||
name: "deploy",
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
return nil
|
||||
return errors.New("deploy not supported in this version of Hugo; install a release with 'withdeploy' in the archive filename or build yourself with the 'withdeploy' build tag. Also see https://github.com/gohugoio/hugo/pull/12995")
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyDeployFlags(cmd, r)
|
||||
cmd.Hidden = true
|
||||
},
|
||||
}
|
||||
|
||||
+34
-24
@@ -27,7 +27,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
@@ -136,10 +135,6 @@ func (e *hugoBuilderErrState) wasErr() bool {
|
||||
return e.waserr
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) errCount() int {
|
||||
return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
|
||||
}
|
||||
|
||||
// getDirList provides NewWatcher() with a list of directories to watch for changes.
|
||||
func (c *hugoBuilder) getDirList() ([]string, error) {
|
||||
h, err := c.hugo()
|
||||
@@ -345,7 +340,6 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
|
||||
for {
|
||||
select {
|
||||
case changes := <-c.r.changesFromBuild:
|
||||
c.errState.setBuildErr(nil)
|
||||
unlock, err := h.LockBuild()
|
||||
if err != nil {
|
||||
c.r.logger.Errorln("Failed to acquire a build lock: %s", err)
|
||||
@@ -358,7 +352,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
|
||||
}
|
||||
if c.s != nil && c.s.doLiveReload {
|
||||
doReload := c.changeDetector == nil || len(c.changeDetector.changed()) > 0
|
||||
doReload = doReload || c.showErrorInBrowser && c.errCount() > 0
|
||||
doReload = doReload || c.showErrorInBrowser && c.errState.buildErr() != nil
|
||||
if doReload {
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
@@ -372,7 +366,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
|
||||
return
|
||||
}
|
||||
c.handleEvents(watcher, staticSyncer, evs, configSet)
|
||||
if c.showErrorInBrowser && c.errCount() > 0 {
|
||||
if c.showErrorInBrowser && c.errState.buildErr() != nil {
|
||||
// Need to reload browser to show the error
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
@@ -419,11 +413,17 @@ func (c *hugoBuilder) build() error {
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) buildSites(noBuildLock bool) (err error) {
|
||||
h, err := c.hugo()
|
||||
defer func() {
|
||||
c.errState.setBuildErr(err)
|
||||
}()
|
||||
|
||||
var h *hugolib.HugoSites
|
||||
h, err = c.hugo()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
return h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
|
||||
err = h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock})
|
||||
return
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
|
||||
@@ -619,6 +619,9 @@ func (c *hugoBuilder) fullRebuild(changeType string) {
|
||||
// Set the processing on pause until the state is recovered.
|
||||
c.errState.setPaused(true)
|
||||
c.handleBuildErr(err, "Failed to reload config")
|
||||
if c.s.doLiveReload {
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
} else {
|
||||
c.errState.setPaused(false)
|
||||
}
|
||||
@@ -1081,37 +1084,44 @@ func (c *hugoBuilder) printChangeDetected(typ string) {
|
||||
c.r.logger.Println(htime.Now().Format(layout))
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
|
||||
func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) {
|
||||
defer func() {
|
||||
c.errState.setBuildErr(err)
|
||||
}()
|
||||
if err := c.errState.buildErr(); err != nil {
|
||||
ferrs := herrors.UnwrapFileErrorsWithErrorContext(err)
|
||||
for _, err := range ferrs {
|
||||
events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write})
|
||||
}
|
||||
}
|
||||
c.errState.setBuildErr(nil)
|
||||
h, err := c.hugo()
|
||||
var h *hugolib.HugoSites
|
||||
h, err = c.hugo()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
|
||||
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) error {
|
||||
c.errState.setBuildErr(nil)
|
||||
h, err := c.hugo()
|
||||
func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error) {
|
||||
defer func() {
|
||||
c.errState.setBuildErr(err)
|
||||
}()
|
||||
|
||||
var h *hugolib.HugoSites
|
||||
h, err = c.hugo()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
whatChanged := &hugolib.WhatChanged{}
|
||||
whatChanged.Add(ids...)
|
||||
err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()})
|
||||
c.errState.setBuildErr(err)
|
||||
return err
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) reloadConfig() error {
|
||||
c.r.Reset()
|
||||
c.r.resetLogs()
|
||||
c.r.configVersionID.Add(1)
|
||||
|
||||
if err := c.withConfE(func(conf *commonConfig) error {
|
||||
|
||||
+11
-13
@@ -648,9 +648,8 @@ func (c *serverCommand) setServerInfoInConfig() error {
|
||||
}
|
||||
|
||||
func (c *serverCommand) getErrorWithContext() any {
|
||||
errCount := c.errCount()
|
||||
|
||||
if errCount == 0 {
|
||||
buildErr := c.errState.buildErr()
|
||||
if buildErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -659,7 +658,7 @@ func (c *serverCommand) getErrorWithContext() any {
|
||||
m["Error"] = cleanErrorLog(c.r.logger.Errors())
|
||||
|
||||
m["Version"] = hugo.BuildVersionString()
|
||||
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
|
||||
ferrors := herrors.UnwrapFileErrorsWithErrorContext(buildErr)
|
||||
m["Files"] = ferrors
|
||||
|
||||
return m
|
||||
@@ -830,22 +829,25 @@ func (c *serverCommand) fixURL(baseURLFromConfig, baseURLFromFlag string, port i
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (c *serverCommand) partialReRender(urls ...string) error {
|
||||
func (c *serverCommand) partialReRender(urls ...string) (err error) {
|
||||
defer func() {
|
||||
c.errState.setWasErr(false)
|
||||
}()
|
||||
c.errState.setBuildErr(nil)
|
||||
visited := types.NewEvictingStringQueue(len(urls))
|
||||
for _, url := range urls {
|
||||
visited.Add(url)
|
||||
}
|
||||
|
||||
h, err := c.hugo()
|
||||
var h *hugolib.HugoSites
|
||||
h, err = c.hugo()
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
// Note: We do not set NoBuildLock as the file lock is not acquired at this stage.
|
||||
return h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
|
||||
err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *serverCommand) serve() error {
|
||||
@@ -1012,10 +1014,6 @@ func (c *serverCommand) serve() error {
|
||||
c.r.Println("Error:", err)
|
||||
}
|
||||
|
||||
if h := c.hugoTry(); h != nil {
|
||||
h.Close()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
wg2, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
@@ -21,6 +21,8 @@ const (
|
||||
ErrRemoteGetCSV = "error-remote-getcsv"
|
||||
|
||||
WarnFrontMatterParamsOverrides = "warning-frontmatter-params-overrides"
|
||||
WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html"
|
||||
WarnGoldmarkRawHTML = "warning-goldmark-raw-html"
|
||||
)
|
||||
|
||||
// Field/method names with special meaning.
|
||||
|
||||
@@ -137,7 +137,7 @@ var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtim
|
||||
|
||||
const deferredPrefix = "__hdeferred/"
|
||||
|
||||
var deferredStringToRemove = regexp.MustCompile(`executing "__hdeferred/.*" `)
|
||||
var deferredStringToRemove = regexp.MustCompile(`executing "__hdeferred/.*?" `)
|
||||
|
||||
// ImproveRenderErr improves the error message for rendering errors.
|
||||
func ImproveRenderErr(inErr error) (outErr error) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package hugo
|
||||
|
||||
var IsWithdeploy = true
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
//go:build !withdeploy
|
||||
// +build !withdeploy
|
||||
|
||||
package hugo
|
||||
|
||||
var IsWithdeploy = false
|
||||
@@ -152,6 +152,9 @@ func BuildVersionString() string {
|
||||
if IsExtended {
|
||||
version += "+extended"
|
||||
}
|
||||
if IsWithdeploy {
|
||||
version += "+withdeploy"
|
||||
}
|
||||
|
||||
osArch := bi.GoOS + "/" + bi.GoArch
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 136,
|
||||
PatchLevel: 2,
|
||||
Suffix: "",
|
||||
Minor: 138,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package deploy
|
||||
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package deploy
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !solaris && !nodeploy
|
||||
// +build !solaris,!nodeploy
|
||||
//go:build !solaris && withdeploy
|
||||
// +build !solaris,withdeploy
|
||||
|
||||
package deploy
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package deploy
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package deployconfig
|
||||
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package deploy
|
||||
|
||||
|
||||
Vendored
+7
@@ -98,6 +98,8 @@ type Deps struct {
|
||||
// TODO(bep) rethink this re. a plugin setup, but this will have to do for now.
|
||||
WasmDispatchers *warpc.Dispatchers
|
||||
|
||||
isClosed bool
|
||||
|
||||
*globalErrHandler
|
||||
}
|
||||
|
||||
@@ -345,6 +347,11 @@ func (d *Deps) TextTmpl() tpl.TemplateParseFinder {
|
||||
}
|
||||
|
||||
func (d *Deps) Close() error {
|
||||
if d.isClosed {
|
||||
return nil
|
||||
}
|
||||
d.isClosed = true
|
||||
|
||||
if d.MemCache != nil {
|
||||
d.MemCache.Stop()
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ require (
|
||||
github.com/bep/gitmap v1.6.0
|
||||
github.com/bep/goat v0.5.0
|
||||
github.com/bep/godartsass v1.2.0
|
||||
github.com/bep/godartsass/v2 v2.1.0
|
||||
github.com/bep/godartsass/v2 v2.2.0
|
||||
github.com/bep/golibsass v1.2.0
|
||||
github.com/bep/gowebp v0.3.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/lazycache v0.7.0
|
||||
github.com/bep/logg v0.4.0
|
||||
github.com/bep/mclib v1.20400.20402
|
||||
github.com/bep/overlayfs v0.9.2
|
||||
|
||||
@@ -133,6 +133,8 @@ 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/godartsass/v2 v2.2.0 h1:3hO9Dt4BOnxkKmRxc+OpoKVFrDvBycpSCXEdElVAMVI=
|
||||
github.com/bep/godartsass/v2 v2.2.0/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo=
|
||||
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=
|
||||
@@ -143,6 +145,10 @@ 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=
|
||||
github.com/bep/lazycache v0.4.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc=
|
||||
github.com/bep/lazycache v0.6.0 h1:0vCgFo7TBtMQpSx64jnH1sagmw0ZougIFRpsqPHTa5U=
|
||||
github.com/bep/lazycache v0.6.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc=
|
||||
github.com/bep/lazycache v0.7.0 h1:VM257SkkjcR9z55eslXTkUIX8QMNKoqQRNKV/4xIkCY=
|
||||
github.com/bep/lazycache v0.7.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc=
|
||||
github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ=
|
||||
github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0=
|
||||
github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA=
|
||||
|
||||
@@ -246,11 +246,11 @@ func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fss := make([]FileMetaInfo, len(roots))
|
||||
for i, r := range roots {
|
||||
fss := make([]FileMetaInfo, 0, len(roots))
|
||||
for _, r := range roots {
|
||||
if r.fiSingleFile != nil {
|
||||
// A single file mount.
|
||||
fss[i] = r.fiSingleFile
|
||||
fss = append(fss, r.fiSingleFile)
|
||||
continue
|
||||
}
|
||||
bfs := NewBasePathFs(fs.Fs, r.To)
|
||||
@@ -261,9 +261,9 @@ func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) {
|
||||
fs = decorateDirs(fs, r.Meta)
|
||||
fi, err := fs.Stat("")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("RootMappingFs.Dirs: %w", err)
|
||||
continue
|
||||
}
|
||||
fss[i] = fi.(FileMetaInfo)
|
||||
fss = append(fss, fi.(FileMetaInfo))
|
||||
}
|
||||
|
||||
return fss, nil
|
||||
|
||||
@@ -71,9 +71,9 @@ tags_weight: %d
|
||||
{{ $pageGroups := slice $cool $blue }}
|
||||
{{ $weighted := slice $wp1 $wp2 }}
|
||||
|
||||
{{ printf "pages:%d:%T:%v/%v" (len $pages) $pages (index $pages 0) (index $pages 1) }}
|
||||
{{ printf "pageGroups:%d:%T:%v/%v" (len $pageGroups) $pageGroups (index (index $pageGroups 0).Pages 0) (index (index $pageGroups 1).Pages 0)}}
|
||||
{{ printf "weightedPages:%d::%T:%v" (len $weighted) $weighted $weighted | safeHTML }}
|
||||
{{ printf "pages:%d:%T:%s|%s" (len $pages) $pages (index $pages 0).Path (index $pages 1).Path }}
|
||||
{{ printf "pageGroups:%d:%T:%s|%s" (len $pageGroups) $pageGroups (index (index $pageGroups 0).Pages 0).Path (index (index $pageGroups 1).Pages 0).Path}}
|
||||
{{ printf "weightedPages:%d:%T" (len $weighted) $weighted | safeHTML }}
|
||||
|
||||
`)
|
||||
b.CreateSites().Build(BuildCfg{})
|
||||
@@ -82,9 +82,9 @@ tags_weight: %d
|
||||
c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"pages:2:page.Pages:Page(/page1)/Page(/page2)",
|
||||
"pageGroups:2:page.PagesGroup:Page(/page1)/Page(/page2)",
|
||||
`weightedPages:2::page.WeightedPages:[WeightedPage(10,"Page") WeightedPage(20,"Page")]`)
|
||||
"pages:2:page.Pages:/page1|/page2",
|
||||
"pageGroups:2:page.PagesGroup:/page1|/page2",
|
||||
`weightedPages:2:page.WeightedPages`)
|
||||
}
|
||||
|
||||
func TestUnionFunc(t *testing.T) {
|
||||
@@ -189,7 +189,7 @@ tags_weight: %d
|
||||
{{ $appendStrings := slice "a" "b" | append "c" "d" "e" }}
|
||||
{{ $appendStringsSlice := slice "a" "b" "c" | append (slice "c" "d") }}
|
||||
|
||||
{{ printf "pages:%d:%T:%v/%v" (len $pages) $pages (index $pages 0) (index $pages 1) }}
|
||||
{{ printf "pages:%d:%T:%s|%s" (len $pages) $pages (index $pages 0).Path (index $pages 1).Path }}
|
||||
{{ printf "appendPages:%d:%T:%v/%v" (len $appendPages) $appendPages (index $appendPages 0).Kind (index $appendPages 8).Kind }}
|
||||
{{ printf "appendStrings:%T:%v" $appendStrings $appendStrings }}
|
||||
{{ printf "appendStringsSlice:%T:%v" $appendStringsSlice $appendStringsSlice }}
|
||||
@@ -207,7 +207,7 @@ tags_weight: %d
|
||||
c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"pages:2:page.Pages:Page(/page2)/Page(/page1)",
|
||||
"pages:2:page.Pages:/page2|/page1",
|
||||
"appendPages:9:page.Pages:home/page",
|
||||
"appendStrings:[]string:[a b c d e]",
|
||||
"appendStringsSlice:[]string:[a b c c d]",
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/gohugoio/hugo/hugolib/doctree"
|
||||
"github.com/gohugoio/hugo/hugolib/pagesfromdata"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
@@ -109,6 +110,11 @@ type pageMap struct {
|
||||
cfg contentMapConfig
|
||||
}
|
||||
|
||||
// Invoked on rebuilds.
|
||||
func (m *pageMap) Reset() {
|
||||
m.pageReverseIndex.Reset()
|
||||
}
|
||||
|
||||
// pageTrees holds pages and resources in a tree structure for all sites/languages.
|
||||
// Each site gets its own tree set via the Shape method.
|
||||
type pageTrees struct {
|
||||
@@ -958,9 +964,7 @@ type contentTreeReverseIndex struct {
|
||||
}
|
||||
|
||||
func (c *contentTreeReverseIndex) Reset() {
|
||||
c.contentTreeReverseIndexMap = &contentTreeReverseIndexMap{
|
||||
m: make(map[any]contentNodeI),
|
||||
}
|
||||
c.init.ResetWithLock().Unlock()
|
||||
}
|
||||
|
||||
func (c *contentTreeReverseIndex) Get(key any) contentNodeI {
|
||||
@@ -972,7 +976,7 @@ func (c *contentTreeReverseIndex) Get(key any) contentNodeI {
|
||||
}
|
||||
|
||||
type contentTreeReverseIndexMap struct {
|
||||
init sync.Once
|
||||
init lazy.OnceMore
|
||||
m map[any]contentNodeI
|
||||
}
|
||||
|
||||
|
||||
@@ -220,6 +220,18 @@ target = 'content'
|
||||
source = 'content2'
|
||||
target = 'content/c2'
|
||||
[[module.mounts]]
|
||||
source = 'content3'
|
||||
target = 'content/watchdisabled'
|
||||
disableWatch = true
|
||||
[[module.mounts]]
|
||||
source = 'content4'
|
||||
target = 'content/excludedsome'
|
||||
excludeFiles = 'p1.md'
|
||||
[[module.mounts]]
|
||||
source = 'content5'
|
||||
target = 'content/excludedall'
|
||||
excludeFiles = '/**'
|
||||
[[module.mounts]]
|
||||
source = "hugo_stats.json"
|
||||
target = "assets/watching/hugo_stats.json"
|
||||
-- hugo_stats.json --
|
||||
@@ -230,12 +242,27 @@ foo
|
||||
-- themes/t1/layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
-- themes/t1/static/f1.txt --
|
||||
-- content3/p1.md --
|
||||
-- content4/p1.md --
|
||||
-- content4/p2.md --
|
||||
-- content5/p3.md --
|
||||
-- content5/p4.md --
|
||||
`
|
||||
b := hugolib.Test(t, files)
|
||||
bfs := b.H.BaseFs
|
||||
watchFilenames := bfs.WatchFilenames()
|
||||
// []string{"/hugo_stats.json", "/content", "/content2", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"}
|
||||
b.Assert(watchFilenames, qt.HasLen, 6)
|
||||
watchFilenames := toSlashes(bfs.WatchFilenames())
|
||||
|
||||
// content3 has disableWatch = true
|
||||
// content5 has excludeFiles = '/**'
|
||||
b.Assert(watchFilenames, qt.DeepEquals, []string{"/hugo_stats.json", "/content", "/content2", "/content4", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"})
|
||||
}
|
||||
|
||||
func toSlashes(in []string) []string {
|
||||
out := make([]string, len(in))
|
||||
for i, s := range in {
|
||||
out[i] = filepath.ToSlash(s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestNoSymlinks(t *testing.T) {
|
||||
|
||||
@@ -179,9 +179,6 @@ type hugoSitesInit struct {
|
||||
// Loads the data from all of the /data folders.
|
||||
data *lazy.Init
|
||||
|
||||
// Performs late initialization (before render) of the templates.
|
||||
layouts *lazy.Init
|
||||
|
||||
// Loads the Git info and CODEOWNERS for all the pages if enabled.
|
||||
gitInfo *lazy.Init
|
||||
}
|
||||
|
||||
@@ -250,10 +250,6 @@ func (h *HugoSites) process(ctx context.Context, l logg.LevelLogger, config *Bui
|
||||
l = l.WithField("step", "process")
|
||||
defer loggers.TimeTrackf(l, time.Now(), nil, "")
|
||||
|
||||
if _, err := h.init.layouts.Do(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(events) > 0 {
|
||||
// This is a rebuild triggered from file events.
|
||||
return h.processPartialFileEvents(ctx, l, config, init, events)
|
||||
@@ -1067,8 +1063,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
|
||||
}
|
||||
|
||||
if tmplChanged || i18nChanged {
|
||||
// TODO(bep) we should split this, but currently the loading of i18n and layout files are tied together. See #12048.
|
||||
h.init.layouts.Reset()
|
||||
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
|
||||
// TODO(bep) this could probably be optimized to somehow
|
||||
// only load the changed templates and its dependencies, but that is non-trivial.
|
||||
@@ -1141,10 +1135,6 @@ func (s *Site) handleContentAdapterChanges(bi pagesfromdata.BuildInfo, buildConf
|
||||
}
|
||||
|
||||
func (h *HugoSites) processContentAdaptersOnRebuild(ctx context.Context, buildConfig *BuildCfg) error {
|
||||
// Make sure the layouts are initialized.
|
||||
if _, err := h.init.layouts.Do(context.Background()); err != nil {
|
||||
return err
|
||||
}
|
||||
g := rungroup.Run[*pagesfromdata.PagesFromTemplate](ctx, rungroup.Config[*pagesfromdata.PagesFromTemplate]{
|
||||
NumWorkers: h.numWorkers,
|
||||
Handle: func(ctx context.Context, p *pagesfromdata.PagesFromTemplate) error {
|
||||
|
||||
@@ -2,6 +2,7 @@ package hugolib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"golang.org/x/tools/txtar"
|
||||
)
|
||||
@@ -246,11 +248,6 @@ func (s *IntegrationTestBuilder) AssertBuildCountGitInfo(count int) {
|
||||
s.Assert(s.H.init.gitInfo.InitCount(), qt.Equals, count)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertBuildCountLayouts(count int) {
|
||||
s.Helper()
|
||||
s.Assert(s.H.init.layouts.InitCount(), qt.Equals, count)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertFileCount(dirname string, expected int) {
|
||||
s.Helper()
|
||||
fs := s.fs.WorkingDirReadOnly
|
||||
@@ -299,6 +296,12 @@ func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...s
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertFileContentEquals(filename string, match string) {
|
||||
s.Helper()
|
||||
content := s.FileContent(filename)
|
||||
s.Assert(content, qt.Equals, match, qt.Commentf(match))
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches ...string) {
|
||||
s.Helper()
|
||||
content := s.FileContent(filename)
|
||||
@@ -307,6 +310,16 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertNoRenderShortcodesArtifacts() {
|
||||
s.Helper()
|
||||
for _, p := range s.H.Pages() {
|
||||
content, err := p.Content(context.Background())
|
||||
s.Assert(err, qt.IsNil)
|
||||
comment := qt.Commentf("Page: %s\n%s", p.Path(), content)
|
||||
s.Assert(strings.Contains(cast.ToString(content), "__hugo_ctx"), qt.IsFalse, comment)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertPublishDir(matches ...string) {
|
||||
s.AssertFs(s.fs.PublishDir, matches...)
|
||||
}
|
||||
@@ -421,6 +434,12 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder {
|
||||
s.Assert(err, qt.IsNil)
|
||||
}
|
||||
|
||||
s.Cleanup(func() {
|
||||
if h := s.H; h != nil {
|
||||
s.Assert(h.Close(), qt.IsNil)
|
||||
}
|
||||
})
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -834,6 +853,11 @@ type IntegrationTestConfig struct {
|
||||
|
||||
// The files to use on txtar format, see
|
||||
// https://pkg.go.dev/golang.org/x/exp/cmd/txtar
|
||||
// There are some conentions used in this test setup.
|
||||
// - §§§ can be used to wrap code fences.
|
||||
// - §§ can be used to wrap multiline strings.
|
||||
// - filenames prefixed with sourcefilename: will be read from the file system relative to the current dir.
|
||||
// - filenames with a .png or .jpg extension will be treated as binary and base64 decoded.
|
||||
TxtarString string
|
||||
|
||||
// COnfig to use as the base. We will also read the config from the txtar.
|
||||
|
||||
+18
-106
@@ -105,94 +105,6 @@ Menu Main: {{ partial "menu.html" (dict "page" . "menu" "main") }}`,
|
||||
"/sect3/|Sect3s|Sect3s|0|-|-|")
|
||||
}
|
||||
|
||||
// related issue #7594
|
||||
func TestMenusSort(t *testing.T) {
|
||||
b := newTestSitesBuilder(t).WithSimpleConfigFile()
|
||||
|
||||
b.WithTemplatesAdded("index.html", `
|
||||
{{ range $k, $v := .Site.Menus.main }}
|
||||
Default1|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
|
||||
{{ range $k, $v := .Site.Menus.main.ByWeight }}
|
||||
ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
|
||||
{{ range $k, $v := (.Site.Menus.main.ByWeight).Reverse }}
|
||||
Reverse|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
|
||||
{{ range $k, $v := .Site.Menus.main }}
|
||||
Default2|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
|
||||
{{ range $k, $v := .Site.Menus.main.ByWeight }}
|
||||
ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
|
||||
{{ range $k, $v := .Site.Menus.main }}
|
||||
Default3|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
|
||||
`)
|
||||
|
||||
b.WithContent("_index.md", `
|
||||
---
|
||||
title: Home
|
||||
menu:
|
||||
main:
|
||||
weight: 100
|
||||
---`)
|
||||
|
||||
b.WithContent("blog/A.md", `
|
||||
---
|
||||
title: "A"
|
||||
menu:
|
||||
main:
|
||||
weight: 10
|
||||
---
|
||||
`)
|
||||
|
||||
b.WithContent("blog/B.md", `
|
||||
---
|
||||
title: "B"
|
||||
menu:
|
||||
main:
|
||||
weight: 20
|
||||
---
|
||||
`)
|
||||
b.WithContent("blog/C.md", `
|
||||
---
|
||||
title: "C"
|
||||
menu:
|
||||
main:
|
||||
weight: 30
|
||||
---
|
||||
`)
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
`Default1|0|10|A|/blog/a/|Page(/blog/a)
|
||||
Default1|1|20|B|/blog/b/|Page(/blog/b)
|
||||
Default1|2|30|C|/blog/c/|Page(/blog/c)
|
||||
Default1|3|100|Home|/|Page(/)
|
||||
|
||||
ByWeight|0|10|A|/blog/a/|Page(/blog/a)
|
||||
ByWeight|1|20|B|/blog/b/|Page(/blog/b)
|
||||
ByWeight|2|30|C|/blog/c/|Page(/blog/c)
|
||||
ByWeight|3|100|Home|/|Page(/)
|
||||
|
||||
Reverse|0|100|Home|/|Page(/)
|
||||
Reverse|1|30|C|/blog/c/|Page(/blog/c)
|
||||
Reverse|2|20|B|/blog/b/|Page(/blog/b)
|
||||
Reverse|3|10|A|/blog/a/|Page(/blog/a)
|
||||
|
||||
Default2|0|10|A|/blog/a/|Page(/blog/a)
|
||||
Default2|1|20|B|/blog/b/|Page(/blog/b)
|
||||
Default2|2|30|C|/blog/c/|Page(/blog/c)
|
||||
Default2|3|100|Home|/|Page(/)
|
||||
|
||||
ByWeight|0|10|A|/blog/a/|Page(/blog/a)
|
||||
ByWeight|1|20|B|/blog/b/|Page(/blog/b)
|
||||
ByWeight|2|30|C|/blog/c/|Page(/blog/c)
|
||||
ByWeight|3|100|Home|/|Page(/)
|
||||
|
||||
Default3|0|10|A|/blog/a/|Page(/blog/a)
|
||||
Default3|1|20|B|/blog/b/|Page(/blog/b)
|
||||
Default3|2|30|C|/blog/c/|Page(/blog/c)
|
||||
Default3|3|100|Home|/|Page(/)`,
|
||||
)
|
||||
}
|
||||
|
||||
func TestMenusFrontMatter(t *testing.T) {
|
||||
b := newTestSitesBuilder(t).WithSimpleConfigFile()
|
||||
|
||||
@@ -437,8 +349,8 @@ url = "/blog/post3"
|
||||
commonTempl := `
|
||||
Main: {{ len .Site.Menus.main }}
|
||||
{{ range .Site.Menus.main }}
|
||||
{{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page }}
|
||||
{{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page }}
|
||||
{{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page.Path }}
|
||||
{{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page.Path }}
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
@@ -494,34 +406,34 @@ title: "Contact: With No Menu Defined"
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
Main: 5
|
||||
Home|HasMenuCurrent: false|Page: Page(/)
|
||||
Blog|HasMenuCurrent: false|Page: Page(/blog)
|
||||
My Post 2: With Menu Defined|HasMenuCurrent: false|Page: Page(/blog/post2)
|
||||
My Post 3|HasMenuCurrent: false|Page: Page(/blog/post3)
|
||||
Contact Us|HasMenuCurrent: false|Page: Page(/contact)
|
||||
Home|HasMenuCurrent: false|Page: /
|
||||
Blog|HasMenuCurrent: false|Page: /blog
|
||||
My Post 2: With Menu Defined|HasMenuCurrent: false|Page: /blog/post2
|
||||
My Post 3|HasMenuCurrent: false|Page: /blog/post3
|
||||
Contact Us|HasMenuCurrent: false|Page: /contact
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/blog/post1/index.html", `
|
||||
Home|HasMenuCurrent: false|Page: Page(/)
|
||||
Blog|HasMenuCurrent: true|Page: Page(/blog)
|
||||
Home|HasMenuCurrent: false|Page: /
|
||||
Blog|HasMenuCurrent: true|Page: /blog
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/blog/post2/index.html", `
|
||||
Home|HasMenuCurrent: false|Page: Page(/)
|
||||
Blog|HasMenuCurrent: true|Page: Page(/blog)
|
||||
Blog|IsMenuCurrent: false|Page: Page(/blog)
|
||||
Home|HasMenuCurrent: false|Page: /
|
||||
Blog|HasMenuCurrent: true|Page: /blog
|
||||
Blog|IsMenuCurrent: false|Page: /blog
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/blog/post3/index.html", `
|
||||
Home|HasMenuCurrent: false|Page: Page(/)
|
||||
Blog|HasMenuCurrent: true|Page: Page(/blog)
|
||||
Home|HasMenuCurrent: false|Page: /
|
||||
Blog|HasMenuCurrent: true|Page: /blog
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/contact/index.html", `
|
||||
Contact Us|HasMenuCurrent: false|Page: Page(/contact)
|
||||
Contact Us|IsMenuCurrent: true|Page: Page(/contact)
|
||||
Blog|HasMenuCurrent: false|Page: Page(/blog)
|
||||
Blog|IsMenuCurrent: false|Page: Page(/blog)
|
||||
Contact Us|HasMenuCurrent: false|Page: /contact
|
||||
Contact Us|IsMenuCurrent: true|Page: /contact
|
||||
Blog|HasMenuCurrent: false|Page: /blog
|
||||
Blog|IsMenuCurrent: false|Page: /blog
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
+18
-1
@@ -16,7 +16,9 @@ package hugolib
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -358,7 +360,22 @@ func (p *pageState) Site() page.Site {
|
||||
}
|
||||
|
||||
func (p *pageState) String() string {
|
||||
return fmt.Sprintf("Page(%s)", p.Path())
|
||||
var sb strings.Builder
|
||||
if p.File() != nil {
|
||||
// The forward slashes even on Windows is motivated by
|
||||
// getting stable tests.
|
||||
// This information is meant for getting positional information in logs,
|
||||
// so the direction of the slashes should not matter.
|
||||
sb.WriteString(filepath.ToSlash(p.File().Filename()))
|
||||
if p.File().IsContentAdapter() {
|
||||
// Also include the path.
|
||||
sb.WriteString(":")
|
||||
sb.WriteString(p.Path())
|
||||
}
|
||||
} else {
|
||||
sb.WriteString(p.Path())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// IsTranslated returns whether this content file is translated to
|
||||
|
||||
@@ -34,6 +34,15 @@ import (
|
||||
var pageIDCounter atomic.Uint64
|
||||
|
||||
func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
|
||||
p, pth, err := h.doNewPage(m)
|
||||
if err != nil {
|
||||
// Make sure that any partially created page part is marked as stale.
|
||||
m.MarkStale()
|
||||
}
|
||||
return p, pth, err
|
||||
}
|
||||
|
||||
func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
|
||||
m.Staler = &resources.AtomicStaler{}
|
||||
if m.pageMetaParams == nil {
|
||||
m.pageMetaParams = &pageMetaParams{
|
||||
@@ -231,10 +240,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
|
||||
}
|
||||
return ps, nil
|
||||
}()
|
||||
// Make sure to evict any cached and now stale data.
|
||||
if err != nil {
|
||||
m.MarkStale()
|
||||
}
|
||||
|
||||
if ps == nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -69,6 +70,7 @@ Content: {{ .Content }}|
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"Fragments: [p1-h1 p2-h1 p2-h2 p2-h3 p2-withmarkdown p3-h1 p3-h2 p3-withmarkdown]|",
|
||||
"HasShortcode Level 1: true|",
|
||||
@@ -115,6 +117,7 @@ JSON: {{ .Content }}
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html", "Myshort HTML")
|
||||
b.AssertFileContent("public/p1/index.json", "Myshort JSON")
|
||||
}
|
||||
@@ -147,9 +150,11 @@ Myshort Original.
|
||||
{{ .Content }}
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html", "Myshort Original.")
|
||||
|
||||
b.EditFileReplaceAll("layouts/shortcodes/myshort.html", "Original", "Edited").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html", "Myshort Edited.")
|
||||
}
|
||||
|
||||
@@ -192,12 +197,14 @@ Myshort Original.
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html", "Original")
|
||||
|
||||
b.EditFileReplaceFunc("content/p2.md", func(s string) string {
|
||||
return strings.Replace(s, "Original", "Edited", 1)
|
||||
})
|
||||
b.Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html", "Edited")
|
||||
}
|
||||
|
||||
@@ -233,8 +240,10 @@ Myshort Original.
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/mysection/index.html", "p1-h1")
|
||||
b.EditFileReplaceAll("content/mysection/_index.md", "p1-h1", "p1-h1 Edited").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/mysection/index.html", "p1-h1 Edited")
|
||||
}
|
||||
|
||||
@@ -314,6 +323,8 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
|
||||
b.AssertFileContent("public/markdown/index.html",
|
||||
// Images.
|
||||
"Image: /posts/p1/pixel1.png|\nImage: /posts/p1/pixel2.png|\n|\nImage: /markdown/pixel3.png|</p>\n|",
|
||||
@@ -333,3 +344,139 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
|
||||
|
||||
b.AssertFileContent("public/html/index.html", "! hugo_ctx")
|
||||
}
|
||||
|
||||
// Issue 12854.
|
||||
func TestRenderShortcodesWithHTML(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableLiveReload = true
|
||||
disableKinds = ["home", "taxonomy", "term"]
|
||||
markup.goldmark.renderer.unsafe = true
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
{{% include "p2" %}}
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: "p2"
|
||||
---
|
||||
Hello <b>world</b>. Some **bold** text. Some Unicode: 神真美好.
|
||||
-- layouts/shortcodes/include.html --
|
||||
{{ with site.GetPage (.Get 0) }}
|
||||
<div>{{ .RenderShortcodes }}</div>
|
||||
{{ end }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := TestRunning(t, files, TestOptWarn())
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertLogContains(filepath.ToSlash("WARN .RenderShortcodes detected inside HTML block in \"/content/p1.md\"; this may not be what you intended, see https://gohugo.io/methods/page/rendershortcodes/#limitations\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-rendershortcodes-in-html']"))
|
||||
b.AssertFileContent("public/p1/index.html", "<div>Hello <b>world</b>. Some **bold** text. Some Unicode: 神真美好.\n</div>")
|
||||
b.EditFileReplaceAll("content/p2.md", "Hello", "Hello Edited").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContent("public/p1/index.html", "<div>Hello Edited <b>world</b>. Some **bold** text. Some Unicode: 神真美好.\n</div>")
|
||||
}
|
||||
|
||||
func TestRenderShortcodesIncludeMarkdownFileWithoutTrailingNewline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableLiveReload = true
|
||||
disableKinds = ["home", "taxonomy", "term"]
|
||||
markup.goldmark.renderer.unsafe = true
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
Content p1 id-1000.{{% include "p2" %}}{{% include "p3" %}}
|
||||
|
||||
§§§ go
|
||||
code_p1
|
||||
§§§
|
||||
§§§ go
|
||||
code_p1_2
|
||||
§§§
|
||||
|
||||
§§§ go
|
||||
code_p1_3
|
||||
§§§
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: "p2"
|
||||
---
|
||||
§§§ bash
|
||||
code_p2
|
||||
§§§
|
||||
Foo.
|
||||
-- content/p3.md --
|
||||
---
|
||||
title: "p3"
|
||||
---
|
||||
§§§ php
|
||||
code_p3
|
||||
§§§
|
||||
-- layouts/shortcodes/include.html --
|
||||
{{ with site.GetPage (.Get 0) -}}
|
||||
{{ .RenderShortcodes -}}
|
||||
{{ end -}}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
-- layouts/_default/_markup/render-codeblock.html --
|
||||
<code>{{ .Inner | safeHTML }}</code>
|
||||
`
|
||||
|
||||
b := TestRunning(t, files, TestOptWarn())
|
||||
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-1000.</p>\n<code>code_p2</code><p>Foo.\n</p>\n<code>code_p3</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
|
||||
b.EditFileReplaceAll("content/p1.md", "id-1000.", "id-100.").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>code_p2</code><p>Foo.\n</p>\n<code>code_p3</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
|
||||
b.EditFileReplaceAll("content/p2.md", "code_p2", "codep2").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>codep2</code><p>Foo.\n</p>\n<code>code_p3</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
|
||||
b.EditFileReplaceAll("content/p3.md", "code_p3", "code_p3_edited").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContentEquals("public/p1/index.html", "<p>Content p1 id-100.</p>\n<code>codep2</code><p>Foo.\n</p>\n<code>code_p3_edited</code><p></p>\n<code>code_p1</code><code>code_p1_2</code><code>code_p1_3</code>")
|
||||
}
|
||||
|
||||
// Issue 13004.
|
||||
func TestRenderShortcodesIncludeShortRefEdit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableLiveReload = true
|
||||
disableKinds = ["home", "taxonomy", "term", "section", "rss", "sitemap", "robotsTXT", "404"]
|
||||
-- content/first/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
## p1-h1
|
||||
{{% include "p2" %}}
|
||||
-- content/second/p2.md --
|
||||
---
|
||||
title: "p2"
|
||||
---
|
||||
### p2-h1
|
||||
|
||||
This is some **markup**.
|
||||
-- layouts/shortcodes/include.html --
|
||||
{{ $p := site.GetPage (.Get 0) -}}
|
||||
{{ $p.RenderShortcodes -}}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContentEquals("public/first/p1/index.html", "<h2 id=\"p1-h1\">p1-h1</h2>\n<p></p>\n<h3 id=\"p2-h1\">p2-h1</h3>\n<p>This is some <strong>markup</strong>.\n</p>\n")
|
||||
b.EditFileReplaceAll("content/second/p2.md", "p2-h1", "p2-h1-edited").Build()
|
||||
b.AssertNoRenderShortcodesArtifacts()
|
||||
b.AssertFileContentEquals("public/first/p1/index.html", "<h2 id=\"p1-h1\">p1-h1</h2>\n<p></p>\n<h3 id=\"p2-h1-edited\">p2-h1-edited</h3>\n<p>This is some <strong>markup</strong>.\n</p>\n")
|
||||
}
|
||||
|
||||
@@ -125,3 +125,7 @@ func newPageForRenderHook(p *pageState) page.Page {
|
||||
func (p *pageForRenderHooks) Unwrapv() any {
|
||||
return p.p
|
||||
}
|
||||
|
||||
func (p *pageForRenderHooks) String() string {
|
||||
return p.p.String()
|
||||
}
|
||||
|
||||
+1
-10
@@ -344,7 +344,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
|
||||
skipRebuildForFilenames: make(map[string]bool),
|
||||
init: &hugoSitesInit{
|
||||
data: lazy.New(),
|
||||
layouts: lazy.New(),
|
||||
gitInfo: lazy.New(),
|
||||
},
|
||||
}
|
||||
@@ -400,15 +399,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
|
||||
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 {
|
||||
@@ -1361,6 +1351,7 @@ func (s *Site) getLanguagePermalinkLang(alwaysInSubDir bool) string {
|
||||
func (s *Site) resetBuildState(sourceChanged bool) {
|
||||
s.relatedDocsHandler = s.relatedDocsHandler.Clone()
|
||||
s.init.Reset()
|
||||
s.pageMap.Reset()
|
||||
}
|
||||
|
||||
func (s *Site) errorCollator(results <-chan error, errs chan<- error) {
|
||||
|
||||
+7
-2
@@ -1,7 +1,12 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.136.1
|
||||
HUGORELEASER_COMMITISH=64d1865c1e21c66feb96ffedda44a9eba2365af9
|
||||
HUGORELEASER_TAG=v0.137.1
|
||||
HUGORELEASER_COMMITISH=17e15b2148cee6da923acd7adf2ec31ea6b3415c
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,24 @@ archive_alias_replacements = { "linux-amd64.tar.gz" = "Linux-64bit.tar.gz" }
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/extended-withdeploy"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended,withdeploy"]
|
||||
env = ["CGO_ENABLED=1"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[builds.os.build_settings]
|
||||
env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"]
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container2/linux/extended"
|
||||
|
||||
@@ -167,12 +185,35 @@ archive_alias_replacements = { "linux-amd64.tar.gz" = "Linux-64bit.tar.gz" }
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/extended-withdeploy"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended,withdeploy"]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=x86_64-w64-mingw32-gcc",
|
||||
"CXX=x86_64-w64-mingw32-g++",
|
||||
]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/regular/**"]
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/extended-withdeploy/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
# Only extended builds in container2.
|
||||
paths = ["builds/container2/**"]
|
||||
@@ -190,6 +231,13 @@ archive_alias_replacements = { "linux-amd64.tar.gz" = "Linux-64bit.tar.gz" }
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/extended-withdeploy/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/regular/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ type Init struct {
|
||||
prev *Init
|
||||
children []*Init
|
||||
|
||||
init onceMore
|
||||
init OnceMore
|
||||
out any
|
||||
err error
|
||||
f func(context.Context) (any, error)
|
||||
|
||||
+6
-6
@@ -18,19 +18,19 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// onceMore is similar to sync.Once.
|
||||
// OnceMore is similar to sync.Once.
|
||||
//
|
||||
// Additional features are:
|
||||
// * it can be reset, so the action can be repeated if needed
|
||||
// * it has methods to check if it's done or in progress
|
||||
|
||||
type onceMore struct {
|
||||
type OnceMore struct {
|
||||
mu sync.Mutex
|
||||
lock uint32
|
||||
done uint32
|
||||
}
|
||||
|
||||
func (t *onceMore) Do(f func()) {
|
||||
func (t *OnceMore) Do(f func()) {
|
||||
if atomic.LoadUint32(&t.done) == 1 {
|
||||
return
|
||||
}
|
||||
@@ -53,15 +53,15 @@ func (t *onceMore) Do(f func()) {
|
||||
f()
|
||||
}
|
||||
|
||||
func (t *onceMore) InProgress() bool {
|
||||
func (t *OnceMore) InProgress() bool {
|
||||
return atomic.LoadUint32(&t.lock) == 1
|
||||
}
|
||||
|
||||
func (t *onceMore) Done() bool {
|
||||
func (t *OnceMore) Done() bool {
|
||||
return atomic.LoadUint32(&t.done) == 1
|
||||
}
|
||||
|
||||
func (t *onceMore) ResetWithLock() *sync.Mutex {
|
||||
func (t *OnceMore) ResetWithLock() *sync.Mutex {
|
||||
t.mu.Lock()
|
||||
defer atomic.StoreUint32(&t.done, 0)
|
||||
return &t.mu
|
||||
|
||||
+1
-1
@@ -334,7 +334,7 @@ func buildFlags() []string {
|
||||
func buildTags() string {
|
||||
// To build the extended Hugo SCSS/SASS enabled version, build with
|
||||
// HUGO_BUILD_TAGS=extended mage install etc.
|
||||
// To build without `hugo deploy` for smaller binary, use HUGO_BUILD_TAGS=nodeploy
|
||||
// To build with `hugo deploy`, use HUGO_BUILD_TAGS=withdeploy
|
||||
if envtags := os.Getenv("HUGO_BUILD_TAGS"); envtags != "" {
|
||||
return envtags
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
//go:build !withdeploy
|
||||
// +build !withdeploy
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rogpeppe/go-internal/testscript"
|
||||
)
|
||||
|
||||
func TestWithdeploy(t *testing.T) {
|
||||
p := commonTestScriptsParam
|
||||
p.Dir = "testscripts/withdeploy-off"
|
||||
testscript.Run(t, p)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
//go:build withdeploy
|
||||
// +build withdeploy
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rogpeppe/go-internal/testscript"
|
||||
)
|
||||
|
||||
func TestWithdeploy(t *testing.T) {
|
||||
p := commonTestScriptsParam
|
||||
p.Dir = "testscripts/withdeploy"
|
||||
testscript.Run(t, p)
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
|
||||
renderer.WithNodeRenderers(util.Prioritized(emoji.NewHTMLRenderer(), 200)))
|
||||
var (
|
||||
extensions = []goldmark.Extender{
|
||||
hugocontext.New(),
|
||||
hugocontext.New(pcfg.Logger),
|
||||
newLinks(cfg),
|
||||
newTocExtension(tocRendererOptions),
|
||||
blockquotes.New(),
|
||||
|
||||
@@ -575,7 +575,7 @@ sc3_begin|{{ .Inner }}|sc3_end
|
||||
// Issue #7332
|
||||
"<span>:x:\n</span>",
|
||||
// Issue #11587
|
||||
"<p>✔️</p>",
|
||||
"<p>✔️\n</p>",
|
||||
// Should not be converted to emoji
|
||||
"sc1_begin|:smiley:|sc1_end",
|
||||
// Should be converted to emoji
|
||||
@@ -802,3 +802,52 @@ H~2~0
|
||||
"<p>1<sup>st</sup></p>",
|
||||
)
|
||||
}
|
||||
|
||||
// Issue 12997.
|
||||
func TestGoldmarkRawHTMLWarningBlocks(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
markup.goldmark.renderer.unsafe = false
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
<div>Some raw HTML</div>
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptWarn())
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "<!-- raw HTML omitted -->")
|
||||
b.AssertLogContains("WARN Raw HTML omitted from \"/content/p1.md\"; see https://gohugo.io/getting-started/configuration-markup/#rendererunsafe\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-goldmark-raw-html']")
|
||||
|
||||
b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn())
|
||||
b.AssertFileContent("public/p1/index.html", "! <!-- raw HTML omitted -->")
|
||||
b.AssertLogContains("! WARN")
|
||||
}
|
||||
|
||||
func TestGoldmarkRawHTMLWarningInline(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
markup.goldmark.renderer.unsafe = false
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
<em>raw HTML</em>
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptWarn())
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "<!-- raw HTML omitted -->")
|
||||
b.AssertLogContains("WARN Raw HTML omitted from \"/content/p1.md\"; see https://gohugo.io/getting-started/configuration-markup/#rendererunsafe\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-goldmark-raw-html']")
|
||||
|
||||
b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn())
|
||||
b.AssertFileContent("public/p1/index.html", "! <!-- raw HTML omitted -->")
|
||||
b.AssertLogContains("! WARN")
|
||||
}
|
||||
|
||||
@@ -16,20 +16,24 @@ package hugocontext
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/gohugoio/hugo/bufferpool"
|
||||
"github.com/gohugoio/hugo/common/constants"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
func New() goldmark.Extender {
|
||||
return &hugoContextExtension{}
|
||||
func New(logger loggers.Logger) goldmark.Extender {
|
||||
return &hugoContextExtension{logger: logger}
|
||||
}
|
||||
|
||||
// Wrap wraps the given byte slice in a Hugo context that used to determine the correct Page
|
||||
@@ -37,14 +41,19 @@ func New() goldmark.Extender {
|
||||
func Wrap(b []byte, pid uint64) string {
|
||||
buf := bufferpool.GetBuffer()
|
||||
defer bufferpool.PutBuffer(buf)
|
||||
buf.Write(prefix)
|
||||
buf.Write(hugoCtxPrefix)
|
||||
buf.WriteString(" pid=")
|
||||
buf.WriteString(strconv.FormatUint(pid, 10))
|
||||
buf.Write(endDelim)
|
||||
buf.Write(hugoCtxEndDelim)
|
||||
buf.WriteByte('\n')
|
||||
buf.Write(b)
|
||||
buf.Write(prefix)
|
||||
buf.Write(closingDelimAndNewline)
|
||||
// To make sure that we're able to parse it, make sure it ends with a newline.
|
||||
if len(b) > 0 && b[len(b)-1] != '\n' {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.Write(hugoCtxPrefix)
|
||||
buf.Write(hugoCtxClosingDelim)
|
||||
buf.WriteByte('\n')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -89,45 +98,130 @@ func (h *HugoContext) Kind() ast.NodeKind {
|
||||
}
|
||||
|
||||
var (
|
||||
prefix = []byte("{{__hugo_ctx")
|
||||
endDelim = []byte("}}")
|
||||
closingDelimAndNewline = []byte("/}}\n")
|
||||
hugoCtxPrefix = []byte("{{__hugo_ctx")
|
||||
hugoCtxEndDelim = []byte("}}")
|
||||
hugoCtxClosingDelim = []byte("/}}")
|
||||
hugoCtxRe = regexp.MustCompile(`{{__hugo_ctx( pid=\d+)?/?}}\n?`)
|
||||
)
|
||||
|
||||
var _ parser.InlineParser = (*hugoContextParser)(nil)
|
||||
|
||||
type hugoContextParser struct{}
|
||||
|
||||
func (s *hugoContextParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
||||
line, _ := block.PeekLine()
|
||||
if !bytes.HasPrefix(line, prefix) {
|
||||
func (a *hugoContextParser) Trigger() []byte {
|
||||
return []byte{'{'}
|
||||
}
|
||||
|
||||
func (s *hugoContextParser) Parse(parent ast.Node, reader text.Reader, pc parser.Context) ast.Node {
|
||||
line, _ := reader.PeekLine()
|
||||
if !bytes.HasPrefix(line, hugoCtxPrefix) {
|
||||
return nil
|
||||
}
|
||||
end := bytes.Index(line, endDelim)
|
||||
end := bytes.Index(line, hugoCtxEndDelim)
|
||||
if end == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
block.Advance(end + len(endDelim) + 1) // +1 for the newline
|
||||
reader.Advance(end + len(hugoCtxEndDelim) + 1) // +1 for the newline
|
||||
|
||||
if line[end-1] == '/' {
|
||||
return &HugoContext{Closing: true}
|
||||
}
|
||||
|
||||
attrBytes := line[len(prefix)+1 : end]
|
||||
attrBytes := line[len(hugoCtxPrefix)+1 : end]
|
||||
h := &HugoContext{}
|
||||
h.parseAttrs(attrBytes)
|
||||
return h
|
||||
}
|
||||
|
||||
func (a *hugoContextParser) Trigger() []byte {
|
||||
return []byte{'{'}
|
||||
type hugoContextRenderer struct {
|
||||
logger loggers.Logger
|
||||
html.Config
|
||||
}
|
||||
|
||||
type hugoContextRenderer struct{}
|
||||
func (r *hugoContextRenderer) SetOption(name renderer.OptionName, value any) {
|
||||
r.Config.SetOption(name, value)
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(kindHugoContext, r.handleHugoContext)
|
||||
reg.Register(ast.KindRawHTML, r.renderRawHTML)
|
||||
reg.Register(ast.KindHTMLBlock, r.renderHTMLBlock)
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) stripHugoCtx(b []byte) ([]byte, bool) {
|
||||
if !bytes.Contains(b, hugoCtxPrefix) {
|
||||
return b, false
|
||||
}
|
||||
return hugoCtxRe.ReplaceAll(b, nil), true
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) logRawHTMLEmittedWarn(w util.BufWriter) {
|
||||
r.logger.Warnidf(constants.WarnGoldmarkRawHTML, "Raw HTML omitted from %q; see https://gohugo.io/getting-started/configuration-markup/#rendererunsafe", r.getPage(w))
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) getPage(w util.BufWriter) any {
|
||||
var p any
|
||||
ctx, ok := w.(*render.Context)
|
||||
if ok {
|
||||
p, _ = render.GetPageAndPageInner(ctx)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// HTML rendering based on Goldmark implementation.
|
||||
func (r *hugoContextRenderer) renderHTMLBlock(
|
||||
w util.BufWriter, source []byte, node ast.Node, entering bool,
|
||||
) (ast.WalkStatus, error) {
|
||||
n := node.(*ast.HTMLBlock)
|
||||
if entering {
|
||||
if r.Unsafe {
|
||||
l := n.Lines().Len()
|
||||
for i := 0; i < l; i++ {
|
||||
line := n.Lines().At(i)
|
||||
linev := line.Value(source)
|
||||
var stripped bool
|
||||
linev, stripped = r.stripHugoCtx(linev)
|
||||
if stripped {
|
||||
r.logger.Warnidf(constants.WarnRenderShortcodesInHTML, ".RenderShortcodes detected inside HTML block in %q; this may not be what you intended, see https://gohugo.io/methods/page/rendershortcodes/#limitations", r.getPage(w))
|
||||
}
|
||||
r.Writer.SecureWrite(w, linev)
|
||||
}
|
||||
} else {
|
||||
r.logRawHTMLEmittedWarn(w)
|
||||
_, _ = w.WriteString("<!-- raw HTML omitted -->\n")
|
||||
}
|
||||
} else {
|
||||
if n.HasClosure() {
|
||||
if r.Unsafe {
|
||||
closure := n.ClosureLine
|
||||
r.Writer.SecureWrite(w, closure.Value(source))
|
||||
} else {
|
||||
_, _ = w.WriteString("<!-- raw HTML omitted -->\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) renderRawHTML(
|
||||
w util.BufWriter, source []byte, node ast.Node, entering bool,
|
||||
) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkSkipChildren, nil
|
||||
}
|
||||
if r.Unsafe {
|
||||
n := node.(*ast.RawHTML)
|
||||
l := n.Segments.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
segment := n.Segments.At(i)
|
||||
_, _ = w.Write(segment.Value(source))
|
||||
}
|
||||
return ast.WalkSkipChildren, nil
|
||||
}
|
||||
r.logRawHTMLEmittedWarn(w)
|
||||
_, _ = w.WriteString("<!-- raw HTML omitted -->")
|
||||
return ast.WalkSkipChildren, nil
|
||||
}
|
||||
|
||||
func (r *hugoContextRenderer) handleHugoContext(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
@@ -148,7 +242,9 @@ func (r *hugoContextRenderer) handleHugoContext(w util.BufWriter, source []byte,
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
type hugoContextExtension struct{}
|
||||
type hugoContextExtension struct {
|
||||
logger loggers.Logger
|
||||
}
|
||||
|
||||
func (a *hugoContextExtension) Extend(m goldmark.Markdown) {
|
||||
m.Parser().AddOptions(
|
||||
@@ -159,7 +255,12 @@ func (a *hugoContextExtension) Extend(m goldmark.Markdown) {
|
||||
|
||||
m.Renderer().AddOptions(
|
||||
renderer.WithNodeRenderers(
|
||||
util.Prioritized(&hugoContextRenderer{}, 50),
|
||||
util.Prioritized(&hugoContextRenderer{
|
||||
logger: a.logger,
|
||||
Config: html.Config{
|
||||
Writer: html.DefaultWriter,
|
||||
},
|
||||
}, 50),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestWrap(t *testing.T) {
|
||||
|
||||
b := []byte("test")
|
||||
|
||||
c.Assert(Wrap(b, 42), qt.Equals, "{{__hugo_ctx pid=42}}\ntest{{__hugo_ctx/}}\n")
|
||||
c.Assert(Wrap(b, 42), qt.Equals, "{{__hugo_ctx pid=42}}\ntest\n{{__hugo_ctx/}}\n")
|
||||
}
|
||||
|
||||
func BenchmarkWrap(b *testing.B) {
|
||||
|
||||
@@ -17,6 +17,7 @@ package page
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
@@ -180,6 +181,7 @@ type Page interface {
|
||||
ContentProvider
|
||||
TableOfContentsProvider
|
||||
PageWithoutContent
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
type PageFragment interface {
|
||||
|
||||
@@ -161,13 +161,13 @@ includecontent: {{ hugo.Context.MarkupScope }}|{{ $p.Markup.Render.Content }}|
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "Render heading: title: P1 scope: |", "Foo scope: |")
|
||||
b.AssertFileContentExact("public/p1/index.html", "Render heading: title: P1 scope: |", "Foo scope: |")
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
b.AssertFileContentExact("public/index.html",
|
||||
"Begin:\nincludecontent: home|Render heading: title: P3 scope: home|Foo scope: home|\n|\n:End",
|
||||
"Render heading: title: P1 scope: home|",
|
||||
"Foo scope: home|",
|
||||
"Begin:\nincluderendershortcodes: home|</p>\nRender heading: title: P2 scope: home|<p>|:End",
|
||||
"Begin:\nincludecontent: home|Render heading: title: P3 scope: home|Foo scope: home|\n|\n:End",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/internal"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/internal/sass"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
@@ -85,7 +85,7 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error {
|
||||
c: t.c,
|
||||
dependencyManager: ctx.DependencyManager,
|
||||
|
||||
varsStylesheet: godartsass.Import{Content: sass.CreateVarsStyleSheet(opts.Vars)},
|
||||
varsStylesheet: godartsass.Import{Content: sass.CreateVarsStyleSheet(sass.TranspilerDart, opts.Vars)},
|
||||
},
|
||||
OutputStyle: godartsass.ParseOutputStyle(opts.OutputStyle),
|
||||
EnableSourceMap: opts.EnableSourceMap,
|
||||
|
||||
+18
-3
@@ -24,9 +24,14 @@ import (
|
||||
|
||||
const (
|
||||
HugoVarsNamespace = "hugo:vars"
|
||||
// Transpiler implementation can be controlled from the client by
|
||||
// setting the 'transpiler' option.
|
||||
// Default is currently 'libsass', but that may change.
|
||||
TranspilerDart = "dartsass"
|
||||
TranspilerLibSass = "libsass"
|
||||
)
|
||||
|
||||
func CreateVarsStyleSheet(vars map[string]any) string {
|
||||
func CreateVarsStyleSheet(transpiler string, vars map[string]any) string {
|
||||
if vars == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -49,12 +54,22 @@ func CreateVarsStyleSheet(vars map[string]any) string {
|
||||
varsSlice = append(varsSlice, fmt.Sprintf("%s%s: %v;", prefix, k, v))
|
||||
} else {
|
||||
// unquote will preserve quotes around URLs etc. if needed.
|
||||
varsSlice = append(varsSlice, fmt.Sprintf("%s%s: unquote(%q);", prefix, k, v))
|
||||
if transpiler == TranspilerDart {
|
||||
varsSlice = append(varsSlice, fmt.Sprintf("%s%s: string.unquote(%q);", prefix, k, v))
|
||||
} else {
|
||||
varsSlice = append(varsSlice, fmt.Sprintf("%s%s: unquote(%q);", prefix, k, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(varsSlice)
|
||||
varsStylesheet = strings.Join(varsSlice, "\n")
|
||||
|
||||
if transpiler == TranspilerDart {
|
||||
varsStylesheet = `@use "sass:string";` + "\n" + strings.Join(varsSlice, "\n")
|
||||
} else {
|
||||
varsStylesheet = strings.Join(varsSlice, "\n")
|
||||
}
|
||||
|
||||
return varsStylesheet
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/internal/sass"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
|
||||
)
|
||||
|
||||
// Used in tests. This feature requires Hugo to be built with the extended tag.
|
||||
@@ -64,7 +64,7 @@ func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||
}
|
||||
}
|
||||
|
||||
varsStylesheet := sass.CreateVarsStyleSheet(options.from.Vars)
|
||||
varsStylesheet := sass.CreateVarsStyleSheet(sass.TranspilerLibSass, options.from.Vars)
|
||||
|
||||
// To allow for overrides of SCSS files anywhere in the project/theme hierarchy, we need
|
||||
// to help libsass revolve the filename by looking in the composite filesystem first.
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
# Test the gen commands.
|
||||
# Note that adding new commands will require updating the NUM_COMMANDS value.
|
||||
env NUM_COMMANDS=44
|
||||
|
||||
hugo gen -h
|
||||
stdout 'Generate documentation for your project using Hugo''s documentation engine, including syntax highlighting for various programming languages\.'
|
||||
|
||||
hugo gen doc --dir clidocs
|
||||
checkfilecount $NUM_COMMANDS clidocs
|
||||
|
||||
hugo gen man -h
|
||||
stdout 'up-to-date man pages'
|
||||
hugo gen man --dir manpages
|
||||
checkfilecount $NUM_COMMANDS manpages
|
||||
|
||||
hugo gen chromastyles -h
|
||||
stdout 'Generate CSS stylesheet for the Chroma code highlighter'
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Test the hugo server command when adding an error to a config file
|
||||
# and then fixing it.
|
||||
|
||||
hugo server &
|
||||
|
||||
waitServer
|
||||
|
||||
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
|
||||
|
||||
replace $WORK/hugo.toml 'title =' 'titlefoo'
|
||||
httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
|
||||
|
||||
replace $WORK/hugo.toml 'titlefoo' 'title ='
|
||||
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
|
||||
|
||||
stopServer
|
||||
|
||||
-- hugo.toml --
|
||||
title = "Hugo Server Test"
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term", "sitemap"]
|
||||
-- layouts/index.html --
|
||||
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
|
||||
-- layouts/_default/single.html --
|
||||
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: Hugo Home
|
||||
---
|
||||
-- content/p1/index.md --
|
||||
---
|
||||
title: P1
|
||||
---
|
||||
-- content/p2/index.md --
|
||||
---
|
||||
title: P2
|
||||
---
|
||||
-- static/staticfiles/static.txt --
|
||||
static
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Test the hugo server command when adding a front matter error to a content file
|
||||
# and then fixing it.
|
||||
|
||||
hugo server &
|
||||
|
||||
waitServer
|
||||
|
||||
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
|
||||
|
||||
replace $WORK/content/p1/index.md 'title:' 'titlecolon'
|
||||
httpget ${HUGOTEST_BASEURL_0}p1/ 'failed'
|
||||
|
||||
replace $WORK/content/p1/index.md 'titlecolon' 'title:'
|
||||
httpget ${HUGOTEST_BASEURL_0}p1/ 'Title: P1'
|
||||
|
||||
stopServer
|
||||
|
||||
-- hugo.toml --
|
||||
title = "Hugo Server Test"
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term", "sitemap"]
|
||||
-- layouts/index.html --
|
||||
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
|
||||
-- layouts/_default/single.html --
|
||||
Title: {{ .Title }}|BaseURL: {{ site.BaseURL }}|
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: Hugo Home
|
||||
---
|
||||
-- content/p1/index.md --
|
||||
---
|
||||
title: P1
|
||||
---
|
||||
-- content/p2/index.md --
|
||||
---
|
||||
title: P2
|
||||
---
|
||||
-- static/staticfiles/static.txt --
|
||||
static
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
! hugo deploy --force
|
||||
# Issue 13012
|
||||
stderr 'deploy not supported in this version of Hugo'
|
||||
+5
-12
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/babel"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/cssjs"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
|
||||
"github.com/gohugoio/hugo/tpl/internal"
|
||||
"github.com/gohugoio/hugo/tpl/internal/resourcehelpers"
|
||||
@@ -84,21 +85,13 @@ func (ns *Namespace) Sass(args ...any) (resource.Resource, error) {
|
||||
return nil, errors.New("must not provide more arguments than resource object and options")
|
||||
}
|
||||
|
||||
const (
|
||||
// Transpiler implementation can be controlled from the client by
|
||||
// setting the 'transpiler' option.
|
||||
// Default is currently 'libsass', but that may change.
|
||||
transpilerDart = "dartsass"
|
||||
transpilerLibSass = "libsass"
|
||||
)
|
||||
|
||||
var (
|
||||
r resources.ResourceTransformer
|
||||
m map[string]any
|
||||
targetPath string
|
||||
err error
|
||||
ok bool
|
||||
transpiler = transpilerLibSass
|
||||
transpiler = sass.TranspilerLibSass
|
||||
)
|
||||
|
||||
r, targetPath, ok = resourcehelpers.ResolveIfFirstArgIsString(args)
|
||||
@@ -113,15 +106,15 @@ func (ns *Namespace) Sass(args ...any) (resource.Resource, error) {
|
||||
if m != nil {
|
||||
if t, _, found := maps.LookupEqualFold(m, "transpiler"); found {
|
||||
switch t {
|
||||
case transpilerDart, transpilerLibSass:
|
||||
case sass.TranspilerDart, sass.TranspilerLibSass:
|
||||
transpiler = cast.ToString(t)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported transpiler %q; valid values are %q or %q", t, transpilerLibSass, transpilerDart)
|
||||
return nil, fmt.Errorf("unsupported transpiler %q; valid values are %q or %q", t, sass.TranspilerLibSass, sass.TranspilerDart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if transpiler == transpilerLibSass {
|
||||
if transpiler == sass.TranspilerLibSass {
|
||||
var options scss.Options
|
||||
if targetPath != "" {
|
||||
options.TargetPath = paths.ToSlashTrimLeading(targetPath)
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
|
||||
)
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
@@ -238,3 +240,45 @@ match /files/C*: 2|
|
||||
b.AssertFileContent("public/files/b.txt", "I am b.txt")
|
||||
b.AssertFileContent("public/files/C.txt", "I am C.txt")
|
||||
}
|
||||
|
||||
// Issue #12961
|
||||
func TestDartSassVars(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if !scss.Supports() || !dartsass.Supports() {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
|
||||
-- layouts/index.html --
|
||||
{{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" "vars" (dict "color" "red") }}
|
||||
{{ with resources.Get "dartsass.scss" | css.Sass $opts }}
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
|
||||
{{ $opts := dict "transpiler" "libsass" "outputStyle" "compressed" "vars" (dict "color" "blue") }}
|
||||
{{ with resources.Get "libsass.scss" | css.Sass $opts }}
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
-- assets/dartsass.scss --
|
||||
@use "hugo:vars" as v;
|
||||
.dartsass {
|
||||
color: v.$color;
|
||||
}
|
||||
-- assets/libsass.scss --
|
||||
@import "hugo:vars";
|
||||
.libsass {
|
||||
color: $color;
|
||||
}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptWarn())
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
".dartsass{color:red}",
|
||||
".libsass{color:blue}",
|
||||
)
|
||||
b.AssertLogContains("! WARN Dart Sass: hugo:vars")
|
||||
}
|
||||
|
||||
@@ -450,6 +450,17 @@ func (ns *Namespace) Trim(s, cutset any) (string, error) {
|
||||
return strings.Trim(ss, sc), nil
|
||||
}
|
||||
|
||||
// TrimSpace returns the given string, removing leading and trailing whitespace
|
||||
// as defined by Unicode.
|
||||
func (ns *Namespace) TrimSpace(s any) (string, error) {
|
||||
ss, err := cast.ToStringE(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(ss), nil
|
||||
}
|
||||
|
||||
// TrimLeft returns a slice of the string s with all leading characters
|
||||
// contained in cutset removed.
|
||||
func (ns *Namespace) TrimLeft(cutset, s any) (string, error) {
|
||||
|
||||
@@ -854,3 +854,30 @@ func TestDiff(t *testing.T) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrimSpace(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
s any
|
||||
expect any
|
||||
}{
|
||||
{"\n\r test \n\r", "test"},
|
||||
{template.HTML("\n\r test \n\r"), "test"},
|
||||
{[]byte("\n\r test \n\r"), "test"},
|
||||
// errors
|
||||
{tstNoStringer{}, false},
|
||||
} {
|
||||
|
||||
result, err := ns.TrimSpace(test.s)
|
||||
|
||||
if b, ok := test.expect.(bool); ok && !b {
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
continue
|
||||
}
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(result, qt.Equals, test.expect)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ type TemplateManager interface {
|
||||
TemplateHandler
|
||||
TemplateFuncGetter
|
||||
AddTemplate(name, tpl string) error
|
||||
MarkReady() error
|
||||
}
|
||||
|
||||
// TemplateVariants describes the possible variants of a template.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{{- $noop := .Inner -}}
|
||||
+4
-17
@@ -168,6 +168,10 @@ func newTemplateHandlers(d *deps.Deps) (*tpl.TemplateHandlers, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := h.main.createPrototypes(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e := &templateExec{
|
||||
d: d,
|
||||
executor: exec,
|
||||
@@ -312,28 +316,11 @@ func (t *templateExec) GetFunc(name string) (reflect.Value, bool) {
|
||||
return v, found
|
||||
}
|
||||
|
||||
func (t *templateExec) MarkReady() error {
|
||||
var err error
|
||||
t.readyInit.Do(func() {
|
||||
// We only need the clones if base templates are in use.
|
||||
if len(t.needsBaseof) > 0 {
|
||||
err = t.main.createPrototypes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type templateHandler struct {
|
||||
main *templateNamespace
|
||||
needsBaseof map[string]templateInfo
|
||||
baseof map[string]templateInfo
|
||||
|
||||
readyInit sync.Once
|
||||
|
||||
// This is the filesystem to load the templates from. All the templates are
|
||||
// stored in the root of this filesystem.
|
||||
layoutsFs afero.Fs
|
||||
|
||||
@@ -584,3 +584,19 @@ title: p5
|
||||
`<meta name="twitter:description" content="m n and **o** can't.">`,
|
||||
)
|
||||
}
|
||||
|
||||
func TestCommentShortcode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- layouts/index.html --
|
||||
{{ .Content }}
|
||||
-- content/_index.md --
|
||||
a{{< comment >}}b{{< /comment >}}c
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "<p>ac</p>")
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ func TestToMathMacros(t *testing.T) {
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- layouts/index.html --
|
||||
{{ $macros := dict
|
||||
{{ $macros := dict
|
||||
"\\addBar" "\\bar{#1}"
|
||||
"\\bold" "\\mathbf{#1}"
|
||||
}}
|
||||
@@ -261,3 +261,21 @@ disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
<mi>y</mi>
|
||||
`)
|
||||
}
|
||||
|
||||
// Issue #12977
|
||||
func TestUnmarshalWithIndentedYAML(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- layouts/index.html --
|
||||
{{ $yaml := "\n a:\n b: 1\n c:\n d: 2\n" }}
|
||||
{{ $yaml | transform.Unmarshal | encoding.Jsonify }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/index.html", true)
|
||||
b.AssertFileContent("public/index.html", `{"a":{"b":1},"c":{"d":2}}`)
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ func (ns *Namespace) Unmarshal(args ...any) (any, error) {
|
||||
return nil, fmt.Errorf("type %T not supported", data)
|
||||
}
|
||||
|
||||
if dataStr == "" {
|
||||
return nil, errors.New("no data to transform")
|
||||
if strings.TrimSpace(dataStr) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
key := hashing.MD5FromStringHexEncoded(dataStr)
|
||||
|
||||
@@ -139,6 +139,8 @@ func TestUnmarshal(t *testing.T) {
|
||||
a;b;c`, mime: media.Builtin.CSVType}, map[string]any{"DElimiter": ";", "Comment": "%"}, func(r [][]string) {
|
||||
b.Assert([][]string{{"a", "b", "c"}}, qt.DeepEquals, r)
|
||||
}},
|
||||
{``, nil, nil},
|
||||
{` `, nil, nil},
|
||||
// errors
|
||||
{"thisisnotavaliddataformat", nil, false},
|
||||
{testContentResource{key: "r1", content: `invalid&toml"`, mime: media.Builtin.TOMLType}, nil, false},
|
||||
|
||||
Reference in New Issue
Block a user