ref: ebb56e8bdbfaf4f955326017e40b2805850871e9
parent: 6b9934a26615ea614b1774770532cae9762a58d3
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Tue Aug 28 10:18:12 EDT 2018
Improve minifier MIME type resolution This commit also removes the deprecated `Suffix` from MediaType. Now use `Suffixes` and put the MIME type suffix in the type, e.g. `application/svg+xml`. Fixes #5093
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -97,7 +97,7 @@
[mediaTypes]
[mediaTypes."text/m1"]
-suffix = "m1main"
+suffixes = ["m1main"]
[outputFormats.o1]
mediaType = "text/m1"
@@ -135,9 +135,9 @@
[mediaTypes]
[mediaTypes."text/m1"]
-suffix = "m1theme"
+suffixes = ["m1theme"]
[mediaTypes."text/m2"]
-suffix = "m2theme"
+suffixes = ["m2theme"]
[outputFormats.o1]
mediaType = "text/m1"
@@ -207,10 +207,14 @@
b.AssertObject(`
map[string]interface {}{ "text/m1": map[string]interface {}{- "suffix": "m1main",
+ "suffixes": []interface {}{+ "m1main",
+ },
},
"text/m2": map[string]interface {}{- "suffix": "m2theme",
+ "suffixes": []interface {}{+ "m2theme",
+ },
},
}`, got["mediatypes"])
@@ -221,7 +225,6 @@
"mediatype": Type{MainType: "text",
SubType: "m1",
- OldSuffix: "m1main",
Delimiter: ".",
Suffixes: []string{"m1main",
@@ -233,7 +236,6 @@
"mediatype": Type{MainType: "text",
SubType: "m2",
- OldSuffix: "m2theme",
Delimiter: ".",
Suffixes: []string{"m2theme",
--- a/hugolib/page_bundler_test.go
+++ b/hugolib/page_bundler_test.go
@@ -435,7 +435,7 @@
cfg.Set("baseURL", "https://example.com") cfg.Set("mediaTypes", map[string]interface{}{ "text/bepsays": map[string]interface{}{- "suffix": "bep",
+ "suffixes": []string{"bep"},},
})
--- a/hugolib/site_output_test.go
+++ b/hugolib/site_output_test.go
@@ -276,14 +276,12 @@
[mediaTypes]
[mediaTypes."text/nodot"]
-suffix = ""
delimiter = ""
[mediaTypes."text/defaultdelim"]
-suffix = "defd"
+suffixes = ["defd"]
[mediaTypes."text/nosuffix"]
-suffix = ""
[mediaTypes."text/customdelim"]
-suffix = "del"
+suffixes = ["del"]
delimiter = "_"
[outputs]
@@ -321,7 +319,7 @@
th.assertFileContent("public/_redirects", "a dotless") th.assertFileContent("public/defaultdelimbase.defd", "default delimim")// This looks weird, but the user has chosen this definition.
- th.assertFileContent("public/nosuffixbase.", "no suffix")+ th.assertFileContent("public/nosuffixbase", "no suffix") th.assertFileContent("public/customdelimbase_del", "custom delim")s := h.Sites[0]
@@ -332,7 +330,7 @@
require.Equal(t, "/blog/_redirects", outputs.Get("DOTLESS").RelPermalink()) require.Equal(t, "/blog/defaultdelimbase.defd", outputs.Get("DEF").RelPermalink())- require.Equal(t, "/blog/nosuffixbase.", outputs.Get("NOS").RelPermalink())+ require.Equal(t, "/blog/nosuffixbase", outputs.Get("NOS").RelPermalink()) require.Equal(t, "/blog/customdelimbase_del", outputs.Get("CUS").RelPermalink())}
--- a/media/mediaType.go
+++ b/media/mediaType.go
@@ -15,11 +15,13 @@
import (
"encoding/json"
+ "errors"
"fmt"
"sort"
"strings"
- "github.com/gohugoio/hugo/helpers"
+ "github.com/gohugoio/hugo/common/maps"
+
"github.com/mitchellh/mapstructure"
)
@@ -37,10 +39,9 @@
MainType string `json:"mainType"` // i.e. text
SubType string `json:"subType"` // i.e. html
- // Deprecated in Hugo 0.44. To be renamed and unexported.
- // Was earlier used both to set file suffix and to augment the MIME type.
- // This had its limitations and issues.
- OldSuffix string `json:"-" mapstructure:"suffix"`
+ // This is the optional suffix after the "+" in the MIME type,
+ // e.g. "xml" in "applicatiion/rss+xml".
+ mimeSuffix string
Delimiter string `json:"delimiter"` // e.g. "."
@@ -79,7 +80,7 @@
suffix = subParts[1]
}
- return Type{MainType: mainType, SubType: subType, OldSuffix: suffix}, nil+ return Type{MainType: mainType, SubType: subType, mimeSuffix: suffix}, nil}
// Type returns a string representing the main- and sub-type of a media type, e.g. "text/css".
@@ -91,8 +92,8 @@
// Examples are
// image/svg+xml
// text/css
- if m.OldSuffix != "" {- return fmt.Sprintf("%s/%s+%s", m.MainType, m.SubType, m.OldSuffix)+ if m.mimeSuffix != "" {+ return fmt.Sprintf("%s/%s+%s", m.MainType, m.SubType, m.mimeSuffix)}
return fmt.Sprintf("%s/%s", m.MainType, m.SubType)@@ -130,9 +131,9 @@
HTMLType = Type{MainType: "text", SubType: "html", Suffixes: []string{"html"}, Delimiter: defaultDelimiter} JavascriptType = Type{MainType: "application", SubType: "javascript", Suffixes: []string{"js"}, Delimiter: defaultDelimiter} JSONType = Type{MainType: "application", SubType: "json", Suffixes: []string{"json"}, Delimiter: defaultDelimiter}- RSSType = Type{MainType: "application", SubType: "rss", OldSuffix: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter}+ RSSType = Type{MainType: "application", SubType: "rss", mimeSuffix: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter} XMLType = Type{MainType: "application", SubType: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter}- SVGType = Type{MainType: "image", SubType: "svg", OldSuffix: "xml", Suffixes: []string{"svg"}, Delimiter: defaultDelimiter}+ SVGType = Type{MainType: "image", SubType: "svg", mimeSuffix: "xml", Suffixes: []string{"svg"}, Delimiter: defaultDelimiter} TextType = Type{MainType: "text", SubType: "plain", Suffixes: []string{"txt"}, Delimiter: defaultDelimiter} OctetType = Type{MainType: "application", SubType: "octet-stream"}@@ -182,6 +183,17 @@
return Type{}, false}
+// BySuffix will return all media types matching a suffix.
+func (t Types) BySuffix(suffix string) []Type {+ var types []Type
+ for _, tt := range t {+ if match := tt.matchSuffix(suffix); match != "" {+ types = append(types, tt)
+ }
+ }
+ return types
+}
+
// GetFirstBySuffix will return the first media type matching the given suffix.
func (t Types) GetFirstBySuffix(suffix string) (Type, bool) { for _, tt := range t {@@ -214,9 +226,6 @@
}
func (t Type) matchSuffix(suffix string) string {- if strings.EqualFold(suffix, t.OldSuffix) {- return t.OldSuffix
- }
for _, s := range t.Suffixes { if strings.EqualFold(suffix, s) {return s
@@ -246,9 +255,8 @@
return
}
-func suffixIsDeprecated() {- helpers.Deprecated("MediaType", "Suffix in config.toml", `-Before Hugo 0.44 this was used both to set a custom file suffix and as way
+func suffixIsRemoved() error {+ return errors.New(`MediaType.Suffix is removed. Before Hugo 0.44 this was used both to set a custom file suffix and as way
to augment the mediatype definition (what you see after the "+", e.g. "image/svg+xml").
This had its limitations. For one, it was only possible with one file extension per MIME type.
@@ -272,16 +280,13 @@
[mediaTypes."my/custom-mediatype"]
suffixes = ["txt"]
-Hugo will still respect values set in "suffix" if no value for "suffixes" is provided, but this will be removed
-in a future release.
-
Note that you can still get the Media Type's suffix from a template: {{ $mediaType.Suffix }}. But this will now map to the MIME type filename.-`, false)
+`)
}
// DecodeTypes takes a list of media type configurations and merges those,
// in the order given, with the Hugo defaults as the last resort.
-func DecodeTypes(maps ...map[string]interface{}) (Types, error) {+func DecodeTypes(mms ...map[string]interface{}) (Types, error) {var m Types
// Maps type string to Type. Type string is the full application/svg+xml.
@@ -293,7 +298,7 @@
mmm[dt.Type()] = dt
}
- for _, mm := range maps {+ for _, mm := range mms { for k, v := range mm {var mediaType Type
@@ -311,23 +316,16 @@
}
vm := v.(map[string]interface{})+ maps.ToLower(vm)
_, delimiterSet := vm["delimiter"]
_, suffixSet := vm["suffix"]
if suffixSet {- suffixIsDeprecated()
+ return Types{}, suffixIsRemoved()}
- // Before Hugo 0.44 we had a non-standard use of the Suffix
- // attribute, and this is now deprecated (use Suffixes for file suffixes).
- // But we need to keep old configurations working for a while.
- if len(mediaType.Suffixes) == 0 && mediaType.OldSuffix != "" {- mediaType.Suffixes = []string{mediaType.OldSuffix}- }
// The user may set the delimiter as an empty string.
if !delimiterSet && len(mediaType.Suffixes) != 0 {- mediaType.Delimiter = defaultDelimiter
- } else if suffixSet && !delimiterSet {mediaType.Delimiter = defaultDelimiter
}
--- a/media/mediaType_test.go
+++ b/media/mediaType_test.go
@@ -80,11 +80,19 @@
assert.False(found)
}
+func TestBySuffix(t *testing.T) {+ assert := require.New(t)
+ formats := DefaultTypes.BySuffix("xml")+ assert.Equal(2, len(formats))
+ assert.Equal("rss", formats[0].SubType)+ assert.Equal("xml", formats[1].SubType)+}
+
func TestGetFirstBySuffix(t *testing.T) {assert := require.New(t)
f, found := DefaultTypes.GetFirstBySuffix("xml")assert.True(found)
- assert.Equal(Type{MainType: "application", SubType: "rss", OldSuffix: "xml", Delimiter: ".", Suffixes: []string{"xml"}, fileSuffix: "xml"}, f)+ assert.Equal(Type{MainType: "application", SubType: "rss", mimeSuffix: "xml", Delimiter: ".", Suffixes: []string{"xml"}, fileSuffix: "xml"}, f)}
func TestFromTypeString(t *testing.T) {@@ -94,11 +102,11 @@
f, err = fromString("application/custom")require.NoError(t, err)
- require.Equal(t, Type{MainType: "application", SubType: "custom", OldSuffix: "", fileSuffix: ""}, f)+ require.Equal(t, Type{MainType: "application", SubType: "custom", mimeSuffix: "", fileSuffix: ""}, f) f, err = fromString("application/custom+sfx")require.NoError(t, err)
- require.Equal(t, Type{MainType: "application", SubType: "custom", OldSuffix: "sfx"}, f)+ require.Equal(t, Type{MainType: "application", SubType: "custom", mimeSuffix: "sfx"}, f) _, err = fromString("noslash")require.Error(t, err)
@@ -105,7 +113,7 @@
f, err = fromString("text/xml; charset=utf-8")require.NoError(t, err)
- require.Equal(t, Type{MainType: "text", SubType: "xml", OldSuffix: ""}, f)+ require.Equal(t, Type{MainType: "text", SubType: "xml", mimeSuffix: ""}, f)require.Equal(t, "", f.Suffix())
}
@@ -146,28 +154,24 @@
json, found := tt.GetBySuffix("jasn")require.True(t, found)
require.Equal(t, "application/json", json.String(), name)
+ require.Equal(t, ".jasn", json.FullSuffix())
}},
{- "Suffix from key, multiple file suffixes",
+ "MIME suffix in key, multiple file suffixes, custom delimiter",
[]map[string]interface{}{ { "application/hugo+hg": map[string]interface{}{- "Suffixes": []string{"hg1", "hg2"},+ "suffixes": []string{"hg1", "hg2"},+ "Delimiter": "_",
}}},
false,
func(t *testing.T, name string, tt Types) {require.Len(t, tt, len(DefaultTypes)+1)
- hg, found := tt.GetBySuffix("hg")+ hg, found := tt.GetBySuffix("hg2")require.True(t, found)
- require.Equal(t, "hg", hg.OldSuffix)
- require.Equal(t, "hg", hg.Suffix())
- require.Equal(t, ".hg", hg.FullSuffix())
- require.Equal(t, "application/hugo+hg", hg.String(), name)
- hg, found = tt.GetBySuffix("hg2")- require.True(t, found)
- require.Equal(t, "hg", hg.OldSuffix)
+ require.Equal(t, "hg", hg.mimeSuffix)
require.Equal(t, "hg2", hg.Suffix())
- require.Equal(t, ".hg2", hg.FullSuffix())
+ require.Equal(t, "_hg2", hg.FullSuffix())
require.Equal(t, "application/hugo+hg", hg.String(), name)
hg, found = tt.GetByType("application/hugo+hg")@@ -178,8 +182,8 @@
"Add custom media type",
[]map[string]interface{}{ {- "text/hugo": map[string]interface{}{- "suffix": "hgo"}}},
+ "text/hugo+hgo": map[string]interface{}{+ "Suffixes": []string{"hgo2"}}}},false,
func(t *testing.T, name string, tt Types) {require.Len(t, tt, len(DefaultTypes)+1)
@@ -188,7 +192,7 @@
_, found := tt.GetBySuffix("json")require.True(t, found)
- hugo, found := tt.GetBySuffix("hgo")+ hugo, found := tt.GetBySuffix("hgo2")require.True(t, found)
require.Equal(t, "text/hugo+hgo", hugo.String(), name)
}},
--- a/minifiers/minifiers.go
+++ b/minifiers/minifiers.go
@@ -71,60 +71,35 @@
}
// We use the Type definition of the media types defined in the site if found.
- addMinifierFunc(m, mediaTypes, "text/css", "css", css.Minify)
- addMinifierFunc(m, mediaTypes, "application/javascript", "js", js.Minify)
+ addMinifierFunc(m, mediaTypes, "css", css.Minify)
+ addMinifierFunc(m, mediaTypes, "js", js.Minify)
m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)- addMinifierFunc(m, mediaTypes, "application/json", "json", json.Minify)
- addMinifierFunc(m, mediaTypes, "image/svg+xml", "svg", svg.Minify)
- addMinifierFunc(m, mediaTypes, "application/xml", "xml", xml.Minify)
- addMinifierFunc(m, mediaTypes, "application/rss", "xml", xml.Minify)
+ addMinifierFunc(m, mediaTypes, "json", json.Minify)
+ addMinifierFunc(m, mediaTypes, "svg", svg.Minify)
+ addMinifierFunc(m, mediaTypes, "xml", xml.Minify)
// HTML
- addMinifier(m, mediaTypes, "text/html", "html", htmlMin)
+ addMinifier(m, mediaTypes, "html", htmlMin)
for _, of := range outputFormats { if of.IsHTML {- addMinifier(m, mediaTypes, of.MediaType.Type(), "html", htmlMin)
+ m.Add(of.MediaType.Type(), htmlMin)
}
}
+
return Client{m: m}}
-func addMinifier(m *minify.M, mt media.Types, typeString, suffix string, min minify.Minifier) {- resolvedTypeStr := resolveMediaTypeString(mt, typeString, suffix)
- m.Add(resolvedTypeStr, min)
- if resolvedTypeStr != typeString {- m.Add(typeString, min)
+func addMinifier(m *minify.M, mt media.Types, suffix string, min minify.Minifier) {+ types := mt.BySuffix(suffix)
+ for _, t := range types {+ m.Add(t.Type(), min)
}
}
-func addMinifierFunc(m *minify.M, mt media.Types, typeString, suffix string, fn minify.MinifierFunc) {- resolvedTypeStr := resolveMediaTypeString(mt, typeString, suffix)
- m.AddFunc(resolvedTypeStr, fn)
- if resolvedTypeStr != typeString {- m.AddFunc(typeString, fn)
+func addMinifierFunc(m *minify.M, mt media.Types, suffix string, min minify.MinifierFunc) {+ types := mt.BySuffix(suffix)
+ for _, t := range types {+ m.AddFunc(t.Type(), min)
}
-}
-
-func resolveMediaTypeString(types media.Types, typeStr, suffix string) string {- if m, found := resolveMediaType(types, typeStr, suffix); found {- return m.Type()
- }
- // Fall back to the default.
- return typeStr
-}
-
-// Make sure we match the matching pattern with what the user have actually defined
-// in his or hers media types configuration.
-func resolveMediaType(types media.Types, typeStr, suffix string) (media.Type, bool) {- if m, found := types.GetByType(typeStr); found {- return m, true
- }
-
- if m, found := types.GetFirstBySuffix(suffix); found {- return m, true
- }
-
- return media.Type{}, false-
}
--- a/minifiers/minifiers_test.go
+++ b/minifiers/minifiers_test.go
@@ -32,4 +32,10 @@
assert.NoError(m.Minify(media.CSSType, &b, strings.NewReader("body { color: blue; }"))) assert.Equal("body{color:blue}", b.String())+
+ b.Reset()
+
+ // RSS should be handled as XML
+ assert.NoError(m.Minify(media.RSSType, &b, strings.NewReader("<hello> Hugo! </hello> ")))+ assert.Equal("<hello>Hugo!</hello>", b.String())}
--- a/output/outputFormat_test.go
+++ b/output/outputFormat_test.go
@@ -93,11 +93,9 @@
func TestGetFormatByFilename(t *testing.T) {noExtNoDelimMediaType := media.TextType
- noExtNoDelimMediaType.OldSuffix = ""
noExtNoDelimMediaType.Delimiter = ""
noExtMediaType := media.TextType
- noExtMediaType.OldSuffix = ""
var (
noExtDelimFormat = Format{--
⑨