mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-17 21:01:26 +02:00
✨ Implement Page bundling and image handling
This commit is not the smallest in Hugo's history. Some hightlights include: * Page bundles (for complete articles, keeping images and content together etc.). * Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`. * Processed images are cached inside `resources/_gen/images` (default) in your project. * Symbolic links (both files and dirs) are now allowed anywhere inside /content * A new table based build summary * The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below). A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory: ```bash ▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render" benchmark old ns/op new ns/op delta BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30% BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86% benchmark old allocs new allocs delta BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47% BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30% benchmark old bytes new bytes delta BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12% BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35% BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64% ``` Fixes #3651 Closes #3158 Fixes #1014 Closes #2021 Fixes #1240 Updates #3757
This commit is contained in:
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/source"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -26,6 +25,7 @@ type testSiteConfig struct {
|
||||
DefaultContentLanguage string
|
||||
DefaultContentLanguageInSubdir bool
|
||||
Fs afero.Fs
|
||||
Running bool
|
||||
}
|
||||
|
||||
func TestMultiSitesMainLangInRoot(t *testing.T) {
|
||||
@@ -226,7 +226,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
|
||||
gp1 := sites.GetContentPage(filepath.FromSlash("content/sect/doc1.en.md"))
|
||||
require.NotNil(t, gp1)
|
||||
require.Equal(t, "doc1", gp1.Title)
|
||||
gp2 := sites.GetContentPage(filepath.FromSlash("content/sect/notfound.md"))
|
||||
gp2 := sites.GetContentPage(filepath.FromSlash("content/dummysect/notfound.md"))
|
||||
require.Nil(t, gp2)
|
||||
|
||||
enSite := sites.Sites[0]
|
||||
@@ -238,7 +238,6 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
|
||||
if len(enSite.RegularPages) != 4 {
|
||||
t.Fatal("Expected 4 english pages")
|
||||
}
|
||||
require.Len(t, enSite.Source.Files(), 14, "should have 13 source files")
|
||||
require.Len(t, enSite.AllPages, 28, "should have 28 total pages (including translations and index types)")
|
||||
|
||||
doc1en := enSite.RegularPages[0]
|
||||
@@ -401,12 +400,11 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
if !isCI() {
|
||||
defer leaktest.CheckTimeout(t, 30*time.Second)()
|
||||
}
|
||||
siteConfig := testSiteConfig{Fs: afero.NewMemMapFs(), DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: true}
|
||||
siteConfig := testSiteConfig{Running: true, Fs: afero.NewMemMapFs(), DefaultContentLanguage: "fr", DefaultContentLanguageInSubdir: true}
|
||||
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
|
||||
fs := sites.Fs
|
||||
cfg := BuildCfg{Watching: true}
|
||||
th := testHelper{sites.Cfg, fs, t}
|
||||
|
||||
cfg := BuildCfg{}
|
||||
err := sites.Build(cfg)
|
||||
|
||||
if err != nil {
|
||||
@@ -446,8 +444,10 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
// * Change a template
|
||||
// * Change language file
|
||||
{
|
||||
nil,
|
||||
[]fsnotify.Event{{Name: "content/sect/doc2.en.md", Op: fsnotify.Remove}},
|
||||
func(t *testing.T) {
|
||||
fs.Source.Remove("content/sect/doc2.en.md")
|
||||
},
|
||||
[]fsnotify.Event{{Name: filepath.FromSlash("content/sect/doc2.en.md"), Op: fsnotify.Remove}},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 3, "1 en removed")
|
||||
|
||||
@@ -467,9 +467,9 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
writeNewContentFile(t, fs, "new_fr_1", "2016-07-30", "content/new1.fr.md", 10)
|
||||
},
|
||||
[]fsnotify.Event{
|
||||
{Name: "content/new1.en.md", Op: fsnotify.Create},
|
||||
{Name: "content/new2.en.md", Op: fsnotify.Create},
|
||||
{Name: "content/new1.fr.md", Op: fsnotify.Create},
|
||||
{Name: filepath.FromSlash("content/new1.en.md"), Op: fsnotify.Create},
|
||||
{Name: filepath.FromSlash("content/new2.en.md"), Op: fsnotify.Create},
|
||||
{Name: filepath.FromSlash("content/new1.fr.md"), Op: fsnotify.Create},
|
||||
},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 5)
|
||||
@@ -490,7 +490,7 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
doc1 += "CHANGED"
|
||||
writeSource(t, fs, p, doc1)
|
||||
},
|
||||
[]fsnotify.Event{{Name: "content/sect/doc1.en.md", Op: fsnotify.Write}},
|
||||
[]fsnotify.Event{{Name: filepath.FromSlash("content/sect/doc1.en.md"), Op: fsnotify.Write}},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 5)
|
||||
doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
|
||||
@@ -506,8 +506,8 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
}
|
||||
},
|
||||
[]fsnotify.Event{
|
||||
{Name: "content/new1renamed.en.md", Op: fsnotify.Rename},
|
||||
{Name: "content/new1.en.md", Op: fsnotify.Rename},
|
||||
{Name: filepath.FromSlash("content/new1renamed.en.md"), Op: fsnotify.Rename},
|
||||
{Name: filepath.FromSlash("content/new1.en.md"), Op: fsnotify.Rename},
|
||||
},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 5, "Rename")
|
||||
@@ -523,7 +523,7 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
templateContent += "{{ print \"Template Changed\"}}"
|
||||
writeSource(t, fs, template, templateContent)
|
||||
},
|
||||
[]fsnotify.Event{{Name: "layouts/_default/single.html", Op: fsnotify.Write}},
|
||||
[]fsnotify.Event{{Name: filepath.FromSlash("layouts/_default/single.html"), Op: fsnotify.Write}},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 5)
|
||||
require.Len(t, enSite.AllPages, 30)
|
||||
@@ -540,7 +540,7 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
langContent = strings.Replace(langContent, "Bonjour", "Salut", 1)
|
||||
writeSource(t, fs, languageFile, langContent)
|
||||
},
|
||||
[]fsnotify.Event{{Name: "i18n/fr.yaml", Op: fsnotify.Write}},
|
||||
[]fsnotify.Event{{Name: filepath.FromSlash("i18n/fr.yaml"), Op: fsnotify.Write}},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 5)
|
||||
require.Len(t, enSite.AllPages, 30)
|
||||
@@ -563,7 +563,7 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||
writeSource(t, fs, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}")
|
||||
},
|
||||
[]fsnotify.Event{
|
||||
{Name: "layouts/shortcodes/shortcode.html", Op: fsnotify.Write},
|
||||
{Name: filepath.FromSlash("layouts/shortcodes/shortcode.html"), Op: fsnotify.Write},
|
||||
},
|
||||
func(t *testing.T) {
|
||||
require.Len(t, enSite.RegularPages, 5)
|
||||
@@ -1097,16 +1097,16 @@ hello:
|
||||
}
|
||||
|
||||
// Sources
|
||||
sources := []source.ByteSource{
|
||||
{Name: filepath.FromSlash("root.en.md"), Content: []byte(`---
|
||||
sources := [][2]string{
|
||||
{filepath.FromSlash("root.en.md"), `---
|
||||
title: root
|
||||
weight: 10000
|
||||
slug: root
|
||||
publishdate: "2000-01-01"
|
||||
---
|
||||
# root
|
||||
`)},
|
||||
{Name: filepath.FromSlash("sect/doc1.en.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("sect/doc1.en.md"), `---
|
||||
title: doc1
|
||||
weight: 1
|
||||
slug: doc1-slug
|
||||
@@ -1122,8 +1122,8 @@ publishdate: "2000-01-01"
|
||||
{{< lingo >}}
|
||||
|
||||
NOTE: slug should be used as URL
|
||||
`)},
|
||||
{Name: filepath.FromSlash("sect/doc1.fr.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("sect/doc1.fr.md"), `---
|
||||
title: doc1
|
||||
weight: 1
|
||||
plaques:
|
||||
@@ -1140,8 +1140,8 @@ publishdate: "2000-01-04"
|
||||
|
||||
NOTE: should be in the 'en' Page's 'Translations' field.
|
||||
NOTE: date is after "doc3"
|
||||
`)},
|
||||
{Name: filepath.FromSlash("sect/doc2.en.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("sect/doc2.en.md"), `---
|
||||
title: doc2
|
||||
weight: 2
|
||||
publishdate: "2000-01-02"
|
||||
@@ -1149,8 +1149,8 @@ publishdate: "2000-01-02"
|
||||
# doc2
|
||||
*some content*
|
||||
NOTE: without slug, "doc2" should be used, without ".en" as URL
|
||||
`)},
|
||||
{Name: filepath.FromSlash("sect/doc3.en.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("sect/doc3.en.md"), `---
|
||||
title: doc3
|
||||
weight: 3
|
||||
publishdate: "2000-01-03"
|
||||
@@ -1163,8 +1163,8 @@ url: /superbob
|
||||
# doc3
|
||||
*some content*
|
||||
NOTE: third 'en' doc, should trigger pagination on home page.
|
||||
`)},
|
||||
{Name: filepath.FromSlash("sect/doc4.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("sect/doc4.md"), `---
|
||||
title: doc4
|
||||
weight: 4
|
||||
plaques:
|
||||
@@ -1175,8 +1175,8 @@ publishdate: "2000-01-05"
|
||||
*du contenu francophone*
|
||||
NOTE: should use the defaultContentLanguage and mark this doc as 'fr'.
|
||||
NOTE: doesn't have any corresponding translation in 'en'
|
||||
`)},
|
||||
{Name: filepath.FromSlash("other/doc5.fr.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("other/doc5.fr.md"), `---
|
||||
title: doc5
|
||||
weight: 5
|
||||
publishdate: "2000-01-06"
|
||||
@@ -1184,45 +1184,45 @@ publishdate: "2000-01-06"
|
||||
# doc5
|
||||
*autre contenu francophone*
|
||||
NOTE: should use the "permalinks" configuration with :filename
|
||||
`)},
|
||||
`},
|
||||
// Add some for the stats
|
||||
{Name: filepath.FromSlash("stats/expired.fr.md"), Content: []byte(`---
|
||||
{filepath.FromSlash("stats/expired.fr.md"), `---
|
||||
title: expired
|
||||
publishdate: "2000-01-06"
|
||||
expiryDate: "2001-01-06"
|
||||
---
|
||||
# Expired
|
||||
`)},
|
||||
{Name: filepath.FromSlash("stats/future.fr.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("stats/future.fr.md"), `---
|
||||
title: future
|
||||
weight: 6
|
||||
publishdate: "2100-01-06"
|
||||
---
|
||||
# Future
|
||||
`)},
|
||||
{Name: filepath.FromSlash("stats/expired.en.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("stats/expired.en.md"), `---
|
||||
title: expired
|
||||
weight: 7
|
||||
publishdate: "2000-01-06"
|
||||
expiryDate: "2001-01-06"
|
||||
---
|
||||
# Expired
|
||||
`)},
|
||||
{Name: filepath.FromSlash("stats/future.en.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("stats/future.en.md"), `---
|
||||
title: future
|
||||
weight: 6
|
||||
publishdate: "2100-01-06"
|
||||
---
|
||||
# Future
|
||||
`)},
|
||||
{Name: filepath.FromSlash("stats/draft.en.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("stats/draft.en.md"), `---
|
||||
title: expired
|
||||
publishdate: "2000-01-06"
|
||||
draft: true
|
||||
---
|
||||
# Draft
|
||||
`)},
|
||||
{Name: filepath.FromSlash("stats/tax.nn.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("stats/tax.nn.md"), `---
|
||||
title: Tax NN
|
||||
weight: 8
|
||||
publishdate: "2000-01-06"
|
||||
@@ -1231,8 +1231,8 @@ lag:
|
||||
- Sogndal
|
||||
---
|
||||
# Tax NN
|
||||
`)},
|
||||
{Name: filepath.FromSlash("stats/tax.nb.md"), Content: []byte(`---
|
||||
`},
|
||||
{filepath.FromSlash("stats/tax.nb.md"), `---
|
||||
title: Tax NB
|
||||
weight: 8
|
||||
publishdate: "2000-01-06"
|
||||
@@ -1241,7 +1241,7 @@ lag:
|
||||
- Sogndal
|
||||
---
|
||||
# Tax NB
|
||||
`)},
|
||||
`},
|
||||
}
|
||||
|
||||
configFile := "multilangconfig." + configSuffix
|
||||
@@ -1252,10 +1252,8 @@ lag:
|
||||
|
||||
fs := hugofs.NewFrom(mf, cfg)
|
||||
|
||||
// Hugo support using ByteSource's directly (for testing),
|
||||
// but to make it more real, we write them to the mem file system.
|
||||
for _, s := range sources {
|
||||
if err := afero.WriteFile(mf, filepath.Join("content", s.Name), s.Content, 0755); err != nil {
|
||||
if err := afero.WriteFile(mf, filepath.Join("content", s[0]), []byte(s[1]), 0755); err != nil {
|
||||
t.Fatalf("Failed to write file: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -1263,7 +1261,7 @@ lag:
|
||||
// Add some data
|
||||
writeSource(t, fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
|
||||
|
||||
sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) //, Logger: newDebugLogger()})
|
||||
sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg, Running: siteConfig.Running}) //, Logger: newDebugLogger()})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create sites: %s", err)
|
||||
@@ -1311,7 +1309,7 @@ func readFileFromFs(t testing.TB, fs afero.Fs, filename string) string {
|
||||
b, err := afero.ReadFile(fs, filename)
|
||||
if err != nil {
|
||||
// Print some debug info
|
||||
root := strings.Split(filename, helpers.FilePathSeparator)[0]
|
||||
root := "/" //strings.Split(filename, helpers.FilePathSeparator)[0]
|
||||
afero.Walk(fs, root, func(path string, info os.FileInfo, err error) error {
|
||||
if info != nil && !info.IsDir() {
|
||||
fmt.Println(" ", path)
|
||||
|
Reference in New Issue
Block a user