summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Mooring <joe.mooring@veriphor.com>2023-08-16 15:07:01 -0700
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-08-21 10:38:22 +0200
commitb6538532f45bf833226da7277994c1a96d26a56c (patch)
tree554e35423b4cee3ca6f1d526097bbc8677acb7b2
parent90944aa261acf596af774dcea0db1fc31dccc973 (diff)
commands/new: Embed site and theme skeletons
The skeletons are used when creating new sites and themes with the CLI. Closes #11358
-rw-r--r--commands/helpers.go11
-rw-r--r--commands/new.go205
-rw-r--r--create/content.go2
-rw-r--r--create/skeletons/site/archetypes/default.md5
-rw-r--r--create/skeletons/site/assets/.gitkeep0
-rw-r--r--create/skeletons/site/content/.gitkeep0
-rw-r--r--create/skeletons/site/data/.gitkeep0
-rw-r--r--create/skeletons/site/i18n/.gitkeep0
-rw-r--r--create/skeletons/site/layouts/.gitkeep0
-rw-r--r--create/skeletons/site/static/.gitkeep0
-rw-r--r--create/skeletons/site/themes/.gitkeep0
-rw-r--r--create/skeletons/skeletons.go111
-rw-r--r--create/skeletons/theme/LICENSE21
-rw-r--r--create/skeletons/theme/README.md7
-rw-r--r--create/skeletons/theme/archetypes/default.md5
-rw-r--r--create/skeletons/theme/assets/css/main.css22
-rw-r--r--create/skeletons/theme/assets/js/main.js1
-rw-r--r--create/skeletons/theme/config/_default/hugo.toml4
-rw-r--r--create/skeletons/theme/config/_default/menus.toml14
-rw-r--r--create/skeletons/theme/content/_index.md9
-rw-r--r--create/skeletons/theme/content/posts/_index.md7
-rw-r--r--create/skeletons/theme/content/posts/post-1.md10
-rw-r--r--create/skeletons/theme/content/posts/post-2.md10
-rw-r--r--create/skeletons/theme/content/posts/post-3/bryce-canyon.jpgbin0 -> 19224 bytes
-rw-r--r--create/skeletons/theme/content/posts/post-3/index.md12
-rw-r--r--create/skeletons/theme/data/.gitkeep0
-rw-r--r--create/skeletons/theme/i18n/.gitkeep0
-rw-r--r--create/skeletons/theme/layouts/_default/baseof.html17
-rw-r--r--create/skeletons/theme/layouts/_default/home.html7
-rw-r--r--create/skeletons/theme/layouts/_default/list.html8
-rw-r--r--create/skeletons/theme/layouts/_default/single.html10
-rw-r--r--create/skeletons/theme/layouts/partials/footer.html1
-rw-r--r--create/skeletons/theme/layouts/partials/head.html5
-rw-r--r--create/skeletons/theme/layouts/partials/head/css.html9
-rw-r--r--create/skeletons/theme/layouts/partials/head/js.html12
-rw-r--r--create/skeletons/theme/layouts/partials/header.html2
-rw-r--r--create/skeletons/theme/layouts/partials/menu.html45
-rw-r--r--create/skeletons/theme/layouts/partials/terms.html23
-rw-r--r--create/skeletons/theme/static/favicon.icobin0 -> 15406 bytes
-rw-r--r--create/skeletons/theme/theme.toml31
-rw-r--r--testscripts/commands/new.txt42
41 files changed, 465 insertions, 203 deletions
diff --git a/commands/helpers.go b/commands/helpers.go
index d6c5e43ac..3b0c50159 100644
--- a/commands/helpers.go
+++ b/commands/helpers.go
@@ -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"
)
@@ -123,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)
- }
-}
diff --git a/commands/new.go b/commands/new.go
index 42644d7f6..3f43a687f 100644
--- a/commands/new.go
+++ b/commands/new.go
@@ -16,19 +16,13 @@ package commands
import (
"bytes"
"context"
- "errors"
- "fmt"
"path/filepath"
"strings"
"github.com/bep/simplecobra"
- "github.com/gohugoio/hugo/common/htime"
"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 +93,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"),
- }
-
- 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)
- }
+ err = skeletons.CreateSite(createpath, sourceFs, force, format)
+ if err != nil {
+ return 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
},
@@ -173,83 +128,13 @@ according to your needs.`,
sourceFs := ps.Fs.Source
themesDir := h.Configs.LoadingInfo.BaseConfig.ThemesDir
createpath := ps.AbsPathify(filepath.Join(themesDir, args[0]))
- r.Println("Creating theme at", 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)
- 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.
+ r.Println("Creating new theme in", createpath)
-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)
+ err = skeletons.CreateTheme(createpath, sourceFs)
if err != nil {
return err
}
- c.createThemeMD(ps.Fs.Source, createpath)
-
return nil
},
},
@@ -299,77 +184,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.116.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
-}
diff --git a/create/content.go b/create/content.go
index 55159c24c..10442c396 100644
--- a/create/content.go
+++ b/create/content.go
@@ -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
---
diff --git a/create/skeletons/site/archetypes/default.md b/create/skeletons/site/archetypes/default.md
new file mode 100644
index 000000000..c6f3fcef6
--- /dev/null
+++ b/create/skeletons/site/archetypes/default.md
@@ -0,0 +1,5 @@
++++
+title = '{{ replace .File.ContentBaseName "-" " " | title }}'
+date = {{ .Date }}
+draft = true
++++
diff --git a/create/skeletons/site/assets/.gitkeep b/create/skeletons/site/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/assets/.gitkeep
diff --git a/create/skeletons/site/content/.gitkeep b/create/skeletons/site/content/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/content/.gitkeep
diff --git a/create/skeletons/site/data/.gitkeep b/create/skeletons/site/data/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/data/.gitkeep
diff --git a/create/skeletons/site/i18n/.gitkeep b/create/skeletons/site/i18n/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/i18n/.gitkeep
diff --git a/create/skeletons/site/layouts/.gitkeep b/create/skeletons/site/layouts/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/layouts/.gitkeep
diff --git a/create/skeletons/site/static/.gitkeep b/create/skeletons/site/static/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/static/.gitkeep
diff --git a/create/skeletons/site/themes/.gitkeep b/create/skeletons/site/themes/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/create/skeletons/site/themes/.gitkeep
diff --git a/create/skeletons/skeletons.go b/create/skeletons/skeletons.go
new file mode 100644
index 000000000..7f7fb1bb7
--- /dev/null
+++ b/create/skeletons/skeletons.go
@@ -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)
+}
diff --git a/create/skeletons/theme/LICENSE b/create/skeletons/theme/LICENSE
new file mode 100644
index 000000000..8aa26455d
--- /dev/null
+++ b/create/skeletons/theme/LICENSE
@@ -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.
diff --git a/create/skeletons/theme/README.md b/create/skeletons/theme/README.md
new file mode 100644
index 000000000..7cec74e07
--- /dev/null
+++ b/create/skeletons/theme/README.md
@@ -0,0 +1,7 @@
+# Theme Name
+
+## Features
+
+## Installation
+
+## Configuration
diff --git a/create/skeletons/theme/archetypes/default.md b/create/skeletons/theme/archetypes/default.md
new file mode 100644
index 000000000..c6f3fcef6
--- /dev/null
+++ b/create/skeletons/theme/archetypes/default.md
@@ -0,0 +1,5 @@
++++
+title = '{{ replace .File.ContentBaseName "-" " " | title }}'
+date = {{ .Date }}
+draft = true
++++
diff --git a/create/skeletons/theme/assets/css/main.css b/create/skeletons/theme/assets/css/main.css
new file mode 100644
index 000000000..166ade924
--- /dev/null
+++ b/create/skeletons/theme/assets/css/main.css
@@ -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;
+}
diff --git a/create/skeletons/theme/assets/js/main.js b/create/skeletons/theme/assets/js/main.js
new file mode 100644
index 000000000..e2aac5275
--- /dev/null
+++ b/create/skeletons/theme/assets/js/main.js
@@ -0,0 +1 @@
+console.log('This site was generated by Hugo.');
diff --git a/create/skeletons/theme/config/_default/hugo.toml b/create/skeletons/theme/config/_default/hugo.toml
new file mode 100644
index 000000000..123719411
--- /dev/null
+++ b/create/skeletons/theme/config/_default/hugo.toml
@@ -0,0 +1,4 @@
+[module]
+ [module.hugoVersion]
+ extended = false
+ min = "0.116.0"
diff --git a/create/skeletons/theme/config/_default/menus.toml b/create/skeletons/theme/config/_default/menus.toml
new file mode 100644
index 000000000..526645f2c
--- /dev/null
+++ b/create/skeletons/theme/config/_default/menus.toml
@@ -0,0 +1,14 @@
+[[main]]
+name = 'Home'
+pageRef = '/'
+weight = 10
+
+[[main]]
+name = 'Po