media, hugolib: Support extension-less media types

This change is motivated by Netlify's `_redirects` files, which is currently not possible to generate with Hugo.

This commit adds a `Delimiter` field to media type, which defaults to ".", but can be blanked out.

Fixes #3614
This commit is contained in:
Bjørn Erik Pedersen
2017-06-20 08:45:52 +02:00
parent 516e6c6dc5
commit 0f40e1fadf
7 changed files with 183 additions and 34 deletions

View File

@@ -22,6 +22,10 @@ import (
"github.com/mitchellh/mapstructure"
)
const (
defaultDelimiter = "."
)
// A media type (also known as MIME type and content type) is a two-part identifier for
// file formats and format contents transmitted on the Internet.
// For Hugo's use case, we use the top-level type name / subtype name + suffix.
@@ -29,9 +33,10 @@ import (
// If suffix is not provided, the sub type will be used.
// See // https://en.wikipedia.org/wiki/Media_type
type Type struct {
MainType string // i.e. text
SubType string // i.e. html
Suffix string // i.e html
MainType string // i.e. text
SubType string // i.e. html
Suffix string // i.e html
Delimiter string // defaults to "."
}
// FromTypeString creates a new Type given a type sring on the form MainType/SubType and
@@ -54,7 +59,7 @@ func FromString(t string) (Type, error) {
suffix = subParts[1]
}
return Type{MainType: mainType, SubType: subType, Suffix: suffix}, nil
return Type{MainType: mainType, SubType: subType, Suffix: suffix, Delimiter: defaultDelimiter}, nil
}
// Type returns a string representing the main- and sub-type of a media type, i.e. "text/css".
@@ -72,16 +77,21 @@ func (m Type) String() string {
return fmt.Sprintf("%s/%s", m.MainType, m.SubType)
}
// FullSuffix returns the file suffix with any delimiter prepended.
func (m Type) FullSuffix() string {
return m.Delimiter + m.Suffix
}
var (
CalendarType = Type{"text", "calendar", "ics"}
CSSType = Type{"text", "css", "css"}
CSVType = Type{"text", "csv", "csv"}
HTMLType = Type{"text", "html", "html"}
JavascriptType = Type{"application", "javascript", "js"}
JSONType = Type{"application", "json", "json"}
RSSType = Type{"application", "rss", "xml"}
XMLType = Type{"application", "xml", "xml"}
TextType = Type{"text", "plain", "txt"}
CalendarType = Type{"text", "calendar", "ics", defaultDelimiter}
CSSType = Type{"text", "css", "css", defaultDelimiter}
CSVType = Type{"text", "csv", "csv", defaultDelimiter}
HTMLType = Type{"text", "html", "html", defaultDelimiter}
JavascriptType = Type{"application", "javascript", "js", defaultDelimiter}
JSONType = Type{"application", "json", "json", defaultDelimiter}
RSSType = Type{"application", "rss", "xml", defaultDelimiter}
XMLType = Type{"application", "xml", "xml", defaultDelimiter}
TextType = Type{"text", "plain", "txt", defaultDelimiter}
)
var DefaultTypes = Types{

View File

@@ -40,6 +40,7 @@ func TestDefaultTypes(t *testing.T) {
require.Equal(t, test.expectedMainType, test.tp.MainType)
require.Equal(t, test.expectedSubType, test.tp.SubType)
require.Equal(t, test.expectedSuffix, test.tp.Suffix)
require.Equal(t, defaultDelimiter, test.tp.Delimiter)
require.Equal(t, test.expectedType, test.tp.Type())
require.Equal(t, test.expectedString, test.tp.String())
@@ -66,11 +67,11 @@ func TestFromTypeString(t *testing.T) {
f, err = FromString("application/custom")
require.NoError(t, err)
require.Equal(t, Type{MainType: "application", SubType: "custom", Suffix: "custom"}, f)
require.Equal(t, Type{MainType: "application", SubType: "custom", Suffix: "custom", Delimiter: defaultDelimiter}, f)
f, err = FromString("application/custom+pdf")
require.NoError(t, err)
require.Equal(t, Type{MainType: "application", SubType: "custom", Suffix: "pdf"}, f)
require.Equal(t, Type{MainType: "application", SubType: "custom", Suffix: "pdf", Delimiter: defaultDelimiter}, f)
f, err = FromString("noslash")
require.Error(t, err)