mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-25 15:58:53 +00:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ae4786ca1 | |||
| da7983ac4b | |||
| df5d76fed9 | |||
| 7362ba2204 | |||
| 0eb480aa1d | |||
| 7e9092eeeb | |||
| 0200eaf58f | |||
| 28b640a220 | |||
| db45dbbee8 | |||
| 77b976dd92 | |||
| e847a98db6 | |||
| 45c9bbc6ca | |||
| 94fbab2a88 | |||
| c3f273b2d7 | |||
| d7dcc76d27 | |||
| 9bf76fd7e8 | |||
| 15d3e48cec | |||
| b2a02c3c5a | |||
| 6821d6f159 | |||
| 93c7ad12ec | |||
| d2ae9e1363 | |||
| 3a8aad6b19 | |||
| a7b93e6564 | |||
| 65871d5cf4 | |||
| 2e4bf89ec7 | |||
| ebaa733d45 | |||
| 24b1be45c1 | |||
| 111f02db2a | |||
| 8a08f91d5b | |||
| dcf425c846 | |||
| 9a8c84d600 | |||
| a19d03b0e6 | |||
| cdf0b3b7a6 | |||
| d979831dbd | |||
| bcf7421ea5 | |||
| b6538532f4 | |||
| 90944aa261 | |||
| db7bc49690 | |||
| a2f6400d65 | |||
| 7d74cd0cc8 | |||
| d139f30234 | |||
| b1b6912412 | |||
| 0de81c6430 | |||
| 851bf3515e | |||
| d4a6c16c17 | |||
| 5d5fb22ead | |||
| 7c62d6ef16 | |||
| 641390f8f5 | |||
| 2e6191b2e0 | |||
| 22861cb4dc | |||
| 16da1ade70 | |||
| a3d42a277d | |||
| 2c20fd557a | |||
| 243736e751 | |||
| bf891c2259 | |||
| da0df0ada3 | |||
| 0885f8ec2b | |||
| 61be050a95 | |||
| 65af75fb00 | |||
| 2d75f74b86 | |||
| 2ac3d61604 | |||
| ade7ec8187 | |||
| 8fa8ce3e48 | |||
| 9dce45c25d | |||
| 239f2e2c99 | |||
| 3e1ea030a5 | |||
| 30885a6c5f | |||
| 58da8554cb | |||
| 92c1594372 | |||
| 5a7e0da84e | |||
| d7db096a9d | |||
| c1df5b1b0b | |||
| be8e2de597 | |||
| d297c8e1b5 | |||
| fbb8eb39ec | |||
| 87d9bffe74 | |||
| d9fdcbe939 | |||
| 295d733883 | |||
| d5247788ee | |||
| 036e260d8d | |||
| a50356b9a2 | |||
| 8859be1c01 | |||
| 87de22d746 | |||
| bec9b80d95 | |||
| 1c97095ac0 | |||
| b3cb6788b2 | |||
| 5542f02fbc | |||
| 0bc7ed9a17 | |||
| 36b5126050 | |||
| 2589b1295c | |||
| ef6e813ca8 | |||
| f4598a0986 | |||
| dc2a544fac | |||
| 916397320b | |||
| b3f10556f0 | |||
| 4d7af757c9 | |||
| 7f058b8bab | |||
| 575d7f8068 | |||
| 739d10e8bd | |||
| 0dbe0f1a07 | |||
| dc95245212 | |||
| d70b6c7d01 | |||
| d947db3713 | |||
| d8c94c354a | |||
| 6bbec90014 | |||
| 5bd22ba85f | |||
| 7ae62f4aa3 | |||
| f1a061e9ed | |||
| 2f11e673c5 | |||
| 387c5f60f9 |
@@ -5,7 +5,12 @@ assignees: ''
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
|
||||
<!--
|
||||
Please do not use the issue queue for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the forum:
|
||||
|
||||
https://discourse.gohugo.io
|
||||
-->
|
||||
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
|
||||
### What version of Hugo are you using (`hugo version`)?
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.21.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.19.x,1.20.x]
|
||||
go-version: [1.20.x,1.21.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@ee2113536afb7f793eed4ce60e8d3b26db912da4
|
||||
with:
|
||||
ruby-version: '2.7'
|
||||
ruby-version: '2.7'
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
run: |
|
||||
Choco-Install -PackageName mingw -ArgumentList "--version","12.2.0","--allow-downgrade"
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass-embedded Linux
|
||||
name: Install dart-sass Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
echo "$GOBIN"
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'macos-latest'
|
||||
name: Install dart-sass-embedded MacOS
|
||||
name: Install dart-sass MacOS
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Install dart-sass-embedded Windows
|
||||
name: Install dart-sass Windows
|
||||
run: |
|
||||
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
FROM golang:1.19-alpine AS build
|
||||
FROM golang:1.21-alpine AS build
|
||||
|
||||
# Optionally set HUGO_BUILD_TAGS to "extended" or "nodeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=extended .
|
||||
@@ -26,7 +26,7 @@ RUN mage hugo && mage install
|
||||
|
||||
# ---
|
||||
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.18
|
||||
|
||||
COPY --from=build /go/bin/hugo /usr/bin/hugo
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[bep]: https://github.com/spf13
|
||||
[bep]: https://github.com/bep
|
||||
[bugs]: https://github.com/gohugoio/hugo/issues?q=is%3Aopen+is%3Aissue+label%3ABug
|
||||
[contributing]: CONTRIBUTING.md
|
||||
[create a proposal]: https://github.com/gohugoio/hugo/issues/new?labels=Proposal%2C+NeedsTriage&template=feature_request.md
|
||||
|
||||
Vendored
+33
-4
@@ -51,6 +51,9 @@ type Cache struct {
|
||||
pruneAllRootDir string
|
||||
|
||||
nlocker *lockTracker
|
||||
|
||||
initOnce sync.Once
|
||||
initErr error
|
||||
}
|
||||
|
||||
type lockTracker struct {
|
||||
@@ -103,9 +106,23 @@ func (l *lockedFile) Close() error {
|
||||
return l.File.Close()
|
||||
}
|
||||
|
||||
func (c *Cache) init() error {
|
||||
c.initOnce.Do(func() {
|
||||
// Create the base dir if it does not exist.
|
||||
if err := c.Fs.MkdirAll("", 0777); err != nil && !os.IsExist(err) {
|
||||
c.initErr = err
|
||||
}
|
||||
})
|
||||
return c.initErr
|
||||
}
|
||||
|
||||
// WriteCloser returns a transactional writer into the cache.
|
||||
// It's important that it's closed when done.
|
||||
func (c *Cache) WriteCloser(id string) (ItemInfo, io.WriteCloser, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
|
||||
id = cleanID(id)
|
||||
c.nlocker.Lock(id)
|
||||
|
||||
@@ -130,6 +147,10 @@ func (c *Cache) WriteCloser(id string) (ItemInfo, io.WriteCloser, error) {
|
||||
func (c *Cache) ReadOrCreate(id string,
|
||||
read func(info ItemInfo, r io.ReadSeeker) error,
|
||||
create func(info ItemInfo, w io.WriteCloser) error) (info ItemInfo, err error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, err
|
||||
}
|
||||
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -163,6 +184,9 @@ func (c *Cache) ReadOrCreate(id string,
|
||||
// be invoked and the result cached.
|
||||
// This method is protected by a named lock using the given id as identifier.
|
||||
func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (ItemInfo, io.ReadCloser, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -197,6 +221,9 @@ func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (It
|
||||
|
||||
// GetOrCreateBytes is the same as GetOrCreate, but produces a byte slice.
|
||||
func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (ItemInfo, []byte, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -232,6 +259,9 @@ func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (Item
|
||||
|
||||
// GetBytes gets the file content with the given id from the cache, nil if none found.
|
||||
func (c *Cache) GetBytes(id string) (ItemInfo, []byte, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -250,6 +280,9 @@ func (c *Cache) GetBytes(id string) (ItemInfo, []byte, error) {
|
||||
|
||||
// Get gets the file with the given id from the cache, nil if none found.
|
||||
func (c *Cache) Get(id string) (ItemInfo, io.ReadCloser, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -347,10 +380,6 @@ func NewCaches(p *helpers.PathSpec) (Caches, error) {
|
||||
|
||||
baseDir := v.DirCompiled
|
||||
|
||||
if err := cfs.MkdirAll(baseDir, 0777); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bfs := afero.NewBasePathFs(cfs, baseDir)
|
||||
|
||||
var pruneAllRootDir string
|
||||
|
||||
Vendored
+1
-1
@@ -93,7 +93,7 @@ type FileCacheConfig struct {
|
||||
|
||||
// Will resources/_gen will get its own composite filesystem that
|
||||
// also checks any theme.
|
||||
IsResourceDir bool
|
||||
IsResourceDir bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetJSONCache gets the file cache for getJSON.
|
||||
|
||||
Vendored
+6
@@ -53,6 +53,9 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
if c.pruneAllRootDir != "" {
|
||||
return c.pruneRootDir(force)
|
||||
}
|
||||
if err := c.init(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
counter := 0
|
||||
|
||||
@@ -117,6 +120,9 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
}
|
||||
|
||||
func (c *Cache) pruneRootDir(force bool) (int, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
info, err := c.Fs.Stat(c.pruneAllRootDir)
|
||||
if err != nil {
|
||||
if herrors.IsNotExist(err) {
|
||||
|
||||
@@ -515,7 +515,7 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
|
||||
cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory")
|
||||
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
@@ -92,6 +93,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
if err := json.Unmarshal(buf.Bytes(), &m); err != nil {
|
||||
return err
|
||||
}
|
||||
maps.ConvertFloat64WithNoDecimalsToInt(m)
|
||||
switch format {
|
||||
case "yaml":
|
||||
return parser.InterfaceToConfig(m, metadecoders.YAML, os.Stdout)
|
||||
|
||||
+28
-6
@@ -14,6 +14,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -30,8 +31,11 @@ import (
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func newGenCommand() *genCommand {
|
||||
@@ -186,18 +190,36 @@ url: %s
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
r.Println("Generate docs data to", docsHelperTarget)
|
||||
|
||||
targetFile := filepath.Join(docsHelperTarget, "docs.json")
|
||||
var buf bytes.Buffer
|
||||
jsonEnc := json.NewEncoder(&buf)
|
||||
|
||||
configProvider := func() docshelper.DocProvider {
|
||||
conf := hugolib.DefaultConfig()
|
||||
conf.CacheDir = "" // The default value does not make sense in the docs.
|
||||
defaultConfig := parser.LowerCaseCamelJSONMarshaller{Value: conf}
|
||||
return docshelper.DocProvider{"config": defaultConfig}
|
||||
}
|
||||
|
||||
docshelper.AddDocProviderFunc(configProvider)
|
||||
if err := jsonEnc.Encode(docshelper.GetDocProvider()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format.
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(buf.Bytes(), &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetFile := filepath.Join(docsHelperTarget, "docs.yaml")
|
||||
|
||||
f, err := os.Create(targetFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
if err := enc.Encode(docshelper.GetDocProvider()); err != nil {
|
||||
yamlEnc := yaml.NewEncoder(f)
|
||||
if err := yamlEnc.Encode(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+6
-18
@@ -14,7 +14,6 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -24,8 +23,6 @@ import (
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
@@ -86,13 +83,12 @@ func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.P
|
||||
|
||||
// Flags that we for some reason don't want to expose in the site config.
|
||||
internalKeySet := map[string]bool{
|
||||
"quiet": true,
|
||||
"verbose": true,
|
||||
"watch": true,
|
||||
"disableLiveReload": true,
|
||||
"liveReloadPort": true,
|
||||
"renderToMemory": true,
|
||||
"clock": true,
|
||||
"quiet": true,
|
||||
"verbose": true,
|
||||
"watch": true,
|
||||
"liveReloadPort": true,
|
||||
"renderToMemory": true,
|
||||
"clock": true,
|
||||
}
|
||||
|
||||
cmd := cd.CobraCommand
|
||||
@@ -124,11 +120,3 @@ func mkdir(x ...string) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func touchFile(fs afero.Fs, filename string) {
|
||||
mkdir(filepath.Dir(filename))
|
||||
err := helpers.WriteToDisk(filename, bytes.NewReader([]byte{}), fs)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
+26
-191
@@ -16,19 +16,14 @@ package commands
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/common/htime"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/create"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/gohugoio/hugo/create/skeletons"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -99,52 +94,13 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
}
|
||||
sourceFs := conf.fs.Source
|
||||
|
||||
archeTypePath := filepath.Join(createpath, "archetypes")
|
||||
dirs := []string{
|
||||
archeTypePath,
|
||||
filepath.Join(createpath, "assets"),
|
||||
filepath.Join(createpath, "content"),
|
||||
filepath.Join(createpath, "data"),
|
||||
filepath.Join(createpath, "layouts"),
|
||||
filepath.Join(createpath, "static"),
|
||||
filepath.Join(createpath, "themes"),
|
||||
err = skeletons.CreateSite(createpath, sourceFs, force, format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
|
||||
if isDir, _ := helpers.IsDir(createpath, sourceFs); !isDir {
|
||||
return errors.New(createpath + " already exists but not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(createpath, sourceFs)
|
||||
|
||||
switch {
|
||||
case !isEmpty && !force:
|
||||
return errors.New(createpath + " already exists and is not empty. See --force.")
|
||||
|
||||
case !isEmpty && force:
|
||||
all := append(dirs, filepath.Join(createpath, "hugo."+format))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, sourceFs); exists {
|
||||
return errors.New(path + " already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := sourceFs.MkdirAll(dir, 0777); err != nil {
|
||||
return fmt.Errorf("failed to create dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.newSiteCreateConfig(sourceFs, createpath, format)
|
||||
|
||||
// Create a default archetype file.
|
||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||
strings.NewReader(create.DefaultArchetypeTemplateTemplate), sourceFs)
|
||||
|
||||
r.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", createpath)
|
||||
r.Println(c.newSiteNextStepsText())
|
||||
r.Printf("Congratulations! Your new Hugo site was created in %s.\n\n", createpath)
|
||||
r.Println(c.newSiteNextStepsText(createpath, format))
|
||||
|
||||
return nil
|
||||
},
|
||||
@@ -165,91 +121,22 @@ according to your needs.`,
|
||||
if len(args) < 1 {
|
||||
return newUserError("theme name needs to be provided")
|
||||
}
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
cfg := config.New()
|
||||
cfg.Set("publishDir", "public")
|
||||
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ps := h.PathSpec
|
||||
sourceFs := ps.Fs.Source
|
||||
themesDir := h.Configs.LoadingInfo.BaseConfig.ThemesDir
|
||||
createpath := ps.AbsPathify(filepath.Join(themesDir, args[0]))
|
||||
r.Println("Creating theme at", createpath)
|
||||
sourceFs := conf.fs.Source
|
||||
createpath := paths.AbsPathify(conf.configs.Base.WorkingDir, filepath.Join(conf.configs.Base.ThemesDir, args[0]))
|
||||
r.Println("Creating new theme in", createpath)
|
||||
|
||||
if x, _ := helpers.Exists(createpath, sourceFs); x {
|
||||
return errors.New(createpath + " already exists")
|
||||
}
|
||||
|
||||
for _, filename := range []string{
|
||||
"index.html",
|
||||
"404.html",
|
||||
"_default/list.html",
|
||||
"_default/single.html",
|
||||
"partials/head.html",
|
||||
"partials/header.html",
|
||||
"partials/footer.html",
|
||||
} {
|
||||
touchFile(sourceFs, filepath.Join(createpath, "layouts", filename))
|
||||
}
|
||||
|
||||
baseofDefault := []byte(`<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
{{- partial "header.html" . -}}
|
||||
<div id="content">
|
||||
{{- block "main" . }}{{- end }}
|
||||
</div>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "layouts", "_default", "baseof.html"), bytes.NewReader(baseofDefault), sourceFs)
|
||||
err = skeletons.CreateTheme(createpath, sourceFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkdir(createpath, "archetypes")
|
||||
|
||||
archDefault := []byte("+++\n+++\n")
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), sourceFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkdir(createpath, "static", "js")
|
||||
mkdir(createpath, "static", "css")
|
||||
|
||||
by := []byte(`The MIT License (MIT)
|
||||
|
||||
Copyright (c) ` + htime.Now().Format("2006") + ` YOUR_NAME_HERE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE"), bytes.NewReader(by), sourceFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.createThemeMD(ps.Fs.Source, createpath)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
@@ -299,77 +186,25 @@ func (c *newCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *newCommand) newSiteCreateConfig(fs afero.Fs, inpath string, kind string) (err error) {
|
||||
in := map[string]string{
|
||||
"baseURL": "http://example.org/",
|
||||
"title": "My New Hugo Site",
|
||||
"languageCode": "en-us",
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(kind), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(inpath, "hugo."+kind), &buf, fs)
|
||||
}
|
||||
|
||||
func (c *newCommand) newSiteNextStepsText() string {
|
||||
func (c *newCommand) newSiteNextStepsText(path string, format string) string {
|
||||
format = strings.ToLower(format)
|
||||
var nextStepsText bytes.Buffer
|
||||
|
||||
nextStepsText.WriteString(`Just a few more steps and you're ready to go:
|
||||
nextStepsText.WriteString(`Just a few more steps...
|
||||
|
||||
1. Download a theme into the same-named folder.
|
||||
Choose a theme from https://themes.gohugo.io/ or
|
||||
create your own with the "hugo new theme <THEMENAME>" command.
|
||||
2. Perhaps you want to add some content. You can add single files
|
||||
with "hugo new `)
|
||||
1. Change the current directory to ` + path + `.
|
||||
2. Create or install a theme:
|
||||
- Create a new theme with the command "hugo new theme <THEMENAME>"
|
||||
- Install a theme from https://themes.gohugo.io/
|
||||
3. Edit hugo.` + format + `, setting the "theme" property to the theme name.
|
||||
4. Create new content with the command "hugo new content `)
|
||||
|
||||
nextStepsText.WriteString(filepath.Join("<SECTIONNAME>", "<FILENAME>.<FORMAT>"))
|
||||
|
||||
nextStepsText.WriteString(`".
|
||||
3. Start the built-in live server via "hugo server".
|
||||
5. Start the embedded web server with the command "hugo server --buildDrafts".
|
||||
|
||||
Visit https://gohugo.io/ for quickstart guide and full documentation.`)
|
||||
See documentation at https://gohugo.io/.`)
|
||||
|
||||
return nextStepsText.String()
|
||||
}
|
||||
|
||||
func (c *newCommand) createThemeMD(fs afero.Fs, inpath string) (err error) {
|
||||
|
||||
by := []byte(`# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.115.0"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
homepage = ""
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
name = ""
|
||||
homepage = ""
|
||||
repo = ""
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(inpath, "hugo.toml"), strings.NewReader("# Theme config.\n"), fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+11
-11
@@ -23,7 +23,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -470,14 +469,6 @@ func (c *serverCommand) Name() string {
|
||||
}
|
||||
|
||||
func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
err := func() error {
|
||||
defer c.r.timeTrack(time.Now(), "Built")
|
||||
err := c.build()
|
||||
return err
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch runs its own server as part of the routine
|
||||
if c.serverWatch {
|
||||
@@ -501,6 +492,15 @@ func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
|
||||
}
|
||||
|
||||
err := func() error {
|
||||
defer c.r.timeTrack(time.Now(), "Built")
|
||||
err := c.build()
|
||||
return err
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.serve()
|
||||
}
|
||||
|
||||
@@ -685,9 +685,9 @@ func (c *serverCommand) createCertificates(conf *commonConfig) error {
|
||||
c.tlsKeyFile = filepath.Join(keyDir, fmt.Sprintf("%s-key.pem", hostname))
|
||||
|
||||
// Check if the certificate already exists and is valid.
|
||||
certPEM, err := ioutil.ReadFile(c.tlsCertFile)
|
||||
certPEM, err := os.ReadFile(c.tlsCertFile)
|
||||
if err == nil {
|
||||
rootPem, err := ioutil.ReadFile(filepath.Join(mclib.GetCAROOT(), "rootCA.pem"))
|
||||
rootPem, err := os.ReadFile(filepath.Join(mclib.GetCAROOT(), "rootCA.pem"))
|
||||
if err == nil {
|
||||
if err := c.verifyCert(rootPem, certPEM, hostname); err == nil {
|
||||
c.r.Println("Using existing", c.tlsCertFile, "and", c.tlsKeyFile)
|
||||
|
||||
@@ -59,11 +59,34 @@ func GetGID() uint64 {
|
||||
return n
|
||||
}
|
||||
|
||||
// IsFeatureNotAvailableError returns true if the given error is or contains a FeatureNotAvailableError.
|
||||
func IsFeatureNotAvailableError(err error) bool {
|
||||
return errors.Is(err, &FeatureNotAvailableError{})
|
||||
}
|
||||
|
||||
// ErrFeatureNotAvailable denotes that a feature is unavailable.
|
||||
//
|
||||
// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional,
|
||||
// and this error is used to signal those situations.
|
||||
var ErrFeatureNotAvailable = errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")
|
||||
var ErrFeatureNotAvailable = &FeatureNotAvailableError{Cause: errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")}
|
||||
|
||||
// FeatureNotAvailableError is an error type used to signal that a feature is not available.
|
||||
type FeatureNotAvailableError struct {
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Error() string {
|
||||
return e.Cause.Error()
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Is(target error) bool {
|
||||
_, ok := target.(*FeatureNotAvailableError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Must panics if err != nil.
|
||||
func Must(err error) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package herrors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -34,3 +35,12 @@ func TestIsNotExist(t *testing.T) {
|
||||
// os.IsNotExist returns false for wrapped errors.
|
||||
c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestIsFeatureNotAvailableError(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(IsFeatureNotAvailableError(ErrFeatureNotAvailable), qt.Equals, true)
|
||||
c.Assert(IsFeatureNotAvailableError(&FeatureNotAvailableError{}), qt.Equals, true)
|
||||
c.Assert(IsFeatureNotAvailableError(errors.New("asdf")), qt.Equals, false)
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ package hstrings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gohugoio/hugo/compare"
|
||||
)
|
||||
@@ -55,3 +57,45 @@ func EqualAny(a string, b ...string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// regexpCache represents a cache of regexp objects protected by a mutex.
|
||||
type regexpCache struct {
|
||||
mu sync.RWMutex
|
||||
re map[string]*regexp.Regexp
|
||||
}
|
||||
|
||||
func (rc *regexpCache) getOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) {
|
||||
var ok bool
|
||||
|
||||
if re, ok = rc.get(pattern); !ok {
|
||||
re, err = regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc.set(pattern, re)
|
||||
}
|
||||
|
||||
return re, nil
|
||||
}
|
||||
|
||||
func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) {
|
||||
rc.mu.RLock()
|
||||
re, ok = rc.re[key]
|
||||
rc.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *regexpCache) set(key string, re *regexp.Regexp) {
|
||||
rc.mu.Lock()
|
||||
rc.re[key] = re
|
||||
rc.mu.Unlock()
|
||||
}
|
||||
|
||||
var reCache = regexpCache{re: make(map[string]*regexp.Regexp)}
|
||||
|
||||
// GetOrCompileRegexp retrieves a regexp object from the cache based upon the pattern.
|
||||
// If the pattern is not found in the cache, the pattern is compiled and added to
|
||||
// the cache.
|
||||
func GetOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) {
|
||||
return reCache.getOrCompileRegexp(pattern)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package hstrings
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
@@ -34,3 +35,24 @@ func TestStringEqualFold(t *testing.T) {
|
||||
c.Assert(StringEqualFold(s1).Eq("b"), qt.Equals, false)
|
||||
|
||||
}
|
||||
|
||||
func TestGetOrCompileRegexp(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
re, err := GetOrCompileRegexp(`\d+`)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(re.MatchString("123"), qt.Equals, true)
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkGetOrCompileRegexp(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GetOrCompileRegexp(`\d+`)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompileRegexp(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
regexp.MustCompile(`\d+`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2022 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package htime_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
// Issue #11267
|
||||
func TestApplyWithContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
defaultContentLanguage = 'it'
|
||||
-- layouts/index.html --
|
||||
{{ $dates := slice
|
||||
"2022-01-03"
|
||||
"2022-02-01"
|
||||
"2022-03-02"
|
||||
"2022-04-07"
|
||||
"2022-05-06"
|
||||
"2022-06-04"
|
||||
"2022-07-03"
|
||||
"2022-08-01"
|
||||
"2022-09-06"
|
||||
"2022-10-05"
|
||||
"2022-11-03"
|
||||
"2022-12-02"
|
||||
}}
|
||||
{{ range $dates }}
|
||||
{{ . | time.Format "month: _January_ weekday: _Monday_" }}
|
||||
{{ . | time.Format "month: _Jan_ weekday: _Mon_" }}
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
month: _gennaio_ weekday: _lunedì_
|
||||
month: _gen_ weekday: _lun_
|
||||
month: _febbraio_ weekday: _martedì_
|
||||
month: _feb_ weekday: _mar_
|
||||
month: _marzo_ weekday: _mercoledì_
|
||||
month: _mar_ weekday: _mer_
|
||||
month: _aprile_ weekday: _giovedì_
|
||||
month: _apr_ weekday: _gio_
|
||||
month: _maggio_ weekday: _venerdì_
|
||||
month: _mag_ weekday: _ven_
|
||||
month: _giugno_ weekday: _sabato_
|
||||
month: _giu_ weekday: _sab_
|
||||
month: _luglio_ weekday: _domenica_
|
||||
month: _lug_ weekday: _dom_
|
||||
month: _agosto_ weekday: _lunedì_
|
||||
month: _ago_ weekday: _lun_
|
||||
month: _settembre_ weekday: _martedì_
|
||||
month: _set_ weekday: _mar_
|
||||
month: _ottobre_ weekday: _mercoledì_
|
||||
month: _ott_ weekday: _mer_
|
||||
month: _novembre_ weekday: _giovedì_
|
||||
month: _nov_ weekday: _gio_
|
||||
month: _dicembre_ weekday: _venerdì_
|
||||
month: _dic_ weekday: _ven_
|
||||
`)
|
||||
}
|
||||
@@ -124,12 +124,15 @@ func (f TimeFormatter) Format(t time.Time, layout string) string {
|
||||
monthIdx := t.Month() - 1 // Month() starts at 1.
|
||||
dayIdx := t.Weekday()
|
||||
|
||||
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
|
||||
if !strings.Contains(s, f.ltr.MonthWide(t.Month())) {
|
||||
if strings.Contains(layout, "January") {
|
||||
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
|
||||
} else if strings.Contains(layout, "Jan") {
|
||||
s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthAbbreviated(t.Month()))
|
||||
}
|
||||
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
|
||||
if !strings.Contains(s, f.ltr.WeekdayWide(t.Weekday())) {
|
||||
|
||||
if strings.Contains(layout, "Monday") {
|
||||
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
|
||||
} else if strings.Contains(layout, "Mon") {
|
||||
s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdayAbbreviated(t.Weekday()))
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 116,
|
||||
Minor: 119,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -37,13 +37,13 @@ var (
|
||||
|
||||
// Options defines options for the logger.
|
||||
type Options struct {
|
||||
Level logg.Level
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Distinct bool
|
||||
StoreErrors bool
|
||||
HandlerPost func(e *logg.Entry) error
|
||||
SuppresssStatements map[string]bool
|
||||
Level logg.Level
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Distinct bool
|
||||
StoreErrors bool
|
||||
HandlerPost func(e *logg.Entry) error
|
||||
SuppressStatements map[string]bool
|
||||
}
|
||||
|
||||
// New creates a new logger with the given options.
|
||||
@@ -97,8 +97,8 @@ func New(opts Options) Logger {
|
||||
logHandler = newStopHandler(logOnce, logHandler)
|
||||
}
|
||||
|
||||
if opts.SuppresssStatements != nil && len(opts.SuppresssStatements) > 0 {
|
||||
logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppresssStatements), logHandler)
|
||||
if opts.SuppressStatements != nil && len(opts.SuppressStatements) > 0 {
|
||||
logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppressStatements), logHandler)
|
||||
}
|
||||
|
||||
logger := logg.New(
|
||||
@@ -173,6 +173,7 @@ type Logger interface {
|
||||
WarnCommand(command string) logg.LevelLogger
|
||||
Warnf(format string, v ...any)
|
||||
Warnln(v ...any)
|
||||
Deprecatef(fail bool, format string, v ...any)
|
||||
}
|
||||
|
||||
type logAdapter struct {
|
||||
@@ -297,6 +298,15 @@ func (l *logAdapter) sprint(v ...any) string {
|
||||
return strings.TrimRight(fmt.Sprintln(v...), "\n")
|
||||
}
|
||||
|
||||
func (l *logAdapter) Deprecatef(fail bool, format string, v ...any) {
|
||||
format = "DEPRECATED: " + format
|
||||
if fail {
|
||||
l.errorl.Logf(format, v...)
|
||||
} else {
|
||||
l.warnl.Logf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
l logg.LevelLogger
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func TestSuppressStatements(t *testing.T) {
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
SuppresssStatements: map[string]bool{
|
||||
SuppressStatements: map[string]bool{
|
||||
"error-1": true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -210,3 +210,28 @@ func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertFloat64WithNoDecimalsToInt converts float64 values with no decimals to int recursively.
|
||||
func ConvertFloat64WithNoDecimalsToInt(m map[string]any) {
|
||||
for k, v := range m {
|
||||
switch vv := v.(type) {
|
||||
case float64:
|
||||
if v == float64(int64(vv)) {
|
||||
m[k] = int64(vv)
|
||||
}
|
||||
case map[string]any:
|
||||
ConvertFloat64WithNoDecimalsToInt(vv)
|
||||
case []any:
|
||||
for i, vvv := range vv {
|
||||
switch vvvv := vvv.(type) {
|
||||
case float64:
|
||||
if vvv == float64(int64(vvvv)) {
|
||||
vv[i] = int64(vvvv)
|
||||
}
|
||||
case map[string]any:
|
||||
ConvertFloat64WithNoDecimalsToInt(vvvv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/related"
|
||||
"github.com/gohugoio/hugo/resources/images"
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
"github.com/spf13/afero"
|
||||
@@ -57,12 +58,11 @@ type InternalConfig struct {
|
||||
// Server mode?
|
||||
Running bool
|
||||
|
||||
Quiet bool
|
||||
Verbose bool
|
||||
Clock string
|
||||
Watch bool
|
||||
DisableLiveReload bool
|
||||
LiveReloadPort int
|
||||
Quiet bool
|
||||
Verbose bool
|
||||
Clock string
|
||||
Watch bool
|
||||
LiveReloadPort int
|
||||
}
|
||||
|
||||
// All non-params config keys for language.
|
||||
@@ -240,9 +240,14 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
disabledKinds := make(map[string]bool)
|
||||
for _, kind := range c.DisableKinds {
|
||||
kind = strings.ToLower(kind)
|
||||
if kind == "taxonomyterm" {
|
||||
if newKind := kinds.IsDeprecatedAndReplacedWith(kind); newKind != "" {
|
||||
logger.Deprecatef(false, "Kind %q used in disableKinds is deprecated, use %q instead.", kind, newKind)
|
||||
// Legacy config.
|
||||
kind = "term"
|
||||
kind = newKind
|
||||
}
|
||||
if kinds.GetKindAny(kind) == "" {
|
||||
logger.Warnf("Unknown kind %q in disableKinds configuration.", kind)
|
||||
continue
|
||||
}
|
||||
disabledKinds[kind] = true
|
||||
}
|
||||
@@ -250,9 +255,17 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
isRssDisabled := disabledKinds["rss"]
|
||||
outputFormats := c.OutputFormats.Config
|
||||
for kind, formats := range c.Outputs {
|
||||
if newKind := kinds.IsDeprecatedAndReplacedWith(kind); newKind != "" {
|
||||
logger.Deprecatef(false, "Kind %q used in outputs configuration is deprecated, use %q instead.", kind, newKind)
|
||||
kind = newKind
|
||||
}
|
||||
if disabledKinds[kind] {
|
||||
continue
|
||||
}
|
||||
if kinds.GetKindAny(kind) == "" {
|
||||
logger.Warnf("Unknown kind %q in outputs configuration.", kind)
|
||||
continue
|
||||
}
|
||||
for _, format := range formats {
|
||||
if isRssDisabled && format == "rss" {
|
||||
// Legacy config.
|
||||
@@ -454,6 +467,9 @@ type RootConfig struct {
|
||||
// Disable the injection of the Hugo generator tag on the home page.
|
||||
DisableHugoGeneratorInject bool
|
||||
|
||||
// Disable live reloading in server mode.
|
||||
DisableLiveReload bool
|
||||
|
||||
// Enable replacement in Pages' Content of Emoji shortcodes with their equivalent Unicode characters.
|
||||
// <docsmeta>{"identifiers": ["Content", "Unicode"] }</docsmeta>
|
||||
EnableEmoji bool
|
||||
@@ -727,7 +743,8 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
cfg := res.Cfg
|
||||
|
||||
all := &Config{}
|
||||
err := decodeConfigFromParams(fs, bcfg, cfg, all, nil)
|
||||
|
||||
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -815,7 +832,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
// Create a copy of the complete config and replace the root keys with the language specific ones.
|
||||
clone := all.cloneForLang()
|
||||
|
||||
if err := decodeConfigFromParams(fs, bcfg, mergedConfig, clone, differentRootKeys); err != nil {
|
||||
if err := decodeConfigFromParams(fs, logger, bcfg, mergedConfig, clone, differentRootKeys); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode config for language %q: %w", k, err)
|
||||
}
|
||||
if err := clone.CompileConfig(logger); err != nil {
|
||||
@@ -869,6 +886,10 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
|
||||
bcfg.PublishDir = all.PublishDir
|
||||
res.BaseConfig = bcfg
|
||||
all.CommonDirs.CacheDir = bcfg.CacheDir
|
||||
for _, l := range langConfigs {
|
||||
l.CommonDirs.CacheDir = bcfg.CacheDir
|
||||
}
|
||||
|
||||
cm := &Configs{
|
||||
Base: all,
|
||||
@@ -883,7 +904,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, p config.Provider, target *Config, keys []string) error {
|
||||
func decodeConfigFromParams(fs afero.Fs, logger loggers.Logger, bcfg config.BaseConfig, p config.Provider, target *Config, keys []string) error {
|
||||
|
||||
var decoderSetups []decodeWeight
|
||||
|
||||
@@ -896,7 +917,7 @@ func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, p config.Provid
|
||||
if v, found := allDecoderSetups[key]; found {
|
||||
decoderSetups = append(decoderSetups, v)
|
||||
} else {
|
||||
return fmt.Errorf("unknown config key %q", key)
|
||||
logger.Warnf("Skip unknown config key %q", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -933,11 +954,11 @@ func createDefaultOutputFormats(allFormats output.Formats) map[string][]string {
|
||||
}
|
||||
|
||||
m := map[string][]string{
|
||||
page.KindPage: {htmlOut.Name},
|
||||
page.KindHome: defaultListTypes,
|
||||
page.KindSection: defaultListTypes,
|
||||
page.KindTerm: defaultListTypes,
|
||||
page.KindTaxonomy: defaultListTypes,
|
||||
kinds.KindPage: {htmlOut.Name},
|
||||
kinds.KindHome: defaultListTypes,
|
||||
kinds.KindSection: defaultListTypes,
|
||||
kinds.KindTerm: defaultListTypes,
|
||||
kinds.KindTaxonomy: defaultListTypes,
|
||||
}
|
||||
|
||||
// May be disabled
|
||||
|
||||
@@ -49,10 +49,11 @@ type decodeConfig struct {
|
||||
}
|
||||
|
||||
type decodeWeight struct {
|
||||
key string
|
||||
decode func(decodeWeight, decodeConfig) error
|
||||
getCompiler func(c *Config) configCompiler
|
||||
weight int
|
||||
key string
|
||||
decode func(decodeWeight, decodeConfig) error
|
||||
getCompiler func(c *Config) configCompiler
|
||||
weight int
|
||||
internalOrDeprecated bool // Hide it from the docs.
|
||||
}
|
||||
|
||||
var allDecoderSetups = map[string]decodeWeight{
|
||||
@@ -239,7 +240,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
} else {
|
||||
p.c.Related = related.DefaultConfig
|
||||
if _, found := p.c.Taxonomies["tag"]; found {
|
||||
p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80})
|
||||
p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80, Type: related.TypeBasic})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -340,6 +341,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
p.c.Author = maps.CleanConfigStringMap(p.p.GetStringMap(d.key))
|
||||
return nil
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
"social": {
|
||||
key: "social",
|
||||
@@ -347,6 +349,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
p.c.Social = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
return nil
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
"uglyurls": {
|
||||
key: "uglyurls",
|
||||
@@ -362,12 +365,14 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
"internal": {
|
||||
key: "internal",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
return mapstructure.WeakDecode(p.p.GetStringMap(d.key), &p.c.Internal)
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ func (c ConfigLanguage) GetConfigSection(s string) any {
|
||||
return c.config.Permalinks
|
||||
case "minify":
|
||||
return c.config.Minify
|
||||
case "activeModules":
|
||||
case "allModules":
|
||||
return c.m.Modules
|
||||
case "deployment":
|
||||
return c.config.Deployment
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2023 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.
|
||||
@@ -11,10 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
package allconfig
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
)
|
||||
|
||||
@@ -22,8 +23,11 @@ import (
|
||||
func init() {
|
||||
docsProvider := func() docshelper.DocProvider {
|
||||
|
||||
cfg := New()
|
||||
for _, configRoot := range ConfigRootKeys {
|
||||
cfg := config.New()
|
||||
for configRoot, v := range allDecoderSetups {
|
||||
if v.internalOrDeprecated {
|
||||
continue
|
||||
}
|
||||
cfg.Set(configRoot, make(maps.Params))
|
||||
}
|
||||
lang := maps.Params{
|
||||
@@ -38,7 +42,7 @@ func init() {
|
||||
configHelpers := map[string]any{
|
||||
"mergeStrategy": cfg.Get(""),
|
||||
}
|
||||
return docshelper.DocProvider{"config": configHelpers}
|
||||
return docshelper.DocProvider{"config_helpers": configHelpers}
|
||||
}
|
||||
|
||||
docshelper.AddDocProviderFunc(docsProvider)
|
||||
@@ -79,11 +79,12 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
|
||||
if err := configs.transientErr(); err != nil {
|
||||
return nil, fmt.Errorf("failed to create config from modules config: %w", err)
|
||||
}
|
||||
configs.LoadingInfo.ConfigFiles = append(configs.LoadingInfo.ConfigFiles, l.ModulesConfigFiles...)
|
||||
} else if err := configs.transientErr(); err != nil {
|
||||
return nil, fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
|
||||
configs.Modules = moduleConfig.ActiveModules
|
||||
configs.Modules = moduleConfig.AllModules
|
||||
configs.ModulesClient = modulesClient
|
||||
|
||||
if err := configs.Init(); err != nil {
|
||||
@@ -297,14 +298,20 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any = env.Value
|
||||
if _, ok := allDecoderSetups[env.Key]; ok {
|
||||
var val any
|
||||
key := strings.ReplaceAll(env.Key, delim, ".")
|
||||
_, ok := allDecoderSetups[key]
|
||||
if ok {
|
||||
// A map.
|
||||
val, err = metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{})
|
||||
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{}); err == nil {
|
||||
val = v
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
l.cfg.Set(strings.ReplaceAll(env.Key, delim, "."), val)
|
||||
if val == nil {
|
||||
// A string.
|
||||
val = l.envStringToVal(key, env.Value)
|
||||
}
|
||||
l.cfg.Set(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,6 +319,20 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *configLoader) envStringToVal(k, v string) any {
|
||||
switch k {
|
||||
case "disablekinds", "disablelanguages":
|
||||
if strings.Contains(v, ",") {
|
||||
return strings.Split(v, ",")
|
||||
} else {
|
||||
return strings.Fields(v)
|
||||
}
|
||||
default:
|
||||
return v
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *configLoader) loadConfigMain(d ConfigSourceDescriptor) (config.LoadConfigResult, modules.ModulesConfig, error) {
|
||||
var res config.LoadConfigResult
|
||||
|
||||
@@ -450,7 +471,7 @@ func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *mo
|
||||
ex := hexec.New(conf.Security)
|
||||
|
||||
hook := func(m *modules.ModulesConfig) error {
|
||||
for _, tc := range m.ActiveModules {
|
||||
for _, tc := range m.AllModules {
|
||||
if len(tc.ConfigFilenames()) > 0 {
|
||||
if tc.Watch() {
|
||||
l.ModulesConfigFiles = append(l.ModulesConfigFiles, tc.ConfigFilenames()...)
|
||||
|
||||
@@ -148,7 +148,7 @@ func (b BuildConfig) UseResourceCache(err error) bool {
|
||||
}
|
||||
|
||||
if b.UseResourceCacheWhen == "fallback" {
|
||||
return err == herrors.ErrFeatureNotAvailable
|
||||
return herrors.IsFeatureNotAvailableError(err)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -333,11 +333,11 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
}
|
||||
|
||||
source := c.Source
|
||||
target := c.Target
|
||||
sourceRe, err := regexp.Compile(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile cache buster source %q: %w", c.Source, err)
|
||||
}
|
||||
target := c.Target
|
||||
var compileErr error
|
||||
debugl := logger.Logger().WithLevel(logg.LevelDebug).WithField(loggers.FieldNameCmd, "cachebuster")
|
||||
|
||||
@@ -353,23 +353,23 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
return nil
|
||||
}
|
||||
groups := m[1:]
|
||||
currentTarget := target
|
||||
// Replace $1, $2 etc. in target.
|
||||
|
||||
for i, g := range groups {
|
||||
target = strings.ReplaceAll(target, fmt.Sprintf("$%d", i+1), g)
|
||||
currentTarget = strings.ReplaceAll(target, fmt.Sprintf("$%d", i+1), g)
|
||||
}
|
||||
targetRe, err := regexp.Compile(target)
|
||||
targetRe, err := regexp.Compile(currentTarget)
|
||||
if err != nil {
|
||||
compileErr = fmt.Errorf("failed to compile cache buster target %q: %w", target, err)
|
||||
compileErr = fmt.Errorf("failed to compile cache buster target %q: %w", currentTarget, err)
|
||||
return nil
|
||||
}
|
||||
return func(s string) bool {
|
||||
match = targetRe.MatchString(s)
|
||||
return func(ss string) bool {
|
||||
match = targetRe.MatchString(ss)
|
||||
matchString := "no match"
|
||||
if match {
|
||||
matchString = "match!"
|
||||
}
|
||||
logger.Debugf("Matching %q with target %q: %s", s, target, matchString)
|
||||
logger.Debugf("Matching %q with target %q: %s", ss, currentTarget, matchString)
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
@@ -164,3 +164,36 @@ func TestBuildConfigCacheBusters(t *testing.T) {
|
||||
c.Assert(m("json"), qt.IsTrue)
|
||||
|
||||
}
|
||||
|
||||
func TestBuildConfigCacheBusterstTailwindSetup(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := New()
|
||||
cfg.Set("build", map[string]interface{}{
|
||||
"cacheBusters": []map[string]string{
|
||||
{
|
||||
"source": "assets/watching/hugo_stats\\.json",
|
||||
"target": "css",
|
||||
},
|
||||
{
|
||||
"source": "(postcss|tailwind)\\.config\\.js",
|
||||
"target": "css",
|
||||
},
|
||||
{
|
||||
"source": "assets/.*\\.(js|ts|jsx|tsx)",
|
||||
"target": "js",
|
||||
},
|
||||
{
|
||||
"source": "assets/.*\\.(.*)$",
|
||||
"target": "$1",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
conf := DecodeBuildConfig(cfg)
|
||||
l := loggers.NewDefault()
|
||||
c.Assert(conf.CompileConfig(l), qt.IsNil)
|
||||
|
||||
m, err := conf.MatchCacheBuster(l, "assets/watching/hugo_stats.json")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m("css"), qt.IsTrue)
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2021 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 security
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
docsProvider := func() docshelper.DocProvider {
|
||||
|
||||
return docshelper.DocProvider{"config": DefaultConfig.ToSecurityMap()}
|
||||
}
|
||||
docshelper.AddDocProviderFunc(docsProvider)
|
||||
}
|
||||
+1
-1
@@ -42,7 +42,7 @@ const (
|
||||
// DefaultArchetypeTemplateTemplate is the template used in 'hugo new site'
|
||||
// and the template we use as a fall back.
|
||||
DefaultArchetypeTemplateTemplate = `---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||
date = {{ .Date }}
|
||||
draft = true
|
||||
+++
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2023 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 skeletons
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
//go:embed all:site/*
|
||||
var siteFs embed.FS
|
||||
|
||||
//go:embed all:theme/*
|
||||
var themeFs embed.FS
|
||||
|
||||
// CreateTheme creates a theme skeleton.
|
||||
func CreateTheme(createpath string, sourceFs afero.Fs) error {
|
||||
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
|
||||
return errors.New(createpath + " already exists")
|
||||
}
|
||||
return copyFiles(createpath, sourceFs, themeFs)
|
||||
}
|
||||
|
||||
// CreateSite creates a site skeleton.
|
||||
func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string) error {
|
||||
format = strings.ToLower(format)
|
||||
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
|
||||
if isDir, _ := helpers.IsDir(createpath, sourceFs); !isDir {
|
||||
return errors.New(createpath + " already exists but not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(createpath, sourceFs)
|
||||
|
||||
switch {
|
||||
case !isEmpty && !force:
|
||||
return errors.New(createpath + " already exists and is not empty. See --force.")
|
||||
case !isEmpty && force:
|
||||
var all []string
|
||||
fs.WalkDir(siteFs, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() && path != "." {
|
||||
all = append(all, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
all = append(all, filepath.Join(createpath, "hugo."+format))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, sourceFs); exists {
|
||||
return errors.New(path + " already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := newSiteCreateConfig(sourceFs, createpath, format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return copyFiles(createpath, sourceFs, siteFs)
|
||||
}
|
||||
|
||||
func copyFiles(createpath string, sourceFs afero.Fs, skeleton embed.FS) error {
|
||||
return fs.WalkDir(skeleton, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
_, slug, _ := strings.Cut(path, "/")
|
||||
if d.IsDir() {
|
||||
return sourceFs.MkdirAll(filepath.Join(createpath, slug), 0777)
|
||||
} else {
|
||||
if filepath.Base(path) != ".gitkeep" {
|
||||
data, _ := fs.ReadFile(skeleton, path)
|
||||
return helpers.WriteToDisk(filepath.Join(createpath, slug), bytes.NewReader(data), sourceFs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newSiteCreateConfig(fs afero.Fs, createpath string, format string) (err error) {
|
||||
in := map[string]string{
|
||||
"baseURL": "https://example.org/",
|
||||
"title": "My New Hugo Site",
|
||||
"languageCode": "en-us",
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(format), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(createpath, "hugo."+format), &buf, fs)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Theme Name
|
||||
|
||||
## Features
|
||||
|
||||
## Installation
|
||||
|
||||
## Configuration
|
||||
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||
date = {{ .Date }}
|
||||
draft = true
|
||||
+++
|
||||
@@ -0,0 +1,22 @@
|
||||
body {
|
||||
color: #222;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.5;
|
||||
margin: 1rem;
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid #222;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #222;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00e;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
console.log('This site was generated by Hugo.');
|
||||
@@ -0,0 +1,9 @@
|
||||
+++
|
||||
title = 'Home'
|
||||
date = 2023-01-01T08:00:00-07:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
Laborum voluptate pariatur ex culpa magna nostrud est incididunt fugiat
|
||||
pariatur do dolor ipsum enim. Consequat tempor do dolor eu. Non id id anim anim
|
||||
excepteur excepteur pariatur nostrud qui irure ullamco.
|
||||
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = 'Posts'
|
||||
date = 2023-01-01T08:30:00-07:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
Tempor est exercitation ad qui pariatur quis adipisicing aliquip nisi ea consequat ipsum occaecat. Nostrud consequat ullamco laboris fugiat esse esse adipisicing velit laborum ipsum incididunt ut enim. Dolor pariatur nulla quis fugiat dolore excepteur. Aliquip ad quis aliqua enim do consequat.
|
||||
@@ -0,0 +1,10 @@
|
||||
+++
|
||||
title = 'Post 1'
|
||||
date = 2023-01-15T09:00:00-07:00
|
||||
draft = false
|
||||
tags = ['red']
|
||||
+++
|
||||
|
||||
Tempor proident minim aliquip reprehenderit dolor et ad anim Lorem duis sint eiusmod. Labore ut ea duis dolor. Incididunt consectetur proident qui occaecat incididunt do nisi Lorem. Tempor do laborum elit laboris excepteur eiusmod do. Eiusmod nisi excepteur ut amet pariatur adipisicing Lorem.
|
||||
|
||||
Occaecat nulla excepteur dolore excepteur duis eiusmod ullamco officia anim in voluptate ea occaecat officia. Cillum sint esse velit ea officia minim fugiat. Elit ea esse id aliquip pariatur cupidatat id duis minim incididunt ea ea. Anim ut duis sunt nisi. Culpa cillum sit voluptate voluptate eiusmod dolor. Enim nisi Lorem ipsum irure est excepteur voluptate eu in enim nisi. Nostrud ipsum Lorem anim sint labore consequat do.
|
||||
@@ -0,0 +1,10 @@
|
||||
+++
|
||||
title = 'Post 2'
|
||||
date = 2023-02-15T10:00:00-07:00
|
||||
draft = false
|
||||
tags = ['red','green']
|
||||
+++
|
||||
|
||||
Anim eiusmod irure incididunt sint cupidatat. Incididunt irure irure irure nisi ipsum do ut quis fugiat consectetur proident cupidatat incididunt cillum. Dolore voluptate occaecat qui mollit laborum ullamco et. Ipsum laboris officia anim laboris culpa eiusmod ex magna ex cupidatat anim ipsum aute. Mollit aliquip occaecat qui sunt velit ut cupidatat reprehenderit enim sunt laborum. Velit veniam in officia nulla adipisicing ut duis officia.
|
||||
|
||||
Exercitation voluptate irure in irure tempor mollit Lorem nostrud ad officia. Velit id fugiat occaecat do tempor. Sit officia Lorem aliquip eu deserunt consectetur. Aute proident deserunt in nulla aliquip dolore ipsum Lorem ut cupidatat consectetur sit sint laborum. Esse cupidatat sit sint sunt tempor exercitation deserunt. Labore dolor duis laborum est do nisi ut veniam dolor et nostrud nostrud.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,12 @@
|
||||
+++
|
||||
title = 'Post 3'
|
||||
date = 2023-03-15T11:00:00-07:00
|
||||
draft = false
|
||||
tags = ['red','green','blue']
|
||||
+++
|
||||
|
||||
Occaecat aliqua consequat laborum ut ex aute aliqua culpa quis irure esse magna dolore quis. Proident fugiat labore eu laboris officia Lorem enim. Ipsum occaecat cillum ut tempor id sint aliqua incididunt nisi incididunt reprehenderit. Voluptate ad minim sint est aute aliquip esse occaecat tempor officia qui sunt. Aute ex ipsum id ut in est velit est laborum incididunt. Aliqua qui id do esse sunt eiusmod id deserunt eu nostrud aute sit ipsum. Deserunt esse cillum Lorem non magna adipisicing mollit amet consequat.
|
||||
|
||||

|
||||
|
||||
Sit excepteur do velit veniam mollit in nostrud laboris incididunt ea. Amet eu cillum ut reprehenderit culpa aliquip labore laborum amet sit sit duis. Laborum id proident nostrud dolore laborum reprehenderit quis mollit nulla amet veniam officia id id. Aliquip in deserunt qui magna duis qui pariatur officia sunt deserunt.
|
||||
@@ -0,0 +1,23 @@
|
||||
baseURL = 'https://example.org/'
|
||||
languageCode = 'en-us'
|
||||
title = 'My New Hugo Site'
|
||||
|
||||
[[menus.main]]
|
||||
name = 'Home'
|
||||
pageRef = '/'
|
||||
weight = 10
|
||||
|
||||
[[menus.main]]
|
||||
name = 'Posts'
|
||||
pageRef = '/posts'
|
||||
weight = 20
|
||||
|
||||
[[menus.main]]
|
||||
name = 'Tags'
|
||||
pageRef = '/tags'
|
||||
weight = 30
|
||||
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
extended = false
|
||||
min = "0.116.0"
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{{ partial "header.html" . }}
|
||||
</header>
|
||||
<main>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
<footer>
|
||||
{{ partial "footer.html" . }}
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
{{ define "main" }}
|
||||
{{ .Content }}
|
||||
{{ range site.RegularPages }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ .Summary }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,8 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
{{ range .Pages }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ .Summary }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
|
||||
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||
{{ $dateHuman := .Date | time.Format ":date_long" }}
|
||||
<time datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>
|
||||
|
||||
{{ .Content }}
|
||||
{{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1 @@
|
||||
<p>Copyright {{ now.Year }}. All rights reserved.</p>
|
||||
@@ -0,0 +1,5 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0">
|
||||
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
||||
{{ partialCached "head/css.html" . }}
|
||||
{{ partialCached "head/js.html" . }}
|
||||
@@ -0,0 +1,9 @@
|
||||
{{- with resources.Get "css/main.css" }}
|
||||
{{- if eq hugo.Environment "development" }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{- else }}
|
||||
{{- with . | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,12 @@
|
||||
{{- with resources.Get "js/main.js" }}
|
||||
{{- if eq hugo.Environment "development" }}
|
||||
{{- with . | js.Build }}
|
||||
<script src="{{ .RelPermalink }}"></script>
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- $opts := dict "minify" true }}
|
||||
{{- with . | js.Build $opts | fingerprint }}
|
||||
<script src="{{ .RelPermalink }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,2 @@
|
||||
<h1>{{ site.Title }}</h1>
|
||||
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
||||
@@ -0,0 +1,45 @@
|
||||
{{- /*
|
||||
Renders a menu for the given menu ID.
|
||||
|
||||
@context {page} page The current page.
|
||||
@context {string} menuID The menu ID.
|
||||
|
||||
@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
||||
*/}}
|
||||
|
||||
{{- $page := .page }}
|
||||
{{- $menuID := .menuID }}
|
||||
|
||||
{{- with index site.Menus $menuID }}
|
||||
<nav>
|
||||
<ul>
|
||||
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{- define "partials/inline/menu/walk.html" }}
|
||||
{{- $page := .page }}
|
||||
{{- range .menuEntries }}
|
||||
{{- $attrs := dict "href" .URL }}
|
||||
{{- if $page.IsMenuCurrent .Menu . }}
|
||||
{{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }}
|
||||
{{- else if $page.HasMenuCurrent .Menu .}}
|
||||
{{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }}
|
||||
{{- end }}
|
||||
<li>
|
||||
<a
|
||||
{{- range $k, $v := $attrs }}
|
||||
{{- with $v }}
|
||||
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
>{{ or (T .Identifier) .Name | safeHTML }}</a>
|
||||
{{- with .Children }}
|
||||
<ul>
|
||||
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||
</ul>
|
||||
{{- end }}
|
||||
</li>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{- /*
|
||||
For a given taxonomy, renders a list of terms assigned to the page.
|
||||
|
||||
@context {page} page The current page.
|
||||
@context {string} taxonomy The taxonony.
|
||||
|
||||
@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||
*/}}
|
||||
|
||||
{{- $page := .page }}
|
||||
{{- $taxonomy := .taxonomy }}
|
||||
|
||||
{{- with $page.GetTerms $taxonomy }}
|
||||
{{- $label := (index . 0).Parent.LinkTitle }}
|
||||
<div>
|
||||
<div>{{ $label }}:</div>
|
||||
<ul>
|
||||
{{- range . }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</div>
|
||||
{{- end }}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,31 @@
|
||||
name = 'Theme name'
|
||||
license = 'MIT'
|
||||
licenselink = 'https://github.com/owner/repo/LICENSE'
|
||||
description = 'Theme description'
|
||||
|
||||
# The home page of the theme, where the source can be found
|
||||
homepage = 'https://github.com/owner/repo'
|
||||
|
||||
# If you have a running demo of the theme
|
||||
demosite = 'https://owner.github.io/repo'
|
||||
|
||||
# Taxonomy terms
|
||||
tags = ['blog', 'company']
|
||||
features = ['some', 'awesome', 'features']
|
||||
|
||||
# If the theme has multiple authors
|
||||
authors = [
|
||||
{name = 'Name of author', homepage = 'Website of author'},
|
||||
{name = 'Name of author', homepage = 'Website of author'}
|
||||
]
|
||||
|
||||
# If the theme has a single author
|
||||
[author]
|
||||
name = 'Your name'
|
||||
homepage = 'Your website'
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
author = 'Name of original author'
|
||||
homepage = 'Website of original author'
|
||||
repo = 'https://github.com/owner/repo'
|
||||
+10
-12
@@ -18,29 +18,27 @@ package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/cloudfront"
|
||||
gcaws "gocloud.dev/aws"
|
||||
)
|
||||
|
||||
// InvalidateCloudFront invalidates the CloudFront cache for distributionID.
|
||||
// It uses the default AWS credentials from the environment.
|
||||
func InvalidateCloudFront(ctx context.Context, distributionID string) error {
|
||||
// SharedConfigEnable enables loading "shared config (~/.aws/config) and
|
||||
// shared credentials (~/.aws/credentials) files".
|
||||
// See https://docs.aws.amazon.com/sdk-for-go/api/aws/session/ for more
|
||||
// details.
|
||||
// This is the same codepath used by Go CDK when creating an s3 URL.
|
||||
// TODO: Update this to a Go CDK helper once available
|
||||
// (https://github.com/google/go-cloud/issues/2003).
|
||||
sess, err := session.NewSessionWithOptions(session.Options{SharedConfigState: session.SharedConfigEnable})
|
||||
// Uses AWS credentials config from the bucket URL.
|
||||
func InvalidateCloudFront(ctx context.Context, target *Target) error {
|
||||
u, err := url.Parse(target.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sess, _, err := gcaws.NewSessionFromURLParams(u.Query())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &cloudfront.CreateInvalidationInput{
|
||||
DistributionId: aws.String(distributionID),
|
||||
DistributionId: aws.String(target.CloudFrontDistributionID),
|
||||
InvalidationBatch: &cloudfront.InvalidationBatch{
|
||||
CallerReference: aws.String(time.Now().Format("20060102150405")),
|
||||
Paths: &cloudfront.Paths{
|
||||
|
||||
+1
-1
@@ -271,7 +271,7 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
}
|
||||
} else {
|
||||
d.logger.Println("Invalidating CloudFront CDN...")
|
||||
if err := InvalidateCloudFront(ctx, d.target.CloudFrontDistributionID); err != nil {
|
||||
if err := InvalidateCloudFront(ctx, d.target); err != nil {
|
||||
d.logger.Printf("Failed to invalidate CloudFront CDN: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
### Asking Support Questions
|
||||
### Asking support questions
|
||||
|
||||
We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions. Please don't use the GitHub issue tracker to ask questions.
|
||||
|
||||
@@ -12,6 +12,18 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.prose-glossary h3 {
|
||||
margin-top: 0;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.prose-glossary h3:first-of-type {
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.prose-glossary h3 ~ p {
|
||||
margin: 0.5em 0 2em 0;
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
|
||||
+3
-2
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 9.3 KiB |
Generated
+6
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
Generated
+6
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
@@ -804,6 +804,8 @@ img { max-width: 100%; }
|
||||
.b--washed-red { border-color: #ffdfdf; }
|
||||
.b--transparent { border-color: transparent; }
|
||||
.b--inherit { border-color: inherit; }
|
||||
.b--initial { border-color: currentColor; border-color: initial; }
|
||||
.b--unset { border-color: unset; }
|
||||
/*
|
||||
|
||||
BORDER RADIUS
|
||||
@@ -852,6 +854,9 @@ img { max-width: 100%; }
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.br-inherit { border-radius: inherit; }
|
||||
.br-initial { border-radius: 0; border-radius: initial; }
|
||||
.br-unset { border-radius: unset; }
|
||||
@media screen and (min-width: 30em) {
|
||||
.br0-ns { border-radius: 0; }
|
||||
.br1-ns { border-radius: .125rem; }
|
||||
@@ -876,6 +881,9 @@ img { max-width: 100%; }
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.br-inherit-ns { border-radius: inherit; }
|
||||
.br-initial-ns { border-radius: 0; border-radius: initial; }
|
||||
.br-unset-ns { border-radius: unset; }
|
||||
}
|
||||
@media screen and (min-width: 30em) and (max-width: 60em) {
|
||||
.br0-m { border-radius: 0; }
|
||||
@@ -901,6 +909,9 @@ img { max-width: 100%; }
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.br-inherit-m { border-radius: inherit; }
|
||||
.br-initial-m { border-radius: 0; border-radius: initial; }
|
||||
.br-unset-m { border-radius: unset; }
|
||||
}
|
||||
@media screen and (min-width: 60em) {
|
||||
.br0-l { border-radius: 0; }
|
||||
@@ -926,6 +937,9 @@ img { max-width: 100%; }
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.br-inherit-l { border-radius: inherit; }
|
||||
.br-initial-l { border-radius: 0; border-radius: initial; }
|
||||
.br-unset-l { border-radius: unset; }
|
||||
}
|
||||
/*
|
||||
|
||||
@@ -2367,6 +2381,7 @@ img { max-width: 100%; }
|
||||
.washed-yellow { color: #fffceb; }
|
||||
.washed-red { color: #ffdfdf; }
|
||||
.color-inherit { color: inherit; }
|
||||
/* Background colors */
|
||||
.bg-black-90 { background-color: rgba(0, 0, 0, .9); }
|
||||
.bg-black-80 { background-color: rgba(0, 0, 0, .8); }
|
||||
.bg-black-70 { background-color: rgba(0, 0, 0, .7); }
|
||||
@@ -2386,7 +2401,6 @@ img { max-width: 100%; }
|
||||
.bg-white-30 { background-color: rgba(255, 255, 255, .3); }
|
||||
.bg-white-20 { background-color: rgba(255, 255, 255, .2); }
|
||||
.bg-white-10 { background-color: rgba(255, 255, 255, .1); }
|
||||
/* Background colors */
|
||||
.bg-black { background-color: #000; }
|
||||
.bg-near-black { background-color: #111; }
|
||||
.bg-dark-gray { background-color: #333; }
|
||||
@@ -4671,6 +4685,7 @@ h6:hover .header-link {
|
||||
.searchbox__input::-webkit-input-placeholder{color:#aaa}
|
||||
.searchbox__input:-ms-input-placeholder{color:#aaa}
|
||||
.searchbox__input::-ms-input-placeholder{color:#aaa}
|
||||
.searchbox__input::-moz-placeholder{color:#aaa}
|
||||
.searchbox__input::placeholder{color:#aaa}
|
||||
.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69, 142, 225, 0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}
|
||||
.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}
|
||||
@@ -4904,14 +4919,14 @@ pre {
|
||||
text-decoration: none;
|
||||
|
||||
}
|
||||
.column-count-2 {-webkit-column-count: 1;column-count: 1}
|
||||
.column-gap-1 {-webkit-column-gap: 0;column-gap: 0}
|
||||
.break-inside-avoid {-webkit-column-break-inside: auto;break-inside: auto}
|
||||
.column-count-2 {-webkit-column-count: 1;-moz-column-count: 1;column-count: 1}
|
||||
.column-gap-1 {-webkit-column-gap: 0;-moz-column-gap: 0;column-gap: 0}
|
||||
.break-inside-avoid {-webkit-column-break-inside: auto;page-break-inside: auto;break-inside: auto}
|
||||
@media screen and (min-width: 60em) {
|
||||
.column-count-3-l {-webkit-column-count: 3;column-count: 3}
|
||||
.column-count-2-l {-webkit-column-count: 2;column-count: 2}
|
||||
.column-gap-1-l {-webkit-column-gap: 1;column-gap: 1}
|
||||
.break-inside-avoid-l {-webkit-column-break-inside: avoid;break-inside: avoid}
|
||||
.column-count-3-l {-webkit-column-count: 3;-moz-column-count: 3;column-count: 3}
|
||||
.column-count-2-l {-webkit-column-count: 2;-moz-column-count: 2;column-count: 2}
|
||||
.column-gap-1-l {-webkit-column-gap: 1;-moz-column-gap: 1;column-gap: 1}
|
||||
.break-inside-avoid-l {-webkit-column-break-inside: avoid;page-break-inside: avoid;break-inside: avoid}
|
||||
}
|
||||
.prose ul, .prose ol {
|
||||
margin-bottom: 2em;
|
||||
@@ -4922,10 +4937,24 @@ pre {
|
||||
.prose li:hover {
|
||||
background-color: #eee
|
||||
}
|
||||
.prose ::-moz-selection {
|
||||
background: #0594CB; /* WebKit/Blink Browsers */
|
||||
color: white;
|
||||
}
|
||||
.prose ::selection {
|
||||
background: #0594CB; /* WebKit/Blink Browsers */
|
||||
color: white;
|
||||
}
|
||||
.prose-glossary h3 {
|
||||
margin-top: 0;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
.prose-glossary h3:first-of-type {
|
||||
margin-top: 3em;
|
||||
}
|
||||
.prose-glossary h3 ~ p {
|
||||
margin: 0.5em 0 2em 0;
|
||||
}
|
||||
body {
|
||||
|
||||
line-height: 1.45;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+11
-10
@@ -3,19 +3,20 @@
|
||||
link = "https://www.linode.com/"
|
||||
logo = "images/sponsors/linode-logo.svg"
|
||||
utm_campaign = "hugosponsor"
|
||||
bgcolor = "#ffffff"
|
||||
|
||||
[[banners]]
|
||||
name = "CloudCannon"
|
||||
link = "https://cloudcannon.com/hugo-cms/"
|
||||
logo = "/images/sponsors/cloudcannon-white.svg"
|
||||
utm_campaign = "HugoSponsorship"
|
||||
utm_source = "sponsor"
|
||||
utm_content = "gohugo"
|
||||
bgcolor = "#034AD8"
|
||||
|
||||
[[banners]]
|
||||
name = "Your Company?"
|
||||
link = "https://bep.is/en/hugo-sponsor-2023-01/"
|
||||
logo = "/images/sponsors/your-company.svg"
|
||||
utm_campaign = "hugosponsor"
|
||||
show_on_hover = true
|
||||
bgcolor = "#004887"
|
||||
|
||||
[[banners]]
|
||||
name = "Your Company?"
|
||||
link = "https://bep.is/en/hugo-sponsor-2023-01/"
|
||||
logo = "/images/sponsors/your-company.svg"
|
||||
utm_campaign = "hugosponsor"
|
||||
show_on_hover = true
|
||||
bgcolor = "#004887"
|
||||
bgcolor = "#4e4f4f"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<img src="{{ . }}" alt="Featured Image for {{ $.Title }}" class="mw-100">
|
||||
{{ end }}
|
||||
|
||||
<div class="prose" id="prose">
|
||||
<div class="prose prose-{{ .Type }}" id="prose">
|
||||
{{- partial "docs/functions-signature.html" . -}}
|
||||
{{ .Content }}
|
||||
</div>
|
||||
|
||||
Generated
+3
-3
@@ -23,7 +23,7 @@
|
||||
class="{{ $classes_box }} o-100"
|
||||
style="background-color: {{ .bgcolor }};">
|
||||
{{ $query_params := .query_params | default "" }}
|
||||
{{ $url := printf "%s?%s%s" .link $query_params (querify "utm_source" $utmSource "utm_medium" "banner" "utm_campaign" (.utm_campaign | default "hugosponsor")) | safeURL }}
|
||||
{{ $url := printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" "banner" "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }}
|
||||
{{ $logo := resources.Get .logo }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ $gtagID := printf "Sponsor %s %s" .name $gtag | title }}
|
||||
@@ -34,7 +34,7 @@
|
||||
show-on-hover
|
||||
{{ end }}"
|
||||
style="">
|
||||
{{ $logo.Content | safeHTML }}
|
||||
{{ with $logo }}{{ .Content | safeHTML }}{{ end }}
|
||||
</a>
|
||||
{{ else }}
|
||||
<a
|
||||
@@ -42,7 +42,7 @@
|
||||
class="w-100 grow pa3{{ if .show_on_hover }}
|
||||
show-on-hover
|
||||
{{ end }}">
|
||||
{{ $logo.Content | safeHTML }}
|
||||
{{ with $logo }}{{ .Content | safeHTML }}{{ end }}
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
@@ -1 +1 @@
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20230527124826-78bc315d7b8a
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20230812102551-06f8256e7ee5
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
url = "/installation/"
|
||||
|
||||
[[docs]]
|
||||
name = "Getting Started"
|
||||
name = "Getting started"
|
||||
weight = 30
|
||||
identifier = "getting-started"
|
||||
url = "/getting-started/"
|
||||
@@ -26,7 +26,7 @@
|
||||
# Core menus
|
||||
|
||||
[[docs]]
|
||||
name = "Content Management"
|
||||
name = "Content management"
|
||||
weight = 50
|
||||
identifier = "content-management"
|
||||
post = "expanded"
|
||||
@@ -53,7 +53,7 @@
|
||||
[[docs]]
|
||||
name = "Hugo Pipes"
|
||||
weight = 90
|
||||
identifier = "pipes"
|
||||
identifier = "hugo-pipes"
|
||||
url = "/hugo-pipes/"
|
||||
|
||||
[[docs]]
|
||||
@@ -72,13 +72,13 @@
|
||||
url = "/troubleshooting/"
|
||||
|
||||
[[docs]]
|
||||
name = "Tools"
|
||||
name = "Developer tools"
|
||||
weight = 120
|
||||
identifier = "tools"
|
||||
identifier = "developer-tools"
|
||||
url = "/tools/"
|
||||
|
||||
[[docs]]
|
||||
name = "Hosting & Deployment"
|
||||
name = "Hosting and deployment"
|
||||
weight = 130
|
||||
identifier = "hosting-and-deployment"
|
||||
url = "/hosting-and-deployment/"
|
||||
|
||||
@@ -8,8 +8,6 @@ ghrepo = "https://github.com/gohugoio/hugoDocs/"
|
||||
github_repo = "https://github.com/gohugoio/hugo/issues/new"
|
||||
### Edit content repo (set to automatically enter "edit" mode; this is good for "improve this page" links)
|
||||
ghdocsrepo = "https://github.com/gohugoio/hugoDocs/tree/master/docs"
|
||||
## Gitter URL
|
||||
gitter = "https://gitter.im/spf13/hugo"
|
||||
## Discuss Forum URL
|
||||
forum = "https://discourse.gohugo.io/"
|
||||
## Google Tag Manager
|
||||
@@ -20,4 +18,4 @@ images = ["images/gohugoio-card.png"]
|
||||
|
||||
flex_box_interior_classes = "flex-auto w-100 w-40-l mr3 mb3 bg-white ba b--moon-gray nested-copy-line-height"
|
||||
|
||||
#sidebar_direction = "sidebar_left"
|
||||
#sidebar_direction = "sidebar_left"
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
|
||||
enableInlineShortcodes = false
|
||||
|
||||
[exec]
|
||||
allow = ['^go$']
|
||||
osEnv = ['^PATH$']
|
||||
|
||||
[funcs]
|
||||
getenv = ['^HUGO_', '^REPOSITORY_URL$', '^BRANCH$']
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "The world’s fastest framework for building websites"
|
||||
title: The world’s fastest framework for building websites
|
||||
date: 2017-03-02T12:00:00-05:00
|
||||
features:
|
||||
- heading: Blistering Speed
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
---
|
||||
title: About Hugo
|
||||
linktitle: Overview
|
||||
linkTitle: Overview
|
||||
description: Hugo's features, roadmap, license, and motivation.
|
||||
categories: []
|
||||
keywords: []
|
||||
menu:
|
||||
docs:
|
||||
identifier: about-hugo-overview
|
||||
parent: about
|
||||
weight: 1
|
||||
weight: 1
|
||||
weight: 10
|
||||
weight: 10
|
||||
aliases: [/about-hugo/,/docs/]
|
||||
---
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: The Benefits of Static Site Generators
|
||||
linktitle: The Benefits of Static
|
||||
title: Benefits of static site generators
|
||||
linkTitle: Static site generators
|
||||
description: Improved performance, security and ease of use are just a few of the reasons static site generators are so appealing.
|
||||
keywords: [ssg,static,performance,security]
|
||||
menu:
|
||||
docs:
|
||||
parent: about
|
||||
weight: 30
|
||||
weight: 30
|
||||
weight: 40
|
||||
weight: 40
|
||||
---
|
||||
|
||||
The purpose of website generators is to render content into HTML files. Most are "dynamic site generators." That means the HTTP server---i.e., the program that sends files to the browser to be viewed---runs the generator to create a new HTML file every time an end user requests a page.
|
||||
@@ -18,7 +18,7 @@ Hugo takes caching a step further and all HTML files are rendered on your comput
|
||||
|
||||
This has many benefits. The most noticeable is performance. HTTP servers are *very* good at sending files---so good, in fact, that you can effectively serve the same number of pages with a fraction of the memory and CPU needed for a dynamic site.
|
||||
|
||||
## More on Static Site Generators
|
||||
## More on static site generators
|
||||
|
||||
* ["An Introduction to Static Site Generators", David Walsh]
|
||||
* ["Hugo vs. WordPress page load speed comparison: Hugo leaves WordPress in its dust", GettingThingsTech][hugovwordpress]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Hugo Features
|
||||
title: Hugo features
|
||||
description: Hugo boasts blistering speed, robust content management, and a powerful templating language making it a great fit for all kinds of static websites.
|
||||
menu:
|
||||
docs:
|
||||
parent: about
|
||||
weight: 20
|
||||
weight: 20
|
||||
weight: 30
|
||||
weight: 30
|
||||
toc: true
|
||||
---
|
||||
|
||||
@@ -40,7 +40,7 @@ toc: true
|
||||
* ["Minutes to Read"][pagevars] functionality
|
||||
* ["WordCount"][pagevars] functionality
|
||||
|
||||
## Additional Features
|
||||
## Additional features
|
||||
|
||||
* Integrated [Disqus] comment support
|
||||
* Integrated [Google Analytics] support
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Hugo and the General Data Protection Regulation (GDPR)
|
||||
linktitle: Hugo and GDPR
|
||||
title: Hugo and the General Data Protection Regulation
|
||||
linkTitle: Hugo and the GDPR
|
||||
description: About how to configure your Hugo site to meet the new regulations.
|
||||
layout: single
|
||||
keywords: ["GDPR", "Privacy", "Data Protection"]
|
||||
menu:
|
||||
docs:
|
||||
parent: about
|
||||
weight: 5
|
||||
weight: 5
|
||||
weight: 60
|
||||
weight: 60
|
||||
aliases: [/privacy/,/gdpr/]
|
||||
toc: true
|
||||
---
|
||||
@@ -17,7 +17,7 @@ toc: true
|
||||
|
||||
**Hugo is a static site generator. By using Hugo you are already standing on very solid ground. Static HTML files on disk are much easier to reason about compared to server and database driven web sites.**
|
||||
|
||||
But even static websites can integrate with external services, so from version `0.41`, Hugo provides a **Privacy Config** that covers the relevant built-in templates.
|
||||
But even static websites can integrate with external services, so from version `0.41`, Hugo provides a **privacy configuration** that covers the relevant built-in templates.
|
||||
|
||||
Note that:
|
||||
|
||||
@@ -25,9 +25,9 @@ toc: true
|
||||
* These settings work with the [internal templates](/templates/internal/). Some theme may contain custom templates for embedding services like Google Analytics. In that case these options have no effect.
|
||||
* We will continue this work and improve this further in future Hugo versions.
|
||||
|
||||
## All Privacy Settings
|
||||
## All privacy settings
|
||||
|
||||
Below are all privacy settings and their default value. These settings need to be put in your site config (e.g. `hugo.toml`).
|
||||
Below are all privacy settings and their default value. These settings need to be put in your site configuration (e.g. `hugo.toml`).
|
||||
|
||||
{{< code-toggle file="hugo" >}}
|
||||
[privacy]
|
||||
@@ -54,11 +54,11 @@ disable = false
|
||||
privacyEnhanced = false
|
||||
{{< /code-toggle >}}
|
||||
|
||||
## Disable All Services
|
||||
## Disable all services
|
||||
|
||||
An example Privacy Config that disables all the relevant services in Hugo. With this configuration, the other settings will not matter.
|
||||
An example privacy configuration that disables all the relevant services in Hugo. With this configuration, the other settings will not matter.
|
||||
|
||||
{{< code-toggle file="hugo" >}}
|
||||
{{< code-toggle file="hugo" >}}
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = true
|
||||
@@ -74,7 +74,7 @@ disable = true
|
||||
disable = true
|
||||
{{< /code-toggle >}}
|
||||
|
||||
## The Privacy Settings Explained
|
||||
## The privacy settings explained
|
||||
|
||||
### GoogleAnalytics
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
title: Apache License
|
||||
linktitle: License
|
||||
title: License
|
||||
description: Hugo v0.15 and later are released under the Apache 2.0 license.
|
||||
categories: ["about hugo"]
|
||||
keywords: ["License","apache"]
|
||||
menu:
|
||||
docs:
|
||||
parent: about
|
||||
weight: 60
|
||||
weight: 60
|
||||
weight: 70
|
||||
weight: 70
|
||||
aliases: [/meta/license]
|
||||
toc: true
|
||||
---
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
---
|
||||
title: Hugo's Security Model
|
||||
title: Hugo's security model
|
||||
description: A summary of Hugo's security model.
|
||||
layout: single
|
||||
keywords: ["Security", "Privacy"]
|
||||
menu:
|
||||
docs:
|
||||
parent: about
|
||||
weight: 4
|
||||
weight: 5
|
||||
weight: 50
|
||||
weight: 50
|
||||
aliases: [/security/]
|
||||
toc: true
|
||||
---
|
||||
|
||||
## Runtime Security
|
||||
## Runtime security
|
||||
|
||||
Hugo produces static output, so once built, the runtime is the browser (assuming the output is HTML) and any server (API) that you integrate with.
|
||||
|
||||
@@ -25,7 +25,7 @@ But when developing and building your site, the runtime is the `hugo` executable
|
||||
* User-defined components have read-only access to the filesystem.
|
||||
* We shell out to some external binaries to support [Asciidoctor](/content-management/formats/#list-of-content-formats) and similar, but those binaries and their flags are predefined and disabled by default (see [Security Policy](#security-policy)). General functions to run arbitrary external OS commands have been [discussed](https://github.com/gohugoio/hugo/issues/796), but not implemented because of security concerns.
|
||||
|
||||
## Security Policy
|
||||
## Security policy
|
||||
|
||||
Hugo has a built-in security policy that restricts access to [os/exec](https://pkg.go.dev/os/exec), remote communication and similar.
|
||||
|
||||
@@ -33,19 +33,19 @@ The default configuration is listed below. Any build using features not in the a
|
||||
|
||||
{{< code-toggle config="security" />}}
|
||||
|
||||
Note that these and other config settings in Hugo can be overridden by the OS environment. If you want to block all remote HTTP fetching of data:
|
||||
Note that these and other configuration settings in Hugo can be overridden by the OS environment. If you want to block all remote HTTP fetching of data:
|
||||
|
||||
```txt
|
||||
HUGO_SECURITY_HTTP_URLS=none hugo
|
||||
```
|
||||
|
||||
## Dependency Security
|
||||
## Dependency security
|
||||
|
||||
Hugo is built as a static binary using [Go Modules](https://github.com/golang/go/wiki/Modules) to manage its dependencies. Go Modules have several safeguards, one of them being the `go.sum` file. This is a database of the expected cryptographic checksums of all of your dependencies, including transitive dependencies.
|
||||
|
||||
[Hugo Modules](/hugo-modules/) is a feature built on top of the functionality of Go Modules. Like Go Modules, a Hugo project using Hugo Modules will have a `go.sum` file. We recommend that you commit this file to your version control system. The Hugo build will fail if there is a checksum mismatch, which would be an indication of [dependency tampering](https://julienrenaux.fr/2019/12/20/github-actions-security-risk/).
|
||||
|
||||
## Web Application Security
|
||||
## Web application security
|
||||
|
||||
These are the security threats as defined by [OWASP](https://en.wikipedia.org/wiki/OWASP).
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ layout: single
|
||||
menu:
|
||||
docs:
|
||||
parent: about
|
||||
weight: 10
|
||||
weight: 10
|
||||
weight: 20
|
||||
weight: 20
|
||||
aliases: [/overview/introduction/,/about/why-i-built-hugo/]
|
||||
toc: true
|
||||
---
|
||||
@@ -17,15 +17,15 @@ Websites built with Hugo are extremely fast and secure. Hugo sites can be hosted
|
||||
|
||||
We think of Hugo as the ideal website creation tool with nearly instant build times, able to rebuild whenever a change is made.
|
||||
|
||||
## How Fast is Hugo?
|
||||
## How fast is Hugo?
|
||||
|
||||
{{< youtube "CdiDYZ51a2o" >}}
|
||||
|
||||
## What Does Hugo Do?
|
||||
## What does Hugo do?
|
||||
|
||||
In technical terms, Hugo takes a source directory of files and templates and uses these as input to create a complete website.
|
||||
|
||||
## Who Should Use Hugo?
|
||||
## Who should use Hugo?
|
||||
|
||||
Hugo is for people that prefer writing in a text editor over a browser.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ hugo [flags]
|
||||
-D, --buildDrafts include content marked as draft
|
||||
-E, --buildExpired include expired content
|
||||
-F, --buildFuture include content with publishdate in the future
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
|
||||
--config string config file (default is hugo.yaml|json|toml)
|
||||
|
||||
@@ -19,7 +19,7 @@ hugo config [command] [flags]
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--format string preferred file format (toml, yaml or json) (default "toml")
|
||||
-h, --help help for config
|
||||
|
||||
@@ -15,7 +15,7 @@ hugo config mounts [flags] [args]
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for mounts
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
|
||||
@@ -13,10 +13,6 @@ Convert your content (e.g. front matter) to different formats.
|
||||
|
||||
See convert's subcommands toJSON, toTOML and toYAML for more information.
|
||||
|
||||
```
|
||||
hugo convert [command] [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
|
||||
@@ -7,10 +7,6 @@ url: /commands/hugo_gen/
|
||||
|
||||
A collection of several useful generators.
|
||||
|
||||
```
|
||||
hugo gen [command] [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
|
||||
@@ -13,10 +13,6 @@ Import your site from other web site generators like Jekyll.
|
||||
|
||||
Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_path`.
|
||||
|
||||
```
|
||||
hugo import [command] [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
|
||||
@@ -13,10 +13,6 @@ Listing out various types of content.
|
||||
|
||||
List requires a subcommand, e.g. hugo list drafts
|
||||
|
||||
```
|
||||
hugo list [command] [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user