mirror of
https://github.com/gohugoio/hugo.git
synced 2024-05-11 05:54:58 +00: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,9 +16,12 @@ package hugolib
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gohugoio/hugo/resource"
|
||||
|
||||
"github.com/gohugoio/hugo/media"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
@@ -34,6 +37,10 @@ type PageOutput struct {
|
||||
paginator *Pager
|
||||
paginatorInit sync.Once
|
||||
|
||||
// Page output specific resources
|
||||
resources resource.Resources
|
||||
resourcesInit sync.Once
|
||||
|
||||
// Keep this to create URL/path variations, i.e. paginators.
|
||||
targetPathDescriptor targetPathDescriptor
|
||||
|
||||
@@ -51,10 +58,7 @@ func (p *PageOutput) targetPath(addends ...string) (string, error) {
|
||||
func newPageOutput(p *Page, createCopy bool, f output.Format) (*PageOutput, error) {
|
||||
// TODO(bep) This is only needed for tests and we should get rid of it.
|
||||
if p.targetPathDescriptorPrototype == nil {
|
||||
if err := p.initTargetPathDescriptor(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p.initURLs(); err != nil {
|
||||
if err := p.initPaths(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -241,6 +245,68 @@ func (p *PageOutput) AlternativeOutputFormats() (OutputFormats, error) {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// deleteResource removes the resource from this PageOutput and the Page. They will
|
||||
// always be of the same length, but may contain different elements.
|
||||
func (p *PageOutput) deleteResource(i int) {
|
||||
p.resources = append(p.resources[:i], p.resources[i+1:]...)
|
||||
p.Page.Resources = append(p.Page.Resources[:i], p.Page.Resources[i+1:]...)
|
||||
|
||||
}
|
||||
|
||||
func (p *PageOutput) Resources() resource.Resources {
|
||||
p.resourcesInit.Do(func() {
|
||||
// If the current out shares the same path as the main page output, we reuse
|
||||
// the resource set. For the "amp" use case, we need to clone them with new
|
||||
// base folder.
|
||||
ff := p.outputFormats[0]
|
||||
if p.outputFormat.Path == ff.Path {
|
||||
p.resources = p.Page.Resources
|
||||
return
|
||||
}
|
||||
|
||||
// Clone it with new base.
|
||||
resources := make(resource.Resources, len(p.Page.Resources))
|
||||
|
||||
for i, r := range p.Page.Resources {
|
||||
if c, ok := r.(resource.Cloner); ok {
|
||||
// Clone the same resource with a new target.
|
||||
resources[i] = c.WithNewBase(p.outputFormat.Path)
|
||||
} else {
|
||||
resources[i] = r
|
||||
}
|
||||
}
|
||||
|
||||
p.resources = resources
|
||||
})
|
||||
|
||||
return p.resources
|
||||
}
|
||||
|
||||
func (p *PageOutput) renderResources() error {
|
||||
|
||||
for i, r := range p.Resources() {
|
||||
src, ok := r.(resource.Source)
|
||||
if !ok {
|
||||
// Pages gets rendered with the owning page.
|
||||
continue
|
||||
}
|
||||
|
||||
if err := src.Publish(); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// The resource has been deleted from the file system.
|
||||
// This should be extremely rare, but can happen on live reload in server
|
||||
// mode when the same resource is member of different page bundles.
|
||||
p.deleteResource(i)
|
||||
} else {
|
||||
p.s.Log.ERROR.Printf("Failed to publish %q for page %q: %s", src.AbsSourceFilename(), p.pathOrTitle(), err)
|
||||
}
|
||||
} else {
|
||||
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AlternativeOutputFormats is only available on the top level rendering
|
||||
// entry point, and not inside range loops on the Page collections.
|
||||
// This method is just here to inform users of that restriction.
|
||||
|
Reference in New Issue
Block a user