diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2020-03-03 14:25:03 +0300 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2020-04-09 23:57:26 +0300 |
commit | 095bf64c99f57efe083540a50e658808a0a1c32b (patch) | |
tree | 18d3ffb1701250ca5ef3caa2073214b6b9aff6f1 /hugolib | |
parent | 7791a804e2179667617b3b145b0fe7eba17627a1 (diff) |
Collect HTML elements during the build to use in PurgeCSS etc.
The main use case for this is to use with resources.PostProcess and resources.PostCSS with purgecss.
You would normally set it up to extract keywords from your templates, doing it from the full /public takes forever for bigger sites.
Doing the template thing misses dynamically created class names etc., and it's hard/impossible to set up in when using themes.
You can enable this in your site config:
```toml
[build]
writeStats = true
```
It will then write a `hugo_stats.json` file to the project root as part of the build.
If you're only using this for the production build, you should consider putting it below `config/production`.
You can then set it up with PostCSS like this:
```js
const purgecss = require('@fullhuman/postcss-purgecss')({
content: [ './hugo_stats.json' ],
defaultExtractor: (content) => {
let els = JSON.parse(content).htmlElements;
return els.tags.concat(els.classes, els.ids);
}
});
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
...(process.env.HUGO_ENVIRONMENT === 'production' ? [ purgecss ] : [])
]
};
```
Fixes #6999
Diffstat (limited to 'hugolib')
-rw-r--r-- | hugolib/hugo_sites.go | 6 | ||||
-rw-r--r-- | hugolib/hugo_sites_build.go | 62 | ||||
-rw-r--r-- | hugolib/site_test.go | 44 |
3 files changed, 108 insertions, 4 deletions
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index dca9e4968..9ff4d36cd 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -408,7 +408,11 @@ func applyDeps(cfg deps.DepsCfg, sites ...*Site) error { s.Deps = d // Set up the main publishing chain. - pub, err := publisher.NewDestinationPublisher(d.PathSpec.BaseFs.PublishFs, s.outputFormatsConfig, s.mediaTypesConfig, cfg.Cfg) + pub, err := publisher.NewDestinationPublisher( + d.ResourceSpec, + s.outputFormatsConfig, + s.mediaTypesConfig, + ) if err != nil { return err diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 6a65605fc..fac20e883 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -16,11 +16,17 @@ package hugolib import ( "bytes" "context" + "encoding/json" "fmt" "os" + "path/filepath" "runtime/trace" "strings" + "github.com/gohugoio/hugo/publisher" + + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/common/para" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/resources/postpub" @@ -146,10 +152,10 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { if err != nil { h.SendError(err) } - } - if err := h.postProcess(); err != nil { - h.SendError(err) + if err = h.postProcess(); err != nil { + h.SendError(err) + } } if h.Metrics != nil { @@ -337,6 +343,12 @@ func (h *HugoSites) render(config *BuildCfg) error { } func (h *HugoSites) postProcess() error { + // Make sure to write any build stats to disk first so it's available + // to the post processors. + if err := h.writeBuildStats(); err != nil { + return err + } + var toPostProcess []resource.OriginProvider for _, s := range h.Sites { for _, v := range s.ResourceSpec.PostProcessResources { @@ -422,3 +434,47 @@ func (h *HugoSites) postProcess() error { return g.Wait() } + +type publishStats struct { + CSSClasses string `json:"cssClasses"` +} + +func (h *HugoSites) writeBuildStats() error { + if !h.ResourceSpec.BuildConfig.WriteStats { + return nil + } + + htmlElements := &publisher.HTMLElements{} + for _, s := range h.Sites { + stats := s.publisher.PublishStats() + htmlElements.Merge(stats.HTMLElements) + } + + htmlElements.Sort() + + stats := publisher.PublishStats{ + HTMLElements: *htmlElements, + } + + js, err := json.MarshalIndent(stats, "", " ") + if err != nil { + return err + } + + filename := filepath.Join(h.WorkingDir, "hugo_stats.json") + + // Make sure it's always written to the OS fs. + if err := afero.WriteFile(hugofs.Os, filename, js, 0666); err != nil { + return err + } + + // Write to the destination, too, if a mem fs is in play. + if h.Fs.Source != hugofs.Os { + if err := afero.WriteFile(h.Fs.Destination, filename, js, 0666); err != nil { + return err + } + } + + return nil + +} diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 0b05aac12..e404d80a4 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -980,3 +980,47 @@ func TestRefIssues(t *testing.T) { b.AssertFileContent("public/post/nested-a/content-a/index.html", `Content: http://example.com/post/nested-b/content-b/`) } + +func TestClassCollector(t *testing.T) { + b := newTestSitesBuilder(t) + b.WithConfigFile("toml", ` + +[build] + writeStats = true + +`) + + b.WithTemplates("index.html", ` + +<div id="el1" class="a b c">Foo</div> + +Some text. + +<div class="c d e" id="el2">Foo</div> +`) + + b.WithContent("p1.md", "") + + b.Build(BuildCfg{}) + + b.AssertFileContent("hugo_stats.json", ` +{ + "htmlElements": { + "tags": [ + "div" + ], + "classes": [ + "a", + "b", + "c", + "d", + "e" + ], + "ids": [ + "el1", + "el2" + ] + } + } +`) +} |