ref: 7204b354a9f46778f068a4712447d6d4fefbefd8
parent: 574c2959b8d3338764fa1db102a5e0fd6ed322d9
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Fri Mar 20 12:34:53 EDT 2020
Some minify configuration adjustments
--- a/commands/gendocshelper.go
+++ b/commands/gendocshelper.go
@@ -64,7 +64,7 @@
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
- if err := enc.Encode(docshelper.DocProviders); err != nil {
+ if err := enc.Encode(docshelper.GetDocProvider()); err != nil {
return err
}
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -231,11 +231,6 @@
"duplicateTargetPaths",
}
- // Will set a value even if it is the default.
- flagKeysForced := []string{
- "minify",
- }
-
for _, key := range persFlagKeys {
setValueFromFlag(cmd.PersistentFlags(), key, cfg, "", false)
}
@@ -243,9 +238,7 @@
setValueFromFlag(cmd.Flags(), key, cfg, "", false)
}
- for _, key := range flagKeysForced {
- setValueFromFlag(cmd.Flags(), key, cfg, "", true)
- }
+ setValueFromFlag(cmd.Flags(), "minify", cfg, "minifyOutput", true)
// Set some "config aliases"
setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir", false)
--- a/docs/content/en/getting-started/configuration.md
+++ b/docs/content/en/getting-started/configuration.md
@@ -193,6 +193,9 @@
menu
: See [Add Non-content Entries to a Menu](/content-management/menus/#add-non-content-entries-to-a-menu).
+minify
+: See [Configure Minify](#configure-minify)
+
module
: Module config see [Module Config](/hugo-modules/configuration/).{{< new-in "0.56.0" >}}
@@ -480,6 +483,14 @@
## Configure Additional Output Formats
Hugo v0.20 introduced the ability to render your content to multiple output formats (e.g., to JSON, AMP html, or CSV). See [Output Formats][] for information on how to add these values to your Hugo project's configuration file.
+
+## Configure Minify
+
+{{< new-in "0.68.0" >}}
+
+Default configuration:
+
+{{< code-toggle config="minify" />}}
## Configure File Caches
--- a/docs/data/docs.json
+++ b/docs/data/docs.json
@@ -1415,7 +1415,7 @@
"goldmark": {
"renderer": {
"hardWraps": false,
- "xHTML": false,
+ "xhtml": false,
"unsafe": false
},
"parser": {
@@ -1452,14 +1452,15 @@
"footnoteReturnLinkContents": ""
}
},
- "minifiers": {
+ "minify": {
+ "minifyOutput": false,
+ "disableHTML": false,
+ "disableCSS": false,
+ "disableJS": false,
+ "disableJSON": false,
+ "disableSVG": false,
+ "disableXML": false,
"tdewolff": {
- "enableHtml": true,
- "enableCss": true,
- "enableJs": true,
- "enableJson": true,
- "enableSvg": true,
- "enableXml": true,
"html": {
"keepConditionalComments": true,
"keepDefaultAttrVals": true,
--- a/docshelper/docs.go
+++ b/docshelper/docs.go
@@ -15,37 +15,37 @@
// is of limited interest for the general Hugo user.
package docshelper
-import (
- "encoding/json"
+type (
+ DocProviderFunc = func() DocProvider
+ DocProvider map[string]map[string]interface{}
)
-// DocProviders contains all DocProviders added to the system.
-var DocProviders = make(map[string]DocProvider)
+var docProviderFuncs []DocProviderFunc
-// AddDocProvider adds or updates the DocProvider for a given name.
-func AddDocProvider(name string, provider DocProvider) {
- if prev, ok := DocProviders[name]; !ok {
- DocProviders[name] = provider
- } else {
- DocProviders[name] = merge(prev, provider)
- }
+func AddDocProviderFunc(fn DocProviderFunc) {
+ docProviderFuncs = append(docProviderFuncs, fn)
}
-// DocProvider is used to save arbitrary JSON data
-// used for the generation of the documentation.
-type DocProvider func() map[string]interface{}
+func GetDocProvider() DocProvider {
+ provider := make(DocProvider)
-// MarshalJSON returns a JSON representation of the DocProvider.
-func (d DocProvider) MarshalJSON() ([]byte, error) {
- return json.MarshalIndent(d(), "", " ")
+ for _, fn := range docProviderFuncs {
+ p := fn()
+ for k, v := range p {
+ if prev, found := provider[k]; !found {
+ provider[k] = v
+ } else {
+ merge(prev, v)
+ }
+ }
+ }
+
+ return provider
}
-func merge(a, b DocProvider) DocProvider {
- next := a()
- for k, v := range b() {
- next[k] = v
- }
- return func() map[string]interface{} {
- return next
+// Shallow merge
+func merge(dst, src map[string]interface{}) {
+ for k, v := range src {
+ dst[k] = v
}
}
--- a/helpers/docshelper.go
+++ b/helpers/docshelper.go
@@ -12,8 +12,7 @@
// This is is just some helpers used to create some JSON used in the Hugo docs.
func init() {
- docsProvider := func() map[string]interface{} {
- docs := make(map[string]interface{})
+ docsProvider := func() docshelper.DocProvider {
var chromaLexers []interface{}
@@ -48,11 +47,11 @@
chromaLexers = append(chromaLexers, lexerEntry)
- docs["lexers"] = chromaLexers
}
- return docs
+ return docshelper.DocProvider{"chroma": map[string]interface{}{"lexers": chromaLexers}}
+
}
- docshelper.AddDocProvider("chroma", docsProvider)
+ docshelper.AddDocProviderFunc(docsProvider)
}
--- a/hugolib/resource_chain_test.go
+++ b/hugolib/resource_chain_test.go
@@ -947,3 +947,33 @@
build("never", true)
}
+
+func TestResourceMinifyDisabled(t *testing.T) {
+ t.Parallel()
+
+ b := newTestSitesBuilder(t).WithConfigFile("toml", `
+baseURL = "https://example.org"
+
+[minify]
+disableXML=true
+
+
+`)
+
+ b.WithContent("page.md", "")
+
+ b.WithSourceFile(
+ "assets/xml/data.xml", "<root> <foo> asdfasdf </foo> </root>",
+ )
+
+ b.WithTemplates("index.html", `
+{{ $xml := resources.Get "xml/data.xml" | minify | fingerprint }}
+XML: {{ $xml.Content | safeHTML }}|{{ $xml.RelPermalink }}
+`)
+
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/index.html", `
+XML: <root> <foo> asdfasdf </foo> </root>|/xml/data.min.3be4fddd19aaebb18c48dd6645215b822df74701957d6d36e59f203f9c30fd9f.xml
+`)
+}
--- a/markup/markup_config/config.go
+++ b/markup/markup_config/config.go
@@ -94,11 +94,8 @@
}
func init() {
- docsProvider := func() map[string]interface{} {
- docs := make(map[string]interface{})
- docs["markup"] = parser.LowerCaseCamelJSONMarshaller{Value: Default}
- return docs
-
+ docsProvider := func() docshelper.DocProvider {
+ return docshelper.DocProvider{"config": map[string]interface{}{"markup": parser.LowerCaseCamelJSONMarshaller{Value: Default}}}
}
- docshelper.AddDocProvider("config", docsProvider)
+ docshelper.AddDocProviderFunc(docsProvider)
}
--- a/media/docshelper.go
+++ b/media/docshelper.go
@@ -6,12 +6,8 @@
// This is is just some helpers used to create some JSON used in the Hugo docs.
func init() {
- docsProvider := func() map[string]interface{} {
- docs := make(map[string]interface{})
-
- docs["types"] = DefaultTypes
- return docs
+ docsProvider := func() docshelper.DocProvider {
+ return docshelper.DocProvider{"media": map[string]interface{}{"types": DefaultTypes}}
}
-
- docshelper.AddDocProvider("media", docsProvider)
+ docshelper.AddDocProviderFunc(docsProvider)
}
--- a/minifiers/config.go
+++ b/minifiers/config.go
@@ -14,6 +14,7 @@
package minifiers
import (
+ "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/docshelper"
"github.com/gohugoio/hugo/parser"
@@ -61,36 +62,43 @@
XML xml.Minifier
}
-type minifiersConfig struct {
- EnableHTML bool
- EnableCSS bool
- EnableJS bool
- EnableJSON bool
- EnableSVG bool
- EnableXML bool
+type minifyConfig struct {
+ // Whether to minify the published output (the HTML written to /public).
+ MinifyOutput bool
+ DisableHTML bool
+ DisableCSS bool
+ DisableJS bool
+ DisableJSON bool
+ DisableSVG bool
+ DisableXML bool
+
Tdewolff tdewolffConfig
}
-var defaultConfig = minifiersConfig{
- EnableHTML: true,
- EnableCSS: true,
- EnableJS: true,
- EnableJSON: true,
- EnableSVG: true,
- EnableXML: true,
-
+var defaultConfig = minifyConfig{
Tdewolff: defaultTdewolffConfig,
}
-func decodeConfig(cfg config.Provider) (conf minifiersConfig, err error) {
+func decodeConfig(cfg config.Provider) (conf minifyConfig, err error) {
conf = defaultConfig
- m := cfg.GetStringMap("minifiers")
- if m == nil {
+ // May be set by CLI.
+ conf.MinifyOutput = cfg.GetBool("minifyOutput")
+
+ v := cfg.Get("minify")
+ if v == nil {
return
}
+ // Legacy.
+ if b, ok := v.(bool); ok {
+ conf.MinifyOutput = b
+ return
+ }
+
+ m := maps.ToStringMap(v)
+
err = mapstructure.WeakDecode(m, &conf)
if err != nil {
@@ -101,11 +109,8 @@
}
func init() {
- docsProvider := func() map[string]interface{} {
- docs := make(map[string]interface{})
- docs["minifiers"] = parser.LowerCaseCamelJSONMarshaller{Value: defaultConfig}
- return docs
-
+ docsProvider := func() docshelper.DocProvider {
+ return docshelper.DocProvider{"config": map[string]interface{}{"minify": parser.LowerCaseCamelJSONMarshaller{Value: defaultConfig}}}
}
- docshelper.AddDocProvider("config", docsProvider)
+ docshelper.AddDocProviderFunc(docsProvider)
}
--- a/minifiers/config_test.go
+++ b/minifiers/config_test.go
@@ -14,7 +14,6 @@
package minifiers
import (
- "fmt"
"testing"
"github.com/spf13/viper"
@@ -26,8 +25,8 @@
c := qt.New(t)
v := viper.New()
- v.Set("minifiers", map[string]interface{}{
- "enablexml": false,
+ v.Set("minify", map[string]interface{}{
+ "disablexml": true,
"tdewolff": map[string]interface{}{
"html": map[string]interface{}{
"keepwhitespace": false,
@@ -36,10 +35,11 @@
})
conf, err := decodeConfig(v)
- fmt.Println(conf)
c.Assert(err, qt.IsNil)
+ c.Assert(conf.MinifyOutput, qt.Equals, false)
+
// explicitly set value
c.Assert(conf.Tdewolff.HTML.KeepWhitespace, qt.Equals, false)
// default value
@@ -47,6 +47,19 @@
c.Assert(conf.Tdewolff.CSS.KeepCSS2, qt.Equals, true)
// `enable` flags
- c.Assert(conf.EnableHTML, qt.Equals, true)
- c.Assert(conf.EnableXML, qt.Equals, false)
+ c.Assert(conf.DisableHTML, qt.Equals, false)
+ c.Assert(conf.DisableXML, qt.Equals, true)
+}
+
+func TestConfigLegacy(t *testing.T) {
+ c := qt.New(t)
+ v := viper.New()
+
+ // This was a bool < Hugo v0.58.
+ v.Set("minify", true)
+
+ conf, err := decodeConfig(v)
+ c.Assert(err, qt.IsNil)
+ c.Assert(conf.MinifyOutput, qt.Equals, true)
+
}
--- a/minifiers/minifiers.go
+++ b/minifiers/minifiers.go
@@ -30,6 +30,9 @@
// Client wraps a minifier.
type Client struct {
+ // Whether output minification is enabled (HTML in /public)
+ MinifyOutput bool
+
m *minify.M
}
@@ -62,30 +65,30 @@
m := minify.New()
if err != nil {
- return Client{m: m}, err
+ return Client{}, err
}
// We use the Type definition of the media types defined in the site if found.
- if conf.EnableCSS {
+ if !conf.DisableCSS {
addMinifier(m, mediaTypes, "css", &conf.Tdewolff.CSS)
}
- if conf.EnableJS {
+ if !conf.DisableJS {
addMinifier(m, mediaTypes, "js", &conf.Tdewolff.JS)
m.AddRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), &conf.Tdewolff.JS)
}
- if conf.EnableJSON {
+ if !conf.DisableJSON {
addMinifier(m, mediaTypes, "json", &conf.Tdewolff.JSON)
m.AddRegexp(regexp.MustCompile(`^(application|text)/(x-|ld\+)?json$`), &conf.Tdewolff.JSON)
}
- if conf.EnableSVG {
+ if !conf.DisableSVG {
addMinifier(m, mediaTypes, "svg", &conf.Tdewolff.SVG)
}
- if conf.EnableXML {
+ if !conf.DisableXML {
addMinifier(m, mediaTypes, "xml", &conf.Tdewolff.XML)
}
// HTML
- if conf.EnableHTML {
+ if !conf.DisableHTML {
addMinifier(m, mediaTypes, "html", &conf.Tdewolff.HTML)
for _, of := range outputFormats {
if of.IsHTML {
@@ -94,7 +97,7 @@
}
}
- return Client{m: m}, nil
+ return Client{m: m, MinifyOutput: conf.MinifyOutput}, nil
}
func addMinifier(m *minify.M, mt media.Types, suffix string, min minify.Minifier) {
--- a/minifiers/minifiers_test.go
+++ b/minifiers/minifiers_test.go
@@ -75,11 +75,11 @@
}
-func TestConfiguredMinify(t *testing.T) {
+func TestConfigureMinify(t *testing.T) {
c := qt.New(t)
v := viper.New()
- v.Set("minifiers", map[string]interface{}{
- "enablexml": false,
+ v.Set("minify", map[string]interface{}{
+ "disablexml": true,
"tdewolff": map[string]interface{}{
"html": map[string]interface{}{
"keepwhitespace": true,
--- a/output/docshelper.go
+++ b/output/docshelper.go
@@ -10,15 +10,16 @@
// This is is just some helpers used to create some JSON used in the Hugo docs.
func init() {
- docsProvider := func() map[string]interface{} {
- docs := make(map[string]interface{})
-
- docs["formats"] = DefaultFormats
- docs["layouts"] = createLayoutExamples()
- return docs
+ docsProvider := func() docshelper.DocProvider {
+ return docshelper.DocProvider{
+ "output": map[string]interface{}{
+ "formats": DefaultFormats,
+ "layouts": createLayoutExamples(),
+ },
+ }
}
- docshelper.AddDocProvider("output", docsProvider)
+ docshelper.AddDocProviderFunc(docsProvider)
}
func createLayoutExamples() interface{} {
--- a/parser/lowercase_camel_json.go
+++ b/parser/lowercase_camel_json.go
@@ -14,6 +14,7 @@
package parser
import (
+ "bytes"
"encoding/json"
"regexp"
"unicode"
@@ -35,6 +36,12 @@
converted := keyMatchRegex.ReplaceAllFunc(
marshalled,
func(match []byte) []byte {
+
+ // Attributes on the form XML, JSON etc.
+ if bytes.Equal(match, bytes.ToUpper(match)) {
+ return bytes.ToLower(match)
+ }
+
// Empty keys are valid JSON, only lowercase if we do not have an
// empty key.
if len(match) > 2 {
--- a/publisher/publisher.go
+++ b/publisher/publisher.go
@@ -68,21 +68,16 @@
// DestinationPublisher is the default and currently only publisher in Hugo. This
// publisher prepares and publishes an item to the defined destination, e.g. /public.
type DestinationPublisher struct {
- fs afero.Fs
- minify bool
- min minifiers.Client
+ fs afero.Fs
+ min minifiers.Client
}
// NewDestinationPublisher creates a new DestinationPublisher.
func NewDestinationPublisher(fs afero.Fs, outputFormats output.Formats, mediaTypes media.Types, cfg config.Provider) (pub DestinationPublisher, err error) {
pub = DestinationPublisher{fs: fs}
- minify := cfg.GetBool("minify")
- if minify {
- pub.min, err = minifiers.New(mediaTypes, outputFormats, cfg)
- if err != nil {
- return
- }
- pub.minify = true
+ pub.min, err = minifiers.New(mediaTypes, outputFormats, cfg)
+ if err != nil {
+ return
}
return
}
@@ -155,7 +150,7 @@
}
- if p.minify {
+ if p.min.MinifyOutput {
minifyTransformer := p.min.Transformer(f.OutputFormat.MediaType)
if minifyTransformer != nil {
transformers = append(transformers, minifyTransformer)
--- a/resources/resource_transformers/minifier/minify_test.go
+++ b/resources/resource_transformers/minifier/minify_test.go
@@ -41,23 +41,3 @@
c.Assert(content, qt.Equals, "<h1>Hugo Rocks!</h1>")
}
-
-func TestNoMinifier(t *testing.T) {
- c := qt.New(t)
-
- spec, _ := htesting.NewTestResourceSpec()
- spec.Cfg.Set("minifiers.enableXML", false)
- client, _ := New(spec)
-
- original := "<title> Hugo Rocks! </title>"
- r, err := htesting.NewResourceTransformerForSpec(spec, "hugo.xml", original)
- c.Assert(err, qt.IsNil)
-
- transformed, err := client.Minify(r)
- c.Assert(err, qt.IsNil)
-
- content, err := transformed.(resource.ContentProvider).Content()
- // error should be ignored because general users cannot control codes under `theme`s
- c.Assert(err, qt.IsNil)
- c.Assert(content, qt.Equals, original)
-}
--- a/tpl/cast/docshelper.go
+++ b/tpl/cast/docshelper.go
@@ -24,8 +24,7 @@
// This file provides documentation support and is randomly put into this package.
func init() {
- docsProvider := func() map[string]interface{} {
- docs := make(map[string]interface{})
+ docsProvider := func() docshelper.DocProvider {
d := &deps.Deps{
Cfg: viper.New(),
Log: loggers.NewErrorLogger(),
@@ -41,11 +40,11 @@
}
- docs["funcs"] = namespaces
- return docs
+ return docshelper.DocProvider{"tpl": map[string]interface{}{"funcs": namespaces}}
+
}
- docshelper.AddDocProvider("tpl", docsProvider)
+ docshelper.AddDocProviderFunc(docsProvider)
}
func newTestConfig() *viper.Viper {