URI: 
       Improve minifier MIME type resolution - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit ebb56e8bdbfaf4f955326017e40b2805850871e9
   DIR parent 6b9934a26615ea614b1774770532cae9762a58d3
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Tue, 28 Aug 2018 14:18:12 +0200
       
       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
       
       Diffstat:
         M hugolib/config_test.go              |      16 +++++++++-------
         M hugolib/page_bundler_test.go        |       2 +-
         M hugolib/site_output_test.go         |      10 ++++------
         M media/mediaType.go                  |      60 +++++++++++++++----------------
         M media/mediaType_test.go             |      40 +++++++++++++++++--------------
         M minifiers/minifiers.go              |      57 +++++++++----------------------
         M minifiers/minifiers_test.go         |       6 ++++++
         M output/outputFormat_test.go         |       2 --
       
       8 files changed, 87 insertions(+), 106 deletions(-)
       ---
   DIR diff --git a/hugolib/config_test.go b/hugolib/config_test.go
       @@ -97,7 +97,7 @@ top = "top"
        
        [mediaTypes]
        [mediaTypes."text/m1"]
       -suffix = "m1main"
       +suffixes = ["m1main"]
        
        [outputFormats.o1]
        mediaType = "text/m1"
       @@ -135,9 +135,9 @@ p3 = "p3 theme"
        
        [mediaTypes]
        [mediaTypes."text/m1"]
       -suffix = "m1theme"
       +suffixes = ["m1theme"]
        [mediaTypes."text/m2"]
       -suffix = "m2theme"
       +suffixes = ["m2theme"]
        
        [outputFormats.o1]
        mediaType = "text/m1"
       @@ -207,10 +207,14 @@ map[string]interface {}{
                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 @@ map[string]interface {}{
            "mediatype": Type{
              MainType: "text",
              SubType: "m1",
       -      OldSuffix: "m1main",
              Delimiter: ".",
              Suffixes: []string{
                "m1main",
       @@ -233,7 +236,6 @@ map[string]interface {}{
            "mediatype": Type{
              MainType: "text",
              SubType: "m2",
       -      OldSuffix: "m2theme",
              Delimiter: ".",
              Suffixes: []string{
                "m2theme",
   DIR diff --git a/hugolib/page_bundler_test.go b/hugolib/page_bundler_test.go
       @@ -435,7 +435,7 @@ func newTestBundleSources(t *testing.T) (*hugofs.Fs, *viper.Viper) {
                cfg.Set("baseURL", "https://example.com")
                cfg.Set("mediaTypes", map[string]interface{}{
                        "text/bepsays": map[string]interface{}{
       -                        "suffix": "bep",
       +                        "suffixes": []string{"bep"},
                        },
                })
        
   DIR diff --git a/hugolib/site_output_test.go b/hugolib/site_output_test.go
       @@ -276,14 +276,12 @@ disableKinds = ["page", "section", "taxonomy", "taxonomyTerm", "sitemap", "robot
        
        [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 @@ baseName = "customdelimbase"
                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 @@ baseName = "customdelimbase"
        
                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())
        
        }
   DIR diff --git a/media/mediaType.go b/media/mediaType.go
       @@ -15,11 +15,13 @@ package media
        
        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 @@ type Type struct {
                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 @@ func fromString(t string) (Type, error) {
                        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 @@ func (m Type) Type() string {
                // 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 @@ var (
                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 @@ func (t Types) GetByType(tp string) (Type, bool) {
                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 Types) GetBySuffix(suffix string) (tp Type, found bool) {
        }
        
        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 @@ func (t Types) GetByMainSubType(mainType, subType string) (tp Type, found bool) 
                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 @@ To:
        [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 @@ func DecodeTypes(maps ...map[string]interface{}) (Types, error) {
                        mmm[dt.Type()] = dt
                }
        
       -        for _, mm := range maps {
       +        for _, mm := range mms {
                        for k, v := range mm {
                                var mediaType Type
        
       @@ -311,24 +316,17 @@ func DecodeTypes(maps ...map[string]interface{}) (Types, error) {
                                }
        
                                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
                                }
        
                                mmm[k] = mediaType
   DIR diff --git a/media/mediaType_test.go b/media/mediaType_test.go
       @@ -80,11 +80,19 @@ func TestGetByMainSubType(t *testing.T) {
                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,18 +102,18 @@ func TestFromTypeString(t *testing.T) {
        
                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)
        
                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 @@ func TestDecodeTypes(t *testing.T) {
                                        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")
       -                                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")
       +                                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 @@ func TestDecodeTypes(t *testing.T) {
                                "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 @@ func TestDecodeTypes(t *testing.T) {
                                        _, 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)
                                }},
   DIR diff --git a/minifiers/minifiers.go b/minifiers/minifiers.go
       @@ -71,60 +71,35 @@ func New(mediaTypes media.Types, outputFormats output.Formats) Client {
                }
        
                // 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}
        
       -}
       +        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 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 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 resolveMediaTypeString(types media.Types, typeStr, suffix string) string {
       -        if m, found := resolveMediaType(types, typeStr, suffix); found {
       -                return m.Type()
       +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)
                }
       -        // 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
       -
        }
   DIR diff --git a/minifiers/minifiers_test.go b/minifiers/minifiers_test.go
       @@ -32,4 +32,10 @@ func TestNew(t *testing.T) {
        
                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())
        }
   DIR diff --git a/output/outputFormat_test.go b/output/outputFormat_test.go
       @@ -93,11 +93,9 @@ func TestGetFormatByExt(t *testing.T) {
        
        func TestGetFormatByFilename(t *testing.T) {
                noExtNoDelimMediaType := media.TextType
       -        noExtNoDelimMediaType.OldSuffix = ""
                noExtNoDelimMediaType.Delimiter = ""
        
                noExtMediaType := media.TextType
       -        noExtMediaType.OldSuffix = ""
        
                var (
                        noExtDelimFormat = Format{