mirror of
https://github.com/gohugoio/hugo.git
synced 2025-09-01 22:42:45 +02:00
Create a struct with all of Hugo's config options
Primary motivation is documentation, but it will also hopefully simplify the code. Also, * Lower case the default output format names; this is in line with the custom ones (map keys) and how it's treated all the places. This avoids doing `stringds.EqualFold` everywhere. Closes #10896 Closes #10620
This commit is contained in:
163
media/builtin.go
Normal file
163
media/builtin.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package media
|
||||
|
||||
type BuiltinTypes struct {
|
||||
CalendarType Type
|
||||
CSSType Type
|
||||
SCSSType Type
|
||||
SASSType Type
|
||||
CSVType Type
|
||||
HTMLType Type
|
||||
JavascriptType Type
|
||||
TypeScriptType Type
|
||||
TSXType Type
|
||||
JSXType Type
|
||||
|
||||
JSONType Type
|
||||
WebAppManifestType Type
|
||||
RSSType Type
|
||||
XMLType Type
|
||||
SVGType Type
|
||||
TextType Type
|
||||
TOMLType Type
|
||||
YAMLType Type
|
||||
|
||||
// Common image types
|
||||
PNGType Type
|
||||
JPEGType Type
|
||||
GIFType Type
|
||||
TIFFType Type
|
||||
BMPType Type
|
||||
WEBPType Type
|
||||
|
||||
// Common font types
|
||||
TrueTypeFontType Type
|
||||
OpenTypeFontType Type
|
||||
|
||||
// Common document types
|
||||
PDFType Type
|
||||
MarkdownType Type
|
||||
|
||||
// Common video types
|
||||
AVIType Type
|
||||
MPEGType Type
|
||||
MP4Type Type
|
||||
OGGType Type
|
||||
WEBMType Type
|
||||
GPPType Type
|
||||
|
||||
// wasm
|
||||
WasmType Type
|
||||
|
||||
OctetType Type
|
||||
}
|
||||
|
||||
var (
|
||||
Builtin = BuiltinTypes{
|
||||
CalendarType: Type{Type: "text/calendar"},
|
||||
CSSType: Type{Type: "text/css"},
|
||||
SCSSType: Type{Type: "text/x-scss"},
|
||||
SASSType: Type{Type: "text/x-sass"},
|
||||
CSVType: Type{Type: "text/csv"},
|
||||
HTMLType: Type{Type: "text/html"},
|
||||
JavascriptType: Type{Type: "text/javascript"},
|
||||
TypeScriptType: Type{Type: "text/typescript"},
|
||||
TSXType: Type{Type: "text/tsx"},
|
||||
JSXType: Type{Type: "text/jsx"},
|
||||
|
||||
JSONType: Type{Type: "application/json"},
|
||||
WebAppManifestType: Type{Type: "application/manifest+json"},
|
||||
RSSType: Type{Type: "application/rss+xml"},
|
||||
XMLType: Type{Type: "application/xml"},
|
||||
SVGType: Type{Type: "image/svg+xml"},
|
||||
TextType: Type{Type: "text/plain"},
|
||||
TOMLType: Type{Type: "application/toml"},
|
||||
YAMLType: Type{Type: "application/yaml"},
|
||||
|
||||
// Common image types
|
||||
PNGType: Type{Type: "image/png"},
|
||||
JPEGType: Type{Type: "image/jpeg"},
|
||||
GIFType: Type{Type: "image/gif"},
|
||||
TIFFType: Type{Type: "image/tiff"},
|
||||
BMPType: Type{Type: "image/bmp"},
|
||||
WEBPType: Type{Type: "image/webp"},
|
||||
|
||||
// Common font types
|
||||
TrueTypeFontType: Type{Type: "font/ttf"},
|
||||
OpenTypeFontType: Type{Type: "font/otf"},
|
||||
|
||||
// Common document types
|
||||
PDFType: Type{Type: "application/pdf"},
|
||||
MarkdownType: Type{Type: "text/markdown"},
|
||||
|
||||
// Common video types
|
||||
AVIType: Type{Type: "video/x-msvideo"},
|
||||
MPEGType: Type{Type: "video/mpeg"},
|
||||
MP4Type: Type{Type: "video/mp4"},
|
||||
OGGType: Type{Type: "video/ogg"},
|
||||
WEBMType: Type{Type: "video/webm"},
|
||||
GPPType: Type{Type: "video/3gpp"},
|
||||
|
||||
// Web assembly.
|
||||
WasmType: Type{Type: "application/wasm"},
|
||||
|
||||
OctetType: Type{Type: "application/octet-stream"},
|
||||
}
|
||||
)
|
||||
|
||||
var defaultMediaTypesConfig = map[string]any{
|
||||
"text/calendar": map[string]any{"suffixes": []string{"ics"}},
|
||||
"text/css": map[string]any{"suffixes": []string{"css"}},
|
||||
"text/x-scss": map[string]any{"suffixes": []string{"scss"}},
|
||||
"text/x-sass": map[string]any{"suffixes": []string{"sass"}},
|
||||
"text/csv": map[string]any{"suffixes": []string{"csv"}},
|
||||
"text/html": map[string]any{"suffixes": []string{"html"}},
|
||||
"text/javascript": map[string]any{"suffixes": []string{"js", "jsm", "mjs"}},
|
||||
"text/typescript": map[string]any{"suffixes": []string{"ts"}},
|
||||
"text/tsx": map[string]any{"suffixes": []string{"tsx"}},
|
||||
"text/jsx": map[string]any{"suffixes": []string{"jsx"}},
|
||||
|
||||
"application/json": map[string]any{"suffixes": []string{"json"}},
|
||||
"application/manifest+json": map[string]any{"suffixes": []string{"webmanifest"}},
|
||||
"application/rss+xml": map[string]any{"suffixes": []string{"xml", "rss"}},
|
||||
"application/xml": map[string]any{"suffixes": []string{"xml"}},
|
||||
"image/svg+xml": map[string]any{"suffixes": []string{"svg"}},
|
||||
"text/plain": map[string]any{"suffixes": []string{"txt"}},
|
||||
"application/toml": map[string]any{"suffixes": []string{"toml"}},
|
||||
"application/yaml": map[string]any{"suffixes": []string{"yaml", "yml"}},
|
||||
|
||||
// Common image types
|
||||
"image/png": map[string]any{"suffixes": []string{"png"}},
|
||||
"image/jpeg": map[string]any{"suffixes": []string{"jpg", "jpeg", "jpe", "jif", "jfif"}},
|
||||
"image/gif": map[string]any{"suffixes": []string{"gif"}},
|
||||
"image/tiff": map[string]any{"suffixes": []string{"tif", "tiff"}},
|
||||
"image/bmp": map[string]any{"suffixes": []string{"bmp"}},
|
||||
"image/webp": map[string]any{"suffixes": []string{"webp"}},
|
||||
|
||||
// Common font types
|
||||
"font/ttf": map[string]any{"suffixes": []string{"ttf"}},
|
||||
"font/otf": map[string]any{"suffixes": []string{"otf"}},
|
||||
|
||||
// Common document types
|
||||
"application/pdf": map[string]any{"suffixes": []string{"pdf"}},
|
||||
"text/markdown": map[string]any{"suffixes": []string{"md", "markdown"}},
|
||||
|
||||
// Common video types
|
||||
"video/x-msvideo": map[string]any{"suffixes": []string{"avi"}},
|
||||
"video/mpeg": map[string]any{"suffixes": []string{"mpg", "mpeg"}},
|
||||
"video/mp4": map[string]any{"suffixes": []string{"mp4"}},
|
||||
"video/ogg": map[string]any{"suffixes": []string{"ogv"}},
|
||||
"video/webm": map[string]any{"suffixes": []string{"webm"}},
|
||||
"video/3gpp": map[string]any{"suffixes": []string{"3gpp", "3gp"}},
|
||||
|
||||
// wasm
|
||||
"application/wasm": map[string]any{"suffixes": []string{"wasm"}},
|
||||
|
||||
"application/octet-stream": map[string]any{},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Apply delimiter to all.
|
||||
for _, m := range defaultMediaTypesConfig {
|
||||
m.(map[string]any)["delimiter"] = "."
|
||||
}
|
||||
}
|
139
media/config.go
Normal file
139
media/config.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2023 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 media
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// DefaultTypes is the default media types supported by Hugo.
|
||||
var DefaultTypes Types
|
||||
|
||||
func init() {
|
||||
|
||||
ns, err := DecodeTypes(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
DefaultTypes = ns.Config
|
||||
|
||||
// Initialize the Builtin types with values from DefaultTypes.
|
||||
v := reflect.ValueOf(&Builtin).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
builtinType := f.Interface().(Type)
|
||||
defaultType, found := DefaultTypes.GetByType(builtinType.Type)
|
||||
if !found {
|
||||
panic(errors.New("missing default type for builtin type: " + builtinType.Type))
|
||||
}
|
||||
f.Set(reflect.ValueOf(defaultType))
|
||||
}
|
||||
}
|
||||
|
||||
// Hold the configuration for a given media type.
|
||||
type MediaTypeConfig struct {
|
||||
// The file suffixes used for this media type.
|
||||
Suffixes []string
|
||||
// Delimiter used before suffix.
|
||||
Delimiter string
|
||||
}
|
||||
|
||||
// DecodeTypes decodes the given map of media types.
|
||||
func DecodeTypes(in map[string]any) (*config.ConfigNamespace[map[string]MediaTypeConfig, Types], error) {
|
||||
|
||||
buildConfig := func(v any) (Types, any, error) {
|
||||
m, err := maps.ToStringMapE(v)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if m == nil {
|
||||
m = map[string]any{}
|
||||
}
|
||||
m = maps.CleanConfigStringMap(m)
|
||||
// Merge with defaults.
|
||||
maps.MergeShallow(m, defaultMediaTypesConfig)
|
||||
|
||||
var types Types
|
||||
|
||||
for k, v := range m {
|
||||
mediaType, err := FromString(k)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := mapstructure.WeakDecode(v, &mediaType); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mm := maps.ToStringMap(v)
|
||||
suffixes, found := maps.LookupEqualFold(mm, "suffixes")
|
||||
if found {
|
||||
mediaType.SuffixesCSV = strings.TrimSpace(strings.ToLower(strings.Join(cast.ToStringSlice(suffixes), ",")))
|
||||
}
|
||||
if mediaType.SuffixesCSV != "" && mediaType.Delimiter == "" {
|
||||
mediaType.Delimiter = DefaultDelimiter
|
||||
}
|
||||
InitMediaType(&mediaType)
|
||||
types = append(types, mediaType)
|
||||
}
|
||||
|
||||
sort.Sort(types)
|
||||
|
||||
return types, m, nil
|
||||
}
|
||||
|
||||
ns, err := config.DecodeNamespace[map[string]MediaTypeConfig](in, buildConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode media types: %w", err)
|
||||
}
|
||||
return ns, nil
|
||||
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
Now you can specify multiple file suffixes using "suffixes", but you need to specify the full MIME type
|
||||
identifier:
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."image/svg+xml"]
|
||||
suffixes = ["svg", "abc" ]
|
||||
|
||||
In most cases, it will be enough to just change:
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."my/custom-mediatype"]
|
||||
suffix = "txt"
|
||||
|
||||
To:
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."my/custom-mediatype"]
|
||||
suffixes = ["txt"]
|
||||
|
||||
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.
|
||||
`)
|
||||
}
|
150
media/config_test.go
Normal file
150
media/config_test.go
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2023 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 media
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestDecodeTypes(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
m map[string]any
|
||||
shouldError bool
|
||||
assert func(t *testing.T, name string, tt Types)
|
||||
}{
|
||||
{
|
||||
"Redefine JSON",
|
||||
map[string]any{
|
||||
"application/json": map[string]any{
|
||||
"suffixes": []string{"jasn"},
|
||||
},
|
||||
},
|
||||
|
||||
false,
|
||||
func(t *testing.T, name string, tt Types) {
|
||||
for _, ttt := range tt {
|
||||
if _, ok := DefaultTypes.GetByType(ttt.Type); !ok {
|
||||
fmt.Println(ttt.Type, "not found in default types")
|
||||
}
|
||||
}
|
||||
|
||||
c.Assert(len(tt), qt.Equals, len(DefaultTypes))
|
||||
json, si, found := tt.GetBySuffix("jasn")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(json.String(), qt.Equals, "application/json")
|
||||
c.Assert(si.FullSuffix, qt.Equals, ".jasn")
|
||||
},
|
||||
},
|
||||
{
|
||||
"MIME suffix in key, multiple file suffixes, custom delimiter",
|
||||
map[string]any{
|
||||
"application/hugo+hg": map[string]any{
|
||||
"suffixes": []string{"hg1", "hG2"},
|
||||
"Delimiter": "_",
|
||||
},
|
||||
},
|
||||
false,
|
||||
func(t *testing.T, name string, tt Types) {
|
||||
c.Assert(len(tt), qt.Equals, len(DefaultTypes)+1)
|
||||
hg, si, found := tt.GetBySuffix("hg2")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(hg.FirstSuffix.Suffix, qt.Equals, "hg1")
|
||||
c.Assert(hg.FirstSuffix.FullSuffix, qt.Equals, "_hg1")
|
||||
c.Assert(si.Suffix, qt.Equals, "hg2")
|
||||
c.Assert(si.FullSuffix, qt.Equals, "_hg2")
|
||||
c.Assert(hg.String(), qt.Equals, "application/hugo+hg")
|
||||
|
||||
_, found = tt.GetByType("application/hugo+hg")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Add custom media type",
|
||||
map[string]any{
|
||||
"text/hugo+hgo": map[string]any{
|
||||
"Suffixes": []string{"hgo2"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
func(t *testing.T, name string, tp Types) {
|
||||
c.Assert(len(tp), qt.Equals, len(DefaultTypes)+1)
|
||||
// Make sure we have not broken the default config.
|
||||
|
||||
_, _, found := tp.GetBySuffix("json")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
|
||||
hugo, _, found := tp.GetBySuffix("hgo2")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(hugo.String(), qt.Equals, "text/hugo+hgo")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result, err := DecodeTypes(test.m)
|
||||
if test.shouldError {
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
} else {
|
||||
c.Assert(err, qt.IsNil)
|
||||
test.assert(t, test.name, result.Config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultTypes(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
for _, test := range []struct {
|
||||
tp Type
|
||||
expectedMainType string
|
||||
expectedSubType string
|
||||
expectedSuffix string
|
||||
expectedType string
|
||||
expectedString string
|
||||
}{
|
||||
{Builtin.CalendarType, "text", "calendar", "ics", "text/calendar", "text/calendar"},
|
||||
{Builtin.CSSType, "text", "css", "css", "text/css", "text/css"},
|
||||
{Builtin.SCSSType, "text", "x-scss", "scss", "text/x-scss", "text/x-scss"},
|
||||
{Builtin.CSVType, "text", "csv", "csv", "text/csv", "text/csv"},
|
||||
{Builtin.HTMLType, "text", "html", "html", "text/html", "text/html"},
|
||||
{Builtin.JavascriptType, "text", "javascript", "js", "text/javascript", "text/javascript"},
|
||||
{Builtin.TypeScriptType, "text", "typescript", "ts", "text/typescript", "text/typescript"},
|
||||
{Builtin.TSXType, "text", "tsx", "tsx", "text/tsx", "text/tsx"},
|
||||
{Builtin.JSXType, "text", "jsx", "jsx", "text/jsx", "text/jsx"},
|
||||
{Builtin.JSONType, "application", "json", "json", "application/json", "application/json"},
|
||||
{Builtin.RSSType, "application", "rss", "xml", "application/rss+xml", "application/rss+xml"},
|
||||
{Builtin.SVGType, "image", "svg", "svg", "image/svg+xml", "image/svg+xml"},
|
||||
{Builtin.TextType, "text", "plain", "txt", "text/plain", "text/plain"},
|
||||
{Builtin.XMLType, "application", "xml", "xml", "application/xml", "application/xml"},
|
||||
{Builtin.TOMLType, "application", "toml", "toml", "application/toml", "application/toml"},
|
||||
{Builtin.YAMLType, "application", "yaml", "yaml", "application/yaml", "application/yaml"},
|
||||
{Builtin.PDFType, "application", "pdf", "pdf", "application/pdf", "application/pdf"},
|
||||
{Builtin.TrueTypeFontType, "font", "ttf", "ttf", "font/ttf", "font/ttf"},
|
||||
{Builtin.OpenTypeFontType, "font", "otf", "otf", "font/otf", "font/otf"},
|
||||
} {
|
||||
c.Assert(test.tp.MainType, qt.Equals, test.expectedMainType)
|
||||
c.Assert(test.tp.SubType, qt.Equals, test.expectedSubType)
|
||||
|
||||
c.Assert(test.tp.Type, qt.Equals, test.expectedType)
|
||||
c.Assert(test.tp.String(), qt.Equals, test.expectedString)
|
||||
|
||||
}
|
||||
|
||||
c.Assert(len(DefaultTypes), qt.Equals, 36)
|
||||
}
|
@@ -16,38 +16,36 @@ package media
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
var zero Type
|
||||
|
||||
const (
|
||||
defaultDelimiter = "."
|
||||
DefaultDelimiter = "."
|
||||
)
|
||||
|
||||
// Type (also known as MIME type and content type) is a two-part identifier for
|
||||
// MediaType (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.
|
||||
// One example would be application/svg+xml
|
||||
// If suffix is not provided, the sub type will be used.
|
||||
// See // https://en.wikipedia.org/wiki/Media_type
|
||||
// <docsmeta>{ "name": "MediaType" }</docsmeta>
|
||||
type Type struct {
|
||||
MainType string `json:"mainType"` // i.e. text
|
||||
SubType string `json:"subType"` // i.e. html
|
||||
Delimiter string `json:"delimiter"` // e.g. "."
|
||||
// The full MIME type string, e.g. "application/rss+xml".
|
||||
Type string `json:"-"`
|
||||
|
||||
// FirstSuffix holds the first suffix defined for this Type.
|
||||
FirstSuffix SuffixInfo `json:"firstSuffix"`
|
||||
// The top-level type name, e.g. "application".
|
||||
MainType string `json:"mainType"`
|
||||
// The subtype name, e.g. "rss".
|
||||
SubType string `json:"subType"`
|
||||
// The delimiter before the suffix, e.g. ".".
|
||||
Delimiter string `json:"delimiter"`
|
||||
|
||||
// FirstSuffix holds the first suffix defined for this MediaType.
|
||||
FirstSuffix SuffixInfo `json:"-"`
|
||||
|
||||
// This is the optional suffix after the "+" in the MIME type,
|
||||
// e.g. "xml" in "application/rss+xml".
|
||||
@@ -55,12 +53,16 @@ type Type struct {
|
||||
|
||||
// E.g. "jpg,jpeg"
|
||||
// Stored as a string to make Type comparable.
|
||||
suffixesCSV string
|
||||
// For internal use only.
|
||||
SuffixesCSV string `json:"-"`
|
||||
}
|
||||
|
||||
// SuffixInfo holds information about a Type's suffix.
|
||||
// SuffixInfo holds information about a Media Type's suffix.
|
||||
type SuffixInfo struct {
|
||||
Suffix string `json:"suffix"`
|
||||
// Suffix is the suffix without the delimiter, e.g. "xml".
|
||||
Suffix string `json:"suffix"`
|
||||
|
||||
// FullSuffix is the suffix with the delimiter, e.g. ".xml".
|
||||
FullSuffix string `json:"fullSuffix"`
|
||||
}
|
||||
|
||||
@@ -121,12 +123,21 @@ func FromStringAndExt(t, ext string) (Type, error) {
|
||||
if err != nil {
|
||||
return tp, err
|
||||
}
|
||||
tp.suffixesCSV = strings.TrimPrefix(ext, ".")
|
||||
tp.Delimiter = defaultDelimiter
|
||||
tp.SuffixesCSV = strings.TrimPrefix(ext, ".")
|
||||
tp.Delimiter = DefaultDelimiter
|
||||
tp.init()
|
||||
return tp, nil
|
||||
}
|
||||
|
||||
// MustFromString is like FromString but panics on error.
|
||||
func MustFromString(t string) Type {
|
||||
tp, err := FromString(t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tp
|
||||
}
|
||||
|
||||
// FromString creates a new Type given a type string on the form MainType/SubType and
|
||||
// an optional suffix, e.g. "text/html" or "text/html+html".
|
||||
func FromString(t string) (Type, error) {
|
||||
@@ -146,52 +157,49 @@ func FromString(t string) (Type, error) {
|
||||
suffix = subParts[1]
|
||||
}
|
||||
|
||||
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".
|
||||
// A suffix identifier will be appended after a "+" if set, e.g. "image/svg+xml".
|
||||
// Hugo will register a set of default media types.
|
||||
// These can be overridden by the user in the configuration,
|
||||
// by defining a media type with the same Type.
|
||||
func (m Type) Type() string {
|
||||
// Examples are
|
||||
// image/svg+xml
|
||||
// text/css
|
||||
if m.mimeSuffix != "" {
|
||||
return m.MainType + "/" + m.SubType + "+" + m.mimeSuffix
|
||||
var typ string
|
||||
if suffix != "" {
|
||||
typ = mainType + "/" + subType + "+" + suffix
|
||||
} else {
|
||||
typ = mainType + "/" + subType
|
||||
}
|
||||
return m.MainType + "/" + m.SubType
|
||||
|
||||
return Type{Type: typ, MainType: mainType, SubType: subType, mimeSuffix: suffix}, nil
|
||||
}
|
||||
|
||||
// For internal use.
|
||||
func (m Type) String() string {
|
||||
return m.Type()
|
||||
return m.Type
|
||||
}
|
||||
|
||||
// Suffixes returns all valid file suffixes for this type.
|
||||
func (m Type) Suffixes() []string {
|
||||
if m.suffixesCSV == "" {
|
||||
if m.SuffixesCSV == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return strings.Split(m.suffixesCSV, ",")
|
||||
return strings.Split(m.SuffixesCSV, ",")
|
||||
}
|
||||
|
||||
// IsText returns whether this Type is a text format.
|
||||
// Note that this may currently return false negatives.
|
||||
// TODO(bep) improve
|
||||
// For internal use.
|
||||
func (m Type) IsText() bool {
|
||||
if m.MainType == "text" {
|
||||
return true
|
||||
}
|
||||
switch m.SubType {
|
||||
case "javascript", "json", "rss", "xml", "svg", TOMLType.SubType, YAMLType.SubType:
|
||||
case "javascript", "json", "rss", "xml", "svg", "toml", "yml", "yaml":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func InitMediaType(m *Type) {
|
||||
m.init()
|
||||
}
|
||||
|
||||
func (m *Type) init() {
|
||||
m.FirstSuffix.FullSuffix = ""
|
||||
m.FirstSuffix.Suffix = ""
|
||||
@@ -204,13 +212,13 @@ func (m *Type) init() {
|
||||
// WithDelimiterAndSuffixes is used in tests.
|
||||
func WithDelimiterAndSuffixes(t Type, delimiter, suffixesCSV string) Type {
|
||||
t.Delimiter = delimiter
|
||||
t.suffixesCSV = suffixesCSV
|
||||
t.SuffixesCSV = suffixesCSV
|
||||
t.init()
|
||||
return t
|
||||
}
|
||||
|
||||
func newMediaType(main, sub string, suffixes []string) Type {
|
||||
t := Type{MainType: main, SubType: sub, suffixesCSV: strings.Join(suffixes, ","), Delimiter: defaultDelimiter}
|
||||
t := Type{MainType: main, SubType: sub, SuffixesCSV: strings.Join(suffixes, ","), Delimiter: DefaultDelimiter}
|
||||
t.init()
|
||||
return t
|
||||
}
|
||||
@@ -222,118 +230,18 @@ func newMediaTypeWithMimeSuffix(main, sub, mimeSuffix string, suffixes []string)
|
||||
return mt
|
||||
}
|
||||
|
||||
// Definitions from https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types etc.
|
||||
// Note that from Hugo 0.44 we only set Suffix if it is part of the MIME type.
|
||||
var (
|
||||
CalendarType = newMediaType("text", "calendar", []string{"ics"})
|
||||
CSSType = newMediaType("text", "css", []string{"css"})
|
||||
SCSSType = newMediaType("text", "x-scss", []string{"scss"})
|
||||
SASSType = newMediaType("text", "x-sass", []string{"sass"})
|
||||
CSVType = newMediaType("text", "csv", []string{"csv"})
|
||||
HTMLType = newMediaType("text", "html", []string{"html"})
|
||||
JavascriptType = newMediaType("text", "javascript", []string{"js", "jsm", "mjs"})
|
||||
TypeScriptType = newMediaType("text", "typescript", []string{"ts"})
|
||||
TSXType = newMediaType("text", "tsx", []string{"tsx"})
|
||||
JSXType = newMediaType("text", "jsx", []string{"jsx"})
|
||||
|
||||
JSONType = newMediaType("application", "json", []string{"json"})
|
||||
WebAppManifestType = newMediaTypeWithMimeSuffix("application", "manifest", "json", []string{"webmanifest"})
|
||||
RSSType = newMediaTypeWithMimeSuffix("application", "rss", "xml", []string{"xml", "rss"})
|
||||
XMLType = newMediaType("application", "xml", []string{"xml"})
|
||||
SVGType = newMediaTypeWithMimeSuffix("image", "svg", "xml", []string{"svg"})
|
||||
TextType = newMediaType("text", "plain", []string{"txt"})
|
||||
TOMLType = newMediaType("application", "toml", []string{"toml"})
|
||||
YAMLType = newMediaType("application", "yaml", []string{"yaml", "yml"})
|
||||
|
||||
// Common image types
|
||||
PNGType = newMediaType("image", "png", []string{"png"})
|
||||
JPEGType = newMediaType("image", "jpeg", []string{"jpg", "jpeg", "jpe", "jif", "jfif"})
|
||||
GIFType = newMediaType("image", "gif", []string{"gif"})
|
||||
TIFFType = newMediaType("image", "tiff", []string{"tif", "tiff"})
|
||||
BMPType = newMediaType("image", "bmp", []string{"bmp"})
|
||||
WEBPType = newMediaType("image", "webp", []string{"webp"})
|
||||
|
||||
// Common font types
|
||||
TrueTypeFontType = newMediaType("font", "ttf", []string{"ttf"})
|
||||
OpenTypeFontType = newMediaType("font", "otf", []string{"otf"})
|
||||
|
||||
// Common document types
|
||||
PDFType = newMediaType("application", "pdf", []string{"pdf"})
|
||||
MarkdownType = newMediaType("text", "markdown", []string{"md", "markdown"})
|
||||
|
||||
// Common video types
|
||||
AVIType = newMediaType("video", "x-msvideo", []string{"avi"})
|
||||
MPEGType = newMediaType("video", "mpeg", []string{"mpg", "mpeg"})
|
||||
MP4Type = newMediaType("video", "mp4", []string{"mp4"})
|
||||
OGGType = newMediaType("video", "ogg", []string{"ogv"})
|
||||
WEBMType = newMediaType("video", "webm", []string{"webm"})
|
||||
GPPType = newMediaType("video", "3gpp", []string{"3gpp", "3gp"})
|
||||
|
||||
OctetType = newMediaType("application", "octet-stream", nil)
|
||||
)
|
||||
|
||||
// DefaultTypes is the default media types supported by Hugo.
|
||||
var DefaultTypes = Types{
|
||||
CalendarType,
|
||||
CSSType,
|
||||
CSVType,
|
||||
SCSSType,
|
||||
SASSType,
|
||||
HTMLType,
|
||||
MarkdownType,
|
||||
JavascriptType,
|
||||
TypeScriptType,
|
||||
TSXType,
|
||||
JSXType,
|
||||
JSONType,
|
||||
WebAppManifestType,
|
||||
RSSType,
|
||||
XMLType,
|
||||
SVGType,
|
||||
TextType,
|
||||
OctetType,
|
||||
YAMLType,
|
||||
TOMLType,
|
||||
PNGType,
|
||||
GIFType,
|
||||
BMPType,
|
||||
JPEGType,
|
||||
WEBPType,
|
||||
AVIType,
|
||||
MPEGType,
|
||||
MP4Type,
|
||||
OGGType,
|
||||
WEBMType,
|
||||
GPPType,
|
||||
OpenTypeFontType,
|
||||
TrueTypeFontType,
|
||||
PDFType,
|
||||
}
|
||||
|
||||
func init() {
|
||||
sort.Sort(DefaultTypes)
|
||||
|
||||
// Sanity check.
|
||||
seen := make(map[Type]bool)
|
||||
for _, t := range DefaultTypes {
|
||||
if seen[t] {
|
||||
panic(fmt.Sprintf("MediaType %s duplicated in list", t))
|
||||
}
|
||||
seen[t] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Types is a slice of media types.
|
||||
// <docsmeta>{ "name": "MediaTypes" }</docsmeta>
|
||||
type Types []Type
|
||||
|
||||
func (t Types) Len() int { return len(t) }
|
||||
func (t Types) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||
func (t Types) Less(i, j int) bool { return t[i].Type() < t[j].Type() }
|
||||
func (t Types) Less(i, j int) bool { return t[i].Type < t[j].Type }
|
||||
|
||||
// GetByType returns a media type for tp.
|
||||
func (t Types) GetByType(tp string) (Type, bool) {
|
||||
for _, tt := range t {
|
||||
if strings.EqualFold(tt.Type(), tp) {
|
||||
if strings.EqualFold(tt.Type, tp) {
|
||||
return tt, true
|
||||
}
|
||||
}
|
||||
@@ -399,8 +307,19 @@ func (t Types) GetBySuffix(suffix string) (tp Type, si SuffixInfo, found bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t Types) IsTextSuffix(suffix string) bool {
|
||||
suffix = strings.ToLower(suffix)
|
||||
for _, tt := range t {
|
||||
if tt.hasSuffix(suffix) {
|
||||
return tt.IsText()
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
func (m Type) hasSuffix(suffix string) bool {
|
||||
return strings.Contains(","+m.suffixesCSV+",", ","+suffix+",")
|
||||
return strings.Contains(","+m.SuffixesCSV+",", ","+suffix+",")
|
||||
}
|
||||
|
||||
// GetByMainSubType gets a media type given a main and a sub type e.g. "text" and "plain".
|
||||
@@ -423,96 +342,6 @@ func (t Types) GetByMainSubType(mainType, subType string) (tp Type, found bool)
|
||||
return
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
Now you can specify multiple file suffixes using "suffixes", but you need to specify the full MIME type
|
||||
identifier:
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."image/svg+xml"]
|
||||
suffixes = ["svg", "abc" ]
|
||||
|
||||
In most cases, it will be enough to just change:
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."my/custom-mediatype"]
|
||||
suffix = "txt"
|
||||
|
||||
To:
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."my/custom-mediatype"]
|
||||
suffixes = ["txt"]
|
||||
|
||||
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.
|
||||
`)
|
||||
}
|
||||
|
||||
// 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(mms ...map[string]any) (Types, error) {
|
||||
var m Types
|
||||
|
||||
// Maps type string to Type. Type string is the full application/svg+xml.
|
||||
mmm := make(map[string]Type)
|
||||
for _, dt := range DefaultTypes {
|
||||
mmm[dt.Type()] = dt
|
||||
}
|
||||
|
||||
for _, mm := range mms {
|
||||
for k, v := range mm {
|
||||
var mediaType Type
|
||||
|
||||
mediaType, found := mmm[k]
|
||||
if !found {
|
||||
var err error
|
||||
mediaType, err = FromString(k)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := mapstructure.WeakDecode(v, &mediaType); err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
vm := maps.ToStringMap(v)
|
||||
maps.PrepareParams(vm)
|
||||
_, delimiterSet := vm["delimiter"]
|
||||
_, suffixSet := vm["suffix"]
|
||||
|
||||
if suffixSet {
|
||||
return Types{}, suffixIsRemoved()
|
||||
}
|
||||
|
||||
if suffixes, found := vm["suffixes"]; found {
|
||||
mediaType.suffixesCSV = strings.TrimSpace(strings.ToLower(strings.Join(cast.ToStringSlice(suffixes), ",")))
|
||||
}
|
||||
|
||||
// The user may set the delimiter as an empty string.
|
||||
if !delimiterSet && mediaType.suffixesCSV != "" {
|
||||
mediaType.Delimiter = defaultDelimiter
|
||||
}
|
||||
|
||||
mediaType.init()
|
||||
|
||||
mmm[k] = mediaType
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range mmm {
|
||||
m = append(m, v)
|
||||
}
|
||||
sort.Sort(m)
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// IsZero reports whether this Type represents a zero value.
|
||||
// For internal use.
|
||||
func (m Type) IsZero() bool {
|
||||
@@ -530,8 +359,8 @@ func (m Type) MarshalJSON() ([]byte, error) {
|
||||
Suffixes []string `json:"suffixes"`
|
||||
}{
|
||||
Alias: (Alias)(m),
|
||||
Type: m.Type(),
|
||||
Type: m.Type,
|
||||
String: m.String(),
|
||||
Suffixes: strings.Split(m.suffixesCSV, ","),
|
||||
Suffixes: strings.Split(m.SuffixesCSV, ","),
|
||||
})
|
||||
}
|
||||
|
@@ -25,73 +25,32 @@ import (
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
)
|
||||
|
||||
func TestDefaultTypes(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
for _, test := range []struct {
|
||||
tp Type
|
||||
expectedMainType string
|
||||
expectedSubType string
|
||||
expectedSuffix string
|
||||
expectedType string
|
||||
expectedString string
|
||||
}{
|
||||
{CalendarType, "text", "calendar", "ics", "text/calendar", "text/calendar"},
|
||||
{CSSType, "text", "css", "css", "text/css", "text/css"},
|
||||
{SCSSType, "text", "x-scss", "scss", "text/x-scss", "text/x-scss"},
|
||||
{CSVType, "text", "csv", "csv", "text/csv", "text/csv"},
|
||||
{HTMLType, "text", "html", "html", "text/html", "text/html"},
|
||||
{JavascriptType, "text", "javascript", "js", "text/javascript", "text/javascript"},
|
||||
{TypeScriptType, "text", "typescript", "ts", "text/typescript", "text/typescript"},
|
||||
{TSXType, "text", "tsx", "tsx", "text/tsx", "text/tsx"},
|
||||
{JSXType, "text", "jsx", "jsx", "text/jsx", "text/jsx"},
|
||||
{JSONType, "application", "json", "json", "application/json", "application/json"},
|
||||
{RSSType, "application", "rss", "xml", "application/rss+xml", "application/rss+xml"},
|
||||
{SVGType, "image", "svg", "svg", "image/svg+xml", "image/svg+xml"},
|
||||
{TextType, "text", "plain", "txt", "text/plain", "text/plain"},
|
||||
{XMLType, "application", "xml", "xml", "application/xml", "application/xml"},
|
||||
{TOMLType, "application", "toml", "toml", "application/toml", "application/toml"},
|
||||
{YAMLType, "application", "yaml", "yaml", "application/yaml", "application/yaml"},
|
||||
{PDFType, "application", "pdf", "pdf", "application/pdf", "application/pdf"},
|
||||
{TrueTypeFontType, "font", "ttf", "ttf", "font/ttf", "font/ttf"},
|
||||
{OpenTypeFontType, "font", "otf", "otf", "font/otf", "font/otf"},
|
||||
} {
|
||||
c.Assert(test.tp.MainType, qt.Equals, test.expectedMainType)
|
||||
c.Assert(test.tp.SubType, qt.Equals, test.expectedSubType)
|
||||
|
||||
c.Assert(test.tp.Type(), qt.Equals, test.expectedType)
|
||||
c.Assert(test.tp.String(), qt.Equals, test.expectedString)
|
||||
|
||||
}
|
||||
|
||||
c.Assert(len(DefaultTypes), qt.Equals, 34)
|
||||
}
|
||||
|
||||
func TestGetByType(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
types := Types{HTMLType, RSSType}
|
||||
types := DefaultTypes
|
||||
|
||||
mt, found := types.GetByType("text/HTML")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(HTMLType, qt.Equals, mt)
|
||||
c.Assert(mt.SubType, qt.Equals, "html")
|
||||
|
||||
_, found = types.GetByType("text/nono")
|
||||
c.Assert(found, qt.Equals, false)
|
||||
|
||||
mt, found = types.GetByType("application/rss+xml")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(RSSType, qt.Equals, mt)
|
||||
c.Assert(mt.SubType, qt.Equals, "rss")
|
||||
|
||||
mt, found = types.GetByType("application/rss")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(RSSType, qt.Equals, mt)
|
||||
c.Assert(mt.SubType, qt.Equals, "rss")
|
||||
}
|
||||
|
||||
func TestGetByMainSubType(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
f, found := DefaultTypes.GetByMainSubType("text", "plain")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(f, qt.Equals, TextType)
|
||||
c.Assert(f.SubType, qt.Equals, "plain")
|
||||
_, found = DefaultTypes.GetByMainSubType("foo", "plain")
|
||||
c.Assert(found, qt.Equals, false)
|
||||
}
|
||||
@@ -107,7 +66,8 @@ func TestBySuffix(t *testing.T) {
|
||||
func TestGetFirstBySuffix(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
types := DefaultTypes
|
||||
types := make(Types, len(DefaultTypes))
|
||||
copy(types, DefaultTypes)
|
||||
|
||||
// Issue #8406
|
||||
geoJSON := newMediaTypeWithMimeSuffix("application", "geo", "json", []string{"geojson", "gjson"})
|
||||
@@ -124,8 +84,8 @@ func TestGetFirstBySuffix(t *testing.T) {
|
||||
c.Assert(t, qt.Equals, expectedType)
|
||||
}
|
||||
|
||||
check("js", JavascriptType)
|
||||
check("json", JSONType)
|
||||
check("js", Builtin.JavascriptType)
|
||||
check("json", Builtin.JSONType)
|
||||
check("geojson", geoJSON)
|
||||
check("gjson", geoJSON)
|
||||
}
|
||||
@@ -134,15 +94,15 @@ func TestFromTypeString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
f, err := FromString("text/html")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f.Type(), qt.Equals, HTMLType.Type())
|
||||
c.Assert(f.Type, qt.Equals, Builtin.HTMLType.Type)
|
||||
|
||||
f, err = FromString("application/custom")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f, qt.Equals, Type{MainType: "application", SubType: "custom", mimeSuffix: ""})
|
||||
c.Assert(f, qt.Equals, Type{Type: "application/custom", MainType: "application", SubType: "custom", mimeSuffix: ""})
|
||||
|
||||
f, err = FromString("application/custom+sfx")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f, qt.Equals, Type{MainType: "application", SubType: "custom", mimeSuffix: "sfx"})
|
||||
c.Assert(f, qt.Equals, Type{Type: "application/custom+sfx", MainType: "application", SubType: "custom", mimeSuffix: "sfx"})
|
||||
|
||||
_, err = FromString("noslash")
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
@@ -150,17 +110,17 @@ func TestFromTypeString(t *testing.T) {
|
||||
f, err = FromString("text/xml; charset=utf-8")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
c.Assert(f, qt.Equals, Type{MainType: "text", SubType: "xml", mimeSuffix: ""})
|
||||
c.Assert(f, qt.Equals, Type{Type: "text/xml", MainType: "text", SubType: "xml", mimeSuffix: ""})
|
||||
}
|
||||
|
||||
func TestFromStringAndExt(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
f, err := FromStringAndExt("text/html", "html")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f, qt.Equals, HTMLType)
|
||||
c.Assert(f, qt.Equals, Builtin.HTMLType)
|
||||
f, err = FromStringAndExt("text/html", ".html")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(f, qt.Equals, HTMLType)
|
||||
c.Assert(f, qt.Equals, Builtin.HTMLType)
|
||||
}
|
||||
|
||||
// Add a test for the SVG case
|
||||
@@ -185,7 +145,6 @@ func TestFromContent(t *testing.T) {
|
||||
|
||||
files, err := filepath.Glob("./testdata/resource.*")
|
||||
c.Assert(err, qt.IsNil)
|
||||
mtypes := DefaultTypes
|
||||
|
||||
for _, filename := range files {
|
||||
name := filepath.Base(filename)
|
||||
@@ -199,9 +158,9 @@ func TestFromContent(t *testing.T) {
|
||||
} else {
|
||||
exts = []string{ext}
|
||||
}
|
||||
expected, _, found := mtypes.GetFirstBySuffix(ext)
|
||||
expected, _, found := DefaultTypes.GetFirstBySuffix(ext)
|
||||
c.Assert(found, qt.IsTrue)
|
||||
got := FromContent(mtypes, exts, content)
|
||||
got := FromContent(DefaultTypes, exts, content)
|
||||
c.Assert(got, qt.Equals, expected)
|
||||
})
|
||||
}
|
||||
@@ -212,7 +171,6 @@ func TestFromContentFakes(t *testing.T) {
|
||||
|
||||
files, err := filepath.Glob("./testdata/fake.*")
|
||||
c.Assert(err, qt.IsNil)
|
||||
mtypes := DefaultTypes
|
||||
|
||||
for _, filename := range files {
|
||||
name := filepath.Base(filename)
|
||||
@@ -220,109 +178,21 @@ func TestFromContentFakes(t *testing.T) {
|
||||
content, err := os.ReadFile(filename)
|
||||
c.Assert(err, qt.IsNil)
|
||||
ext := strings.TrimPrefix(paths.Ext(filename), ".")
|
||||
got := FromContent(mtypes, []string{ext}, content)
|
||||
got := FromContent(DefaultTypes, []string{ext}, content)
|
||||
c.Assert(got, qt.Equals, zero)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeTypes(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
maps []map[string]any
|
||||
shouldError bool
|
||||
assert func(t *testing.T, name string, tt Types)
|
||||
}{
|
||||
{
|
||||
"Redefine JSON",
|
||||
[]map[string]any{
|
||||
{
|
||||
"application/json": map[string]any{
|
||||
"suffixes": []string{"jasn"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
func(t *testing.T, name string, tt Types) {
|
||||
c.Assert(len(tt), qt.Equals, len(DefaultTypes))
|
||||
json, si, found := tt.GetBySuffix("jasn")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(json.String(), qt.Equals, "application/json")
|
||||
c.Assert(si.FullSuffix, qt.Equals, ".jasn")
|
||||
},
|
||||
},
|
||||
{
|
||||
"MIME suffix in key, multiple file suffixes, custom delimiter",
|
||||
[]map[string]any{
|
||||
{
|
||||
"application/hugo+hg": map[string]any{
|
||||
"suffixes": []string{"hg1", "hG2"},
|
||||
"Delimiter": "_",
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
func(t *testing.T, name string, tt Types) {
|
||||
c.Assert(len(tt), qt.Equals, len(DefaultTypes)+1)
|
||||
hg, si, found := tt.GetBySuffix("hg2")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(hg.mimeSuffix, qt.Equals, "hg")
|
||||
c.Assert(hg.FirstSuffix.Suffix, qt.Equals, "hg1")
|
||||
c.Assert(hg.FirstSuffix.FullSuffix, qt.Equals, "_hg1")
|
||||
c.Assert(si.Suffix, qt.Equals, "hg2")
|
||||
c.Assert(si.FullSuffix, qt.Equals, "_hg2")
|
||||
c.Assert(hg.String(), qt.Equals, "application/hugo+hg")
|
||||
|
||||
_, found = tt.GetByType("application/hugo+hg")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Add custom media type",
|
||||
[]map[string]any{
|
||||
{
|
||||
"text/hugo+hgo": map[string]any{
|
||||
"Suffixes": []string{"hgo2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
func(t *testing.T, name string, tp Types) {
|
||||
c.Assert(len(tp), qt.Equals, len(DefaultTypes)+1)
|
||||
// Make sure we have not broken the default config.
|
||||
|
||||
_, _, found := tp.GetBySuffix("json")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
|
||||
hugo, _, found := tp.GetBySuffix("hgo2")
|
||||
c.Assert(found, qt.Equals, true)
|
||||
c.Assert(hugo.String(), qt.Equals, "text/hugo+hgo")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result, err := DecodeTypes(test.maps...)
|
||||
if test.shouldError {
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
} else {
|
||||
c.Assert(err, qt.IsNil)
|
||||
test.assert(t, test.name, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJSON(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
b, err := json.Marshal(MPEGType)
|
||||
b, err := json.Marshal(Builtin.MPEGType)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(string(b), qt.Equals, `{"mainType":"video","subType":"mpeg","delimiter":".","firstSuffix":{"suffix":"mpg","fullSuffix":".mpg"},"type":"video/mpeg","string":"video/mpeg","suffixes":["mpg","mpeg"]}`)
|
||||
c.Assert(string(b), qt.Equals, `{"mainType":"video","subType":"mpeg","delimiter":".","type":"video/mpeg","string":"video/mpeg","suffixes":["mpg","mpeg"]}`)
|
||||
}
|
||||
|
||||
func BenchmarkTypeOps(b *testing.B) {
|
||||
mt := MPEGType
|
||||
mt := Builtin.MPEGType
|
||||
mts := DefaultTypes
|
||||
for i := 0; i < b.N; i++ {
|
||||
ff := mt.FirstSuffix
|
||||
@@ -335,7 +205,7 @@ func BenchmarkTypeOps(b *testing.B) {
|
||||
_ = mt.String()
|
||||
_ = ff.Suffix
|
||||
_ = mt.Suffixes
|
||||
_ = mt.Type()
|
||||
_ = mt.Type
|
||||
_ = mts.BySuffix("xml")
|
||||
_, _ = mts.GetByMainSubType("application", "xml")
|
||||
_, _, _ = mts.GetBySuffix("xml")
|
||||
|
Reference in New Issue
Block a user