mirror of
https://github.com/gohugoio/hugo.git
synced 2024-12-01 15:49:11 -05:00
095bf64c99
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
154 lines
3.3 KiB
Go
154 lines
3.3 KiB
Go
// Copyright 2019 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 config
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/gohugoio/hugo/common/types"
|
|
|
|
"github.com/gobwas/glob"
|
|
"github.com/gohugoio/hugo/common/herrors"
|
|
"github.com/mitchellh/mapstructure"
|
|
"github.com/spf13/cast"
|
|
jww "github.com/spf13/jwalterweatherman"
|
|
)
|
|
|
|
var DefaultBuild = Build{
|
|
UseResourceCacheWhen: "fallback",
|
|
WriteStats: false,
|
|
}
|
|
|
|
// Build holds some build related condfiguration.
|
|
type Build struct {
|
|
UseResourceCacheWhen string // never, fallback, always. Default is fallback
|
|
|
|
// When enabled, will collect and write a hugo_stats.json with some build
|
|
// related aggregated data (e.g. CSS class names).
|
|
WriteStats bool
|
|
}
|
|
|
|
func (b Build) UseResourceCache(err error) bool {
|
|
if b.UseResourceCacheWhen == "never" {
|
|
return false
|
|
}
|
|
|
|
if b.UseResourceCacheWhen == "fallback" {
|
|
return err == herrors.ErrFeatureNotAvailable
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func DecodeBuild(cfg Provider) Build {
|
|
m := cfg.GetStringMap("build")
|
|
b := DefaultBuild
|
|
if m == nil {
|
|
return b
|
|
}
|
|
|
|
err := mapstructure.WeakDecode(m, &b)
|
|
if err != nil {
|
|
return DefaultBuild
|
|
}
|
|
|
|
b.UseResourceCacheWhen = strings.ToLower(b.UseResourceCacheWhen)
|
|
when := b.UseResourceCacheWhen
|
|
if when != "never" && when != "always" && when != "fallback" {
|
|
b.UseResourceCacheWhen = "fallback"
|
|
}
|
|
|
|
return b
|
|
}
|
|
|
|
// Sitemap configures the sitemap to be generated.
|
|
type Sitemap struct {
|
|
ChangeFreq string
|
|
Priority float64
|
|
Filename string
|
|
}
|
|
|
|
func DecodeSitemap(prototype Sitemap, input map[string]interface{}) Sitemap {
|
|
|
|
for key, value := range input {
|
|
switch key {
|
|
case "changefreq":
|
|
prototype.ChangeFreq = cast.ToString(value)
|
|
case "priority":
|
|
prototype.Priority = cast.ToFloat64(value)
|
|
case "filename":
|
|
prototype.Filename = cast.ToString(value)
|
|
default:
|
|
jww.WARN.Printf("Unknown Sitemap field: %s\n", key)
|
|
}
|
|
}
|
|
|
|
return prototype
|
|
}
|
|
|
|
// Config for the dev server.
|
|
type Server struct {
|
|
Headers []Headers
|
|
|
|
compiledInit sync.Once
|
|
compiled []glob.Glob
|
|
}
|
|
|
|
func (s *Server) Match(pattern string) []types.KeyValueStr {
|
|
s.compiledInit.Do(func() {
|
|
for _, h := range s.Headers {
|
|
s.compiled = append(s.compiled, glob.MustCompile(h.For))
|
|
}
|
|
})
|
|
|
|
if s.compiled == nil {
|
|
return nil
|
|
}
|
|
|
|
var matches []types.KeyValueStr
|
|
|
|
for i, g := range s.compiled {
|
|
if g.Match(pattern) {
|
|
h := s.Headers[i]
|
|
for k, v := range h.Values {
|
|
matches = append(matches, types.KeyValueStr{Key: k, Value: cast.ToString(v)})
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.Slice(matches, func(i, j int) bool {
|
|
return matches[i].Key < matches[j].Key
|
|
})
|
|
|
|
return matches
|
|
|
|
}
|
|
|
|
type Headers struct {
|
|
For string
|
|
Values map[string]interface{}
|
|
}
|
|
|
|
func DecodeServer(cfg Provider) *Server {
|
|
m := cfg.GetStringMap("server")
|
|
s := &Server{}
|
|
if m == nil {
|
|
return s
|
|
}
|
|
|
|
_ = mapstructure.WeakDecode(m, s)
|
|
return s
|
|
}
|