aboutsummaryrefslogtreecommitdiffhomepage
path: root/output
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2023-01-04 18:24:36 +0100
committerBjørn Erik Pedersen <[email protected]>2023-05-16 18:01:29 +0200
commit241b21b0fd34d91fccb2ce69874110dceae6f926 (patch)
treed4e0118eac7e9c42f065815447a70805f8d6ad3e /output
parent6aededf6b42011c3039f5f66487a89a8dd65e0e7 (diff)
downloadhugo-241b21b0fd34d91fccb2ce69874110dceae6f926.tar.gz
hugo-241b21b0fd34d91fccb2ce69874110dceae6f926.zip
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
Diffstat (limited to 'output')
-rw-r--r--output/config.go147
-rw-r--r--output/config_test.go98
-rw-r--r--output/docshelper.go58
-rw-r--r--output/layouts/layout.go (renamed from output/layout.go)62
-rw-r--r--output/layouts/layout_test.go (renamed from output/layout_test.go)179
-rw-r--r--output/outputFormat.go157
-rw-r--r--output/outputFormat_test.go145
7 files changed, 436 insertions, 410 deletions
diff --git a/output/config.go b/output/config.go
new file mode 100644
index 000000000..7b83ef9de
--- /dev/null
+++ b/output/config.go
@@ -0,0 +1,147 @@
+// 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 output
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/gohugoio/hugo/common/maps"
+ "github.com/gohugoio/hugo/config"
+ "github.com/gohugoio/hugo/media"
+ "github.com/mitchellh/mapstructure"
+)
+
+// OutputFormatConfig configures a single output format.
+type OutputFormatConfig struct {
+ // The MediaType string. This must be a configured media type.
+ MediaType string
+ Format
+}
+
+func DecodeConfig(mediaTypes media.Types, in any) (*config.ConfigNamespace[map[string]OutputFormatConfig, Formats], error) {
+ buildConfig := func(in any) (Formats, any, error) {
+ f := make(Formats, len(DefaultFormats))
+ copy(f, DefaultFormats)
+ if in != nil {
+ m, err := maps.ToStringMapE(in)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed convert config to map: %s", err)
+ }
+ m = maps.CleanConfigStringMap(m)
+
+ for k, v := range m {
+ found := false
+ for i, vv := range f {
+ // Both are lower case.
+ if k == vv.Name {
+ // Merge it with the existing
+ if err := decode(mediaTypes, v, &f[i]); err != nil {
+ return f, nil, err
+ }
+ found = true
+ }
+ }
+ if found {
+ continue
+ }
+
+ var newOutFormat Format
+ newOutFormat.Name = k
+ if err := decode(mediaTypes, v, &newOutFormat); err != nil {
+ return f, nil, err
+ }
+
+ // We need values for these
+ if newOutFormat.BaseName == "" {
+ newOutFormat.BaseName = "index"
+ }
+ if newOutFormat.Rel == "" {
+ newOutFormat.Rel = "alternate"
+ }
+
+ f = append(f, newOutFormat)
+
+ }
+ }
+
+ // Also format is a map for documentation purposes.
+ docm := make(map[string]OutputFormatConfig, len(f))
+ for _, ff := range f {
+ docm[ff.Name] = OutputFormatConfig{
+ MediaType: ff.MediaType.Type,
+ Format: ff,
+ }
+ }
+
+ sort.Sort(f)
+ return f, docm, nil
+ }
+
+ return config.DecodeNamespace[map[string]OutputFormatConfig](in, buildConfig)
+}
+
+func decode(mediaTypes media.Types, input any, output *Format) error {
+ config := &mapstructure.DecoderConfig{
+ Metadata: nil,
+ Result: output,
+ WeaklyTypedInput: true,
+ DecodeHook: func(a reflect.Type, b reflect.Type, c any) (any, error) {
+ if a.Kind() == reflect.Map {
+ dataVal := reflect.Indirect(reflect.ValueOf(c))
+ for _, key := range dataVal.MapKeys() {
+ keyStr, ok := key.Interface().(string)
+ if !ok {
+ // Not a string key
+ continue
+ }
+ if strings.EqualFold(keyStr, "mediaType") {
+ // If mediaType is a string, look it up and replace it
+ // in the map.
+ vv := dataVal.MapIndex(key)
+ vvi := vv.Interface()
+
+ switch vviv := vvi.(type) {
+ case media.Type:
+ // OK
+ case string:
+ mediaType, found := mediaTypes.GetByType(vviv)
+ if !found {
+ return c, fmt.Errorf("media type %q not found", vviv)
+ }
+ dataVal.SetMapIndex(key, reflect.ValueOf(mediaType))
+ default:
+ return nil, fmt.Errorf("invalid output format configuration; wrong type for media type, expected string (e.g. text/html), got %T", vvi)
+ }
+ }
+ }
+ }
+ return c, nil
+ },
+ }
+
+ decoder, err := mapstructure.NewDecoder(config)
+ if err != nil {
+ return err
+ }
+
+ if err = decoder.Decode(input); err != nil {
+ return fmt.Errorf("failed to decode output format configuration: %w", err)
+ }
+
+ return nil
+
+}
diff --git a/output/config_test.go b/output/config_test.go
new file mode 100644
index 000000000..52381c5d2
--- /dev/null
+++ b/output/config_test.go
@@ -0,0 +1,98 @@
+// 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 output
+
+import (
+ "testing"
+
+ qt "github.com/frankban/quicktest"
+ "github.com/gohugoio/hugo/media"
+)
+
+func TestDecodeConfig(t *testing.T) {
+ c := qt.New(t)
+
+ mediaTypes := media.Types{media.Builtin.JSONType, media.Builtin.XMLType}
+
+ tests := []struct {
+ name string
+ m map[string]any
+ shouldError bool
+ assert func(t *testing.T, name string, f Formats)
+ }{
+ {
+ "Redefine JSON",
+ map[string]any{
+ "json": map[string]any{
+ "baseName": "myindex",
+ "isPlainText": "false",
+ },
+ },
+ false,
+ func(t *testing.T, name string, f Formats) {
+ msg := qt.Commentf(name)
+ c.Assert(len(f), qt.Equals, len(DefaultFormats), msg)
+ json, _ := f.GetByName("JSON")
+ c.Assert(json.BaseName, qt.Equals, "myindex")
+ c.Assert(json.MediaType, qt.Equals, media.Builtin.JSONType)
+ c.Assert(json.IsPlainText, qt.Equals, false)
+ },
+ },
+ {
+ "Add XML format with string as mediatype",
+ map[string]any{
+ "MYXMLFORMAT": map[string]any{
+ "baseName": "myxml",
+ "mediaType": "application/xml",
+ },
+ },
+ false,
+ func(t *testing.T, name string, f Formats) {
+ c.Assert(len(f), qt.Equals, len(DefaultFormats)+1)
+ xml, found := f.GetByName("MYXMLFORMAT")
+ c.Assert(found, qt.Equals, true)
+ c.Assert(xml.BaseName, qt.Equals, "myxml")
+ c.Assert(xml.MediaType, qt.Equals, media.Builtin.XMLType)
+
+ // Verify that we haven't changed the DefaultFormats slice.
+ json, _ := f.GetByName("JSON")
+ c.Assert(json.BaseName, qt.Equals, "index")
+ },
+ },
+ {
+ "Add format unknown mediatype",
+ map[string]any{
+ "MYINVALID": map[string]any{
+ "baseName": "mymy",
+ "mediaType": "application/hugo",
+ },
+ },
+ true,
+ func(t *testing.T, name string, f Formats) {
+ },
+ },
+ }
+
+ for _, test := range tests {
+ result, err := DecodeConfig(mediaTypes, test.m)
+ msg := qt.Commentf(test.name)
+
+ if test.shouldError {
+ c.Assert(err, qt.Not(qt.IsNil), msg)
+ } else {
+ c.Assert(err, qt.IsNil, msg)
+ test.assert(t, test.name, result.Config)
+ }
+ }
+}
diff --git a/output/docshelper.go b/output/docshelper.go
index abfedd148..fa8ed1342 100644
--- a/output/docshelper.go
+++ b/output/docshelper.go
@@ -6,6 +6,7 @@ import (
// "fmt"
"github.com/gohugoio/hugo/docshelper"
+ "github.com/gohugoio/hugo/output/layouts"
)
// This is is just some helpers used to create some JSON used in the Hugo docs.
@@ -39,44 +40,43 @@ func createLayoutExamples() any {
for _, example := range []struct {
name string
- d LayoutDescriptor
- f Format
+ d layouts.LayoutDescriptor
}{
- // Taxonomy output.LayoutDescriptor={categories category taxonomy en false Type Section
- {"Single page in \"posts\" section", LayoutDescriptor{Kind: "page", Type: "posts"}, HTMLFormat},
- {"Base template for single page in \"posts\" section", LayoutDescriptor{Baseof: true, Kind: "page", Type: "posts"}, HTMLFormat},
- {"Single page in \"posts\" section with layout set", LayoutDescriptor{Kind: "page", Type: "posts", Layout: demoLayout}, HTMLFormat},
- {"Base template for single page in \"posts\" section with layout set", LayoutDescriptor{Baseof: true, Kind: "page", Type: "posts", Layout: demoLayout}, HTMLFormat},
- {"AMP single page", LayoutDescriptor{Kind: "page", Type: "posts"}, AMPFormat},
- {"AMP single page, French language", LayoutDescriptor{Kind: "page", Type: "posts", Lang: "fr"}, AMPFormat},
+ // Taxonomy layouts.LayoutDescriptor={categories category taxonomy en false Type Section
+ {"Single page in \"posts\" section", layouts.LayoutDescriptor{Kind: "page", Type: "posts", OutputFormatName: "html", Suffix: "html"}},
+ {"Base template for single page in \"posts\" section", layouts.LayoutDescriptor{Baseof: true, Kind: "page", Type: "posts", OutputFormatName: "html", Suffix: "html"}},
+ {"Single page in \"posts\" section with layout set", layouts.LayoutDescriptor{Kind: "page", Type: "posts", Layout: demoLayout, OutputFormatName: "html", Suffix: "html"}},
+ {"Base template for single page in \"posts\" section with layout set", layouts.LayoutDescriptor{Baseof: true, Kind: "page", Type: "posts", Layout: demoLayout, OutputFormatName: "html", Suffix: "html"}},
+ {"AMP single page", layouts.LayoutDescriptor{Kind: "page", Type: "posts", OutputFormatName: "amp", Suffix: "html"}},
+ {"AMP single page, French language", layouts.LayoutDescriptor{Kind: "page", Type: "posts", Lang: "fr", OutputFormatName: "html", Suffix: "html"}},
// All section or typeless pages gets "page" as type
- {"Home page", LayoutDescriptor{Kind: "home", Type: "page"}, HTMLFormat},
- {"Base template for home page", LayoutDescriptor{Baseof: true, Kind: "home", Type: "page"}, HTMLFormat},
- {"Home page with type set", LayoutDescriptor{Kind: "home", Type: demoType}, HTMLFormat},
- {"Base template for home page with type set", LayoutDescriptor{Baseof: true, Kind: "home", Type: demoType}, HTMLFormat},
- {"Home page with layout set", LayoutDescriptor{Kind: "home", Type: "page", Layout: demoLayout}, HTMLFormat},
- {"AMP home, French language", LayoutDescriptor{Kind: "home", Type: "page", Lang: "fr"}, AMPFormat},
- {"JSON home", LayoutDescriptor{Kind: "home", Type: "page"}, JSONFormat},
- {"RSS home", LayoutDescriptor{Kind: "home", Type: "page"}, RSSFormat},
- {"RSS section posts", LayoutDescriptor{Kind: "section", Type: "posts"}, RSSFormat},
- {"Taxonomy in categories", LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category"}, RSSFormat},
- {"Term in categories", LayoutDescriptor{Kind: "term", Type: "categories", Section: "category"}, RSSFormat},
- {"Section list for \"posts\" section", LayoutDescriptor{Kind: "section", Type: "posts", Section: "posts"}, HTMLFormat},
- {"Section list for \"posts\" section with type set to \"blog\"", LayoutDescriptor{Kind: "section", Type: "blog", Section: "posts"}, HTMLFormat},
- {"Section list for \"posts\" section with layout set to \"demoLayout\"", LayoutDescriptor{Kind: "section", Layout: demoLayout, Section: "posts"}, HTMLFormat},
+ {"Home page", layouts.LayoutDescriptor{Kind: "home", Type: "page", OutputFormatName: "html", Suffix: "html"}},
+ {"Base template for home page", layouts.LayoutDescriptor{Baseof: true, Kind: "home", Type: "page", OutputFormatName: "html", Suffix: "html"}},
+ {"Home page with type set", layouts.LayoutDescriptor{Kind: "home", Type: demoType, OutputFormatName: "html", Suffix: "html"}},
+ {"Base template for home page with type set", layouts.LayoutDescriptor{Baseof: true, Kind: "home", Type: demoType, OutputFormatName: "html", Suffix: "html"}},
+ {"Home page with layout set", layouts.LayoutDescriptor{Kind: "home", Type: "page", Layout: demoLayout, OutputFormatName: "html", Suffix: "html"}},
+ {"AMP home, French language", layouts.LayoutDescriptor{Kind: "home", Type: "page", Lang: "fr", OutputFormatName: "amp", Suffix: "html"}},
+ {"JSON home", layouts.LayoutDescriptor{Kind: "home", Type: "page", OutputFormatName: "json", Suffix: "json"}},
+ {"RSS home", layouts.LayoutDescriptor{Kind: "home", Type: "page", OutputFormatName: "rss", Suffix: "rss"}},
+ {"RSS section posts", layouts.LayoutDescriptor{Kind: "section", Type: "posts", OutputFormatName: "rss", Suffix: "rss"}},
+ {"Taxonomy in categories", layouts.LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category", OutputFormatName: "rss", Suffix: "rss"}},
+ {"Term in categories", layouts.LayoutDescriptor{Kind: "term", Type: "categories", Section: "category", OutputFormatName: "rss", Suffix: "rss"}},
+ {"Section list for \"posts\" section", layouts.LayoutDescriptor{Kind: "section", Type: "posts", Section: "posts", OutputFormatName: "html", Suffix: "html"}},
+ {"Section list for \"posts\" section with type set to \"blog\"", layouts.LayoutDescriptor{Kind: "section", Type: "blog", Section: "posts", OutputFormatName: "html", Suffix: "html"}},
+ {"Section list for \"posts\" section with layout set to \"demoLayout\"", layouts.LayoutDescriptor{Kind: "section", Layout: demoLayout, Section: "posts", OutputFormatName: "html", Suffix: "html"}},
- {"Taxonomy list in categories", LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category"}, HTMLFormat},
- {"Taxonomy term in categories", LayoutDescriptor{Kind: "term", Type: "categories", Section: "category"}, HTMLFormat},
+ {"Taxonomy list in categories", layouts.LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category", OutputFormatName: "html", Suffix: "html"}},
+ {"Taxonomy term in categories", layouts.LayoutDescriptor{Kind: "term", Type: "categories", Section: "category", OutputFormatName: "html", Suffix: "html"}},
} {
- l := NewLayoutHandler()
- layouts, _ := l.For(example.d, example.f)
+ l := layouts.NewLayoutHandler()
+ layouts, _ := l.For(example.d)
basicExamples = append(basicExamples, Example{
Example: example.name,
Kind: example.d.Kind,
- OutputFormat: example.f.Name,
- Suffix: example.f.MediaType.FirstSuffix.Suffix,
+ OutputFormat: example.d.OutputFormatName,
+ Suffix: example.d.Suffix,
Layouts: makeLayoutsPresentable(layouts),
})
}
diff --git a/output/layout.go b/output/layouts/layout.go
index dcbdf461a..9c5ef17a1 100644
--- a/output/layout.go
+++ b/output/layouts/layout.go
@@ -1,4 +1,4 @@
-// Copyright 2017-present The Hugo Authors. All rights reserved.
+// 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.
@@ -11,13 +11,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package output
+package layouts
import (
"strings"
"sync"
-
- "github.com/gohugoio/hugo/helpers"
)
// These may be used as content sections with potential conflicts. Avoid that.
@@ -43,6 +41,10 @@ type LayoutDescriptor struct {
// LayoutOverride indicates what we should only look for the above layout.
LayoutOverride bool
+ // From OutputFormat and MediaType.
+ OutputFormatName string
+ Suffix string
+
RenderingHook bool
Baseof bool
}
@@ -54,37 +56,31 @@ func (d LayoutDescriptor) isList() bool {
// LayoutHandler calculates the layout template to use to render a given output type.
type LayoutHandler struct {
mu sync.RWMutex
- cache map[layoutCacheKey][]string
-}
-
-type layoutCacheKey struct {
- d LayoutDescriptor
- f string
+ cache map[LayoutDescriptor][]string
}
// NewLayoutHandler creates a new LayoutHandler.
func NewLayoutHandler() *LayoutHandler {
- return &LayoutHandler{cache: make(map[layoutCacheKey][]string)}
+ return &LayoutHandler{cache: make(map[LayoutDescriptor][]string)}
}
// For returns a layout for the given LayoutDescriptor and options.
// Layouts are rendered and cached internally.
-func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) {
+func (l *LayoutHandler) For(d LayoutDescriptor) ([]string, error) {
// We will get lots of requests for the same layouts, so avoid recalculations.
- key := layoutCacheKey{d, f.Name}
l.mu.RLock()
- if cacheVal, found := l.cache[key]; found {
+ if cacheVal, found := l.cache[d]; found {
l.mu.RUnlock()
return cacheVal, nil
}
l.mu.RUnlock()
- layouts := resolvePageTemplate(d, f)
+ layouts := resolvePageTemplate(d)
- layouts = helpers.UniqueStringsReuse(layouts)
+ layouts = uniqueStringsReuse(layouts)
l.mu.Lock()
- l.cache[key] = layouts
+ l.cache[d] = layouts
l.mu.Unlock()
return layouts, nil
@@ -94,7 +90,7 @@ type layoutBuilder struct {
layoutVariations []string
typeVariations []string
d LayoutDescriptor
- f Format
+ //f Format
}
func (l *layoutBuilder) addLayoutVariations(vars ...string) {
@@ -134,8 +130,8 @@ func (l *layoutBuilder) addKind() {
const renderingHookRoot = "/_markup"
-func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
- b := &layoutBuilder{d: d, f: f}
+func resolvePageTemplate(d LayoutDescriptor) []string {
+ b := &layoutBuilder{d: d}
if !d.RenderingHook && d.Layout != "" {
b.addLayoutVariations(d.Layout)
@@ -190,7 +186,7 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
b.addTypeVariations("")
}
- isRSS := f.Name == RSSFormat.Name
+ isRSS := strings.EqualFold(d.OutputFormatName, "rss")
if !d.RenderingHook && !d.Baseof && isRSS {
// The historic and common rss.xml case
b.addLayoutVariations("")
@@ -223,7 +219,7 @@ func (l *layoutBuilder) resolveVariations() []string {
var layouts []string
var variations []string
- name := strings.ToLower(l.f.Name)
+ name := strings.ToLower(l.d.OutputFormatName)
if l.d.Lang != "" {
// We prefer the most specific type before language.
@@ -241,7 +237,7 @@ func (l *layoutBuilder) resolveVariations() []string {
continue
}
- s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.FirstSuffix.Suffix)
+ s := constructLayoutPath(typeVar, layoutVar, variation, l.d.Suffix)
if s != "" {
layouts = append(layouts, s)
}
@@ -300,3 +296,23 @@ func constructLayoutPath(typ, layout, variations, extension string) string {
return p.String()
}
+
+// Inline this here so we can use tinygo to compile a wasm binary of this package.
+func uniqueStringsReuse(s []string) []string {
+ result := s[:0]
+ for i, val := range s {
+ var seen bool
+
+ for j := 0; j < i; j++ {
+ if s[j] == val {
+ seen = true
+ break
+ }
+ }
+
+ if !seen {
+ result = append(result, val)
+ }
+ }
+ return result
+}
diff --git a/output/layout_test.go b/output/layouts/layout_test.go
index 8b7a2b541..2f340f238 100644
--- a/output/layout_test.go
+++ b/output/layouts/layout_test.go
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package output
+package layouts
import (
"fmt"
@@ -19,8 +19,6 @@ import (
"strings"
"testing"
- "github.com/gohugoio/hugo/media"
-
qt "github.com/frankban/quicktest"
"github.com/kylelemons/godebug/diff"
)
@@ -28,42 +26,16 @@ import (
func TestLayout(t *testing.T) {
c := qt.New(t)
- noExtNoDelimMediaType := media.WithDelimiterAndSuffixes(media.TextType, "", "")
- noExtMediaType := media.WithDelimiterAndSuffixes(media.TextType, ".", "")
-
- var (
- ampType = Format{
- Name: "AMP",
- MediaType: media.HTMLType,
- BaseName: "index",
- }
-
- htmlFormat = HTMLFormat
-
- noExtDelimFormat = Format{
- Name: "NEM",
- MediaType: noExtNoDelimMediaType,
- BaseName: "_redirects",
- }
-
- noExt = Format{
- Name: "NEX",
- MediaType: noExtMediaType,
- BaseName: "next",
- }
- )
-
for _, this := range []struct {
name string
layoutDescriptor LayoutDescriptor
layoutOverride string
- format Format
expect []string
}{
{
"Home",
- LayoutDescriptor{Kind: "home"},
- "", ampType,
+ LayoutDescriptor{Kind: "home", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"index.amp.html",
"home.amp.html",
@@ -81,8 +53,8 @@ func TestLayout(t *testing.T) {
},
{
"Home baseof",
- LayoutDescriptor{Kind: "home", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "home", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"index-baseof.amp.html",
"home-baseof.amp.html",
@@ -104,8 +76,8 @@ func TestLayout(t *testing.T) {
},
{
"Home, HTML",
- LayoutDescriptor{Kind: "home"},
- "", htmlFormat,
+ LayoutDescriptor{Kind: "home", OutputFormatName: "html", Suffix: "html"},
+ "",
// We will eventually get to index.html. This looks stuttery, but makes the lookup logic easy to understand.
[]string{
"index.html.html",
@@ -124,8 +96,8 @@ func TestLayout(t *testing.T) {
},
{
"Home, HTML, baseof",
- LayoutDescriptor{Kind: "home", Baseof: true},
- "", htmlFormat,
+ LayoutDescriptor{Kind: "home", Baseof: true, OutputFormatName: "html", Suffix: "html"},
+ "",
[]string{
"index-baseof.html.html",
"home-baseof.html.html",
@@ -147,8 +119,8 @@ func TestLayout(t *testing.T) {
},
{
"Home, french language",
- LayoutDescriptor{Kind: "home", Lang: "fr"},
- "", ampType,
+ LayoutDescriptor{Kind: "home", Lang: "fr", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"index.fr.amp.html",
"home.fr.amp.html",
@@ -178,8 +150,8 @@ func TestLayout(t *testing.T) {
},
{
"Home, no ext or delim",
- LayoutDescriptor{Kind: "home"},
- "", noExtDelimFormat,
+ LayoutDescriptor{Kind: "home", OutputFormatName: "nem", Suffix: ""},
+ "",
[]string{
"index.nem",
"home.nem",
@@ -191,8 +163,8 @@ func TestLayout(t *testing.T) {
},
{
"Home, no ext",
- LayoutDescriptor{Kind: "home"},
- "", noExt,
+ LayoutDescriptor{Kind: "home", OutputFormatName: "nex", Suffix: ""},
+ "",
[]string{
"index.nex",
"home.nex",
@@ -204,14 +176,14 @@ func TestLayout(t *testing.T) {
},
{
"Page, no ext or delim",
- LayoutDescriptor{Kind: "page"},
- "", noExtDelimFormat,
+ LayoutDescriptor{Kind: "page", OutputFormatName: "nem", Suffix: ""},
+ "",
[]string{"_default/single.nem"},
},
{
"Section",
- LayoutDescriptor{Kind: "section", Section: "sect1"},
- "", ampType,
+ LayoutDescriptor{Kind: "section", Section: "sect1", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"sect1/sect1.amp.html",
"sect1/section.amp.html",
@@ -235,8 +207,8 @@ func TestLayout(t *testing.T) {
},
{
"Section, baseof",
- LayoutDescriptor{Kind: "section", Section: "sect1", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "section", Section: "sect1", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"sect1/sect1-baseof.amp.html",
"sect1/section-baseof.amp.html",
@@ -266,8 +238,8 @@ func TestLayout(t *testing.T) {
},
{
"Section, baseof, French, AMP",
- LayoutDescriptor{Kind: "section", Section: "sect1", Lang: "fr", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "section", Section: "sect1", Lang: "fr", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"sect1/sect1-baseof.fr.amp.html",
"sect1/section-baseof.fr.amp.html",
@@ -321,8 +293,8 @@ func TestLayout(t *testing.T) {
},
{
"Section with layout",
- LayoutDescriptor{Kind: "section", Section: "sect1", Layout: "mylayout"},
- "", ampType,
+ LayoutDescriptor{Kind: "section", Section: "sect1", Layout: "mylayout", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"sect1/mylayout.amp.html",
"sect1/sect1.amp.html",
@@ -352,8 +324,8 @@ func TestLayout(t *testing.T) {
},
{
"Term, French, AMP",
- LayoutDescriptor{Kind: "term", Section: "tags", Lang: "fr"},
- "", ampType,
+ LayoutDescriptor{Kind: "term", Section: "tags", Lang: "fr", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"term/term.fr.amp.html",
"term/tags.fr.amp.html",
@@ -423,8 +395,8 @@ func TestLayout(t *testing.T) {
},
{
"Term, baseof, French, AMP",
- LayoutDescriptor{Kind: "term", Section: "tags", Lang: "fr", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "term", Section: "tags", Lang: "fr", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"term/term-baseof.fr.amp.html",
"term/tags-baseof.fr.amp.html",
@@ -510,8 +482,8 @@ func TestLayout(t *testing.T) {
},
{
"Term",
- LayoutDescriptor{Kind: "term", Section: "tags"},
- "", ampType,
+ LayoutDescriptor{Kind: "term", Section: "tags", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"term/term.amp.html",
"term/tags.amp.html",
@@ -549,8 +521,8 @@ func TestLayout(t *testing.T) {
},
{
"Taxonomy",
- LayoutDescriptor{Kind: "taxonomy", Section: "categories"},
- "", ampType,
+ LayoutDescriptor{Kind: "taxonomy", Section: "categories", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"categories/categories.terms.amp.html",
"categories/terms.amp.html",
@@ -580,8 +552,8 @@ func TestLayout(t *testing.T) {
},
{
"Page",
- LayoutDescriptor{Kind: "page"},
- "", ampType,
+ LayoutDescriptor{Kind: "page", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"_default/single.amp.html",
"_default/single.html",
@@ -589,8 +561,8 @@ func TestLayout(t *testing.T) {
},
{
"Page, baseof",
- LayoutDescriptor{Kind: "page", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"_default/single-baseof.amp.html",
"_default/baseof.amp.html",
@@ -600,8 +572,8 @@ func TestLayout(t *testing.T) {
},
{
"Page with layout",
- LayoutDescriptor{Kind: "page", Layout: "mylayout"},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Layout: "mylayout", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"_default/mylayout.amp.html",
"_default/single.amp.html",
@@ -611,8 +583,8 @@ func TestLayout(t *testing.T) {
},
{
"Page with layout, baseof",
- LayoutDescriptor{Kind: "page", Layout: "mylayout", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Layout: "mylayout", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"_default/mylayout-baseof.amp.html",
"_default/single-baseof.amp.html",
@@ -624,8 +596,8 @@ func TestLayout(t *testing.T) {
},
{
"Page with layout and type",
- LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype"},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"myttype/mylayout.amp.html",
"myttype/single.amp.html",
@@ -639,8 +611,8 @@ func TestLayout(t *testing.T) {
},
{
"Page baseof with layout and type",
- LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"myttype/mylayout-baseof.amp.html",
"myttype/single-baseof.amp.html",
@@ -658,8 +630,8 @@ func TestLayout(t *testing.T) {
},
{
"Page baseof with layout and type in French",
- LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype", Lang: "fr", Baseof: true},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype", Lang: "fr", Baseof: true, OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"myttype/mylayout-baseof.fr.amp.html",
"myttype/single-baseof.fr.amp.html",
@@ -689,8 +661,8 @@ func TestLayout(t *testing.T) {
},
{
"Page with layout and type with subtype",
- LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype/mysubtype"},
- "", ampType,
+ LayoutDescriptor{Kind: "page", Layout: "mylayout", Type: "myttype/mysubtype", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"myttype/mysubtype/mylayout.amp.html",
"myttype/mysubtype/single.amp.html",
@@ -705,8 +677,8 @@ func TestLayout(t *testing.T) {
// RSS
{
"RSS Home",
- LayoutDescriptor{Kind: "home"},
- "", RSSFormat,
+ LayoutDescriptor{Kind: "home", OutputFormatName: "rss", Suffix: "xml"},
+ "",
[]string{
"index.rss.xml",
"home.rss.xml",
@@ -727,8 +699,8 @@ func TestLayout(t *testing.T) {
},
{
"RSS Home, baseof",
- LayoutDescriptor{Kind: "home", Baseof: true},
- "", RSSFormat,
+ LayoutDescriptor{Kind: "home", Baseof: true, OutputFormatName: "rss", Suffix: "xml"},
+ "",
[]string{
"index-baseof.rss.xml",
"home-baseof.rss.xml",
@@ -750,8 +722,8 @@ func TestLayout(t *testing.T) {
},
{
"RSS Section",
- LayoutDescriptor{Kind: "section", Section: "sect1"},
- "", RSSFormat,
+ LayoutDescriptor{Kind: "section", Section: "sect1", OutputFormatName: "rss", Suffix: "xml"},
+ "",
[]string{
"sect1/sect1.rss.xml",
"sect1/section.rss.xml",
@@ -779,8 +751,8 @@ func TestLayout(t *testing.T) {
},
{
"RSS Term",
- LayoutDescriptor{Kind: "term", Section: "tag"},
- "", RSSFormat,
+ LayoutDescriptor{Kind: "term", Section: "tag", OutputFormatName: "rss", Suffix: "xml"},
+ "",
[]string{
"term/term.rss.xml",
"term/tag.rss.xml",
@@ -823,8 +795,8 @@ func TestLayout(t *testing.T) {
},
{
"RSS Taxonomy",
- LayoutDescriptor{Kind: "taxonomy", Section: "tag"},
- "", RSSFormat,
+ LayoutDescriptor{Kind: "taxonomy", Section: "tag", OutputFormatName: "rss", Suffix: "xml"},
+ "",
[]string{
"tag/tag.terms.rss.xml",
"tag/terms.rss.xml",
@@ -858,8 +830,8 @@ func TestLayout(t *testing.T) {
},
{
"Home plain text",
- LayoutDescriptor{Kind: "home"},
- "", JSONFormat,
+ LayoutDescriptor{Kind: "home", OutputFormatName: "json", Suffix: "json"},
+ "",
[]string{
"index.json.json",
"home.json.json",
@@ -877,8 +849,8 @@ func TestLayout(t *testing.T) {
},
{
"Page plain text",
- LayoutDescriptor{Kind: "page"},
- "", JSONFormat,
+ LayoutDescriptor{Kind: "page", OutputFormatName: "json", Suffix: "json"},
+ "",
[]string{
"_default/single.json.json",
"_default/single.json",
@@ -886,8 +858,8 @@ func TestLayout(t *testing.T) {
},
{
"Reserved section, shortcodes",
- LayoutDescriptor{Kind: "section", Section: "shortcodes", Type: "shortcodes"},
- "", ampType,
+ LayoutDescriptor{Kind: "section", Section: "shortcodes", Type: "shortcodes", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"section/shortcodes.amp.html",
"section/section.amp.html",
@@ -905,8 +877,8 @@ func TestLayout(t *testing.T) {
},
{
"Reserved section, partials",
- LayoutDescriptor{Kind: "section", Section: "partials", Type: "partials"},
- "", ampType,
+ LayoutDescriptor{Kind: "section", Section: "partials", Type: "partials", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"section/partials.amp.html",
"section/section.amp.html",
@@ -925,8 +897,8 @@ func TestLayout(t *testing.T) {
// This is currently always HTML only
{
"404, HTML",
- LayoutDescriptor{Kind: "404"},
- "", htmlFormat,
+ LayoutDescriptor{Kind: "404", OutputFormatName: "html", Suffix: "html"},
+ "",
[]string{
"404.html.html",
"404.html",
@@ -934,8 +906,8 @@ func TestLayout(t *testing.T) {
},
{
"404, HTML baseof",
- LayoutDescriptor{Kind: "404", Baseof: true},
- "", htmlFormat,
+ LayoutDescriptor{Kind: "404", Baseof: true, OutputFormatName: "html", Suffix: "html"},
+ "",
[]string{
"404-baseof.html.html",
"baseof.html.html",
@@ -949,8 +921,8 @@ func TestLayout(t *testing.T) {
},
{
"Content hook",
- LayoutDescriptor{Kind: "render-link", RenderingHook: true, Layout: "mylayout", Section: "blog"},
- "", ampType,
+ LayoutDescriptor{Kind: "render-link", RenderingHook: true, Layout: "mylayout", Section: "blog", OutputFormatName: "amp", Suffix: "html"},
+ "",
[]string{
"blog/_markup/render-link.amp.html",
"blog/_markup/render-link.html",
@@ -962,7 +934,7 @@ func TestLayout(t *testing.T) {
c.Run(this.name, func(c *qt.C) {
l := NewLayoutHandler()
- layouts, err := l.For(this.layoutDescriptor, this.format)
+ layouts, err := l.For(this.layoutDescriptor)
c.Assert(err, qt.IsNil)
c.Assert(layouts, qt.Not(qt.IsNil), qt.Commentf(this.layoutDescriptor.Kind))
@@ -981,8 +953,10 @@ func TestLayout(t *testing.T) {
}
})
}
+
}
+/*
func BenchmarkLayout(b *testing.B) {
descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
l := NewLayoutHandler()
@@ -1006,3 +980,4 @@ func BenchmarkLayoutUncached(b *testing.B) {
}
}
}
+*/
diff --git a/output/outputFormat.go b/output/outputFormat.go
index 0bc08e490..f602c03f3 100644
--- a/output/outputFormat.go
+++ b/output/outputFormat.go
@@ -17,19 +17,18 @@ package output
import (
"encoding/json"
"fmt"
- "reflect"
"sort"
"strings"
- "github.com/mitchellh/mapstructure"
-
"github.com/gohugoio/hugo/media"
)
// Format represents an output representation, usually to a file on disk.
+// <docsmeta>{ "name": "OutputFormat" }</docsmeta>
type Format struct {
- // The Name is used as an identifier. Internal output formats (i.e. HTML and RSS)
+ // The Name is used as an identifier. Internal output formats (i.e. html and rss)
// can be overridden by providing a new definition for those types.
+ // <docsmeta>{ "identifiers": ["html", "rss"] }</docsmeta>
Name string `json:"name"`
MediaType media.Type `json:"-"`
@@ -40,14 +39,7 @@ type Format struct {
// The base output file name used when not using "ugly URLs", defaults to "index".
BaseName string `json:"baseName"`
- // The value to use for rel links
- //
- // See https://www.w3schools.com/tags/att_link_rel.asp
- //
- // AMP has a special requirement in this department, see:
- // https://www.ampproject.org/docs/guides/deploy/discovery
- // I.e.:
- // <link rel="amphtml" href="https://www.example.com/url/to/amp/document.html">
+ // The value to use for rel links.
Rel string `json:"rel"`
// The protocol to use, i.e. "webcal://". Defaults to the protocol of the baseURL.
@@ -86,8 +78,8 @@ type Format struct {
// An ordered list of built-in output formats.
var (
AMPFormat = Format{
- Name: "AMP",
- MediaType: media.HTMLType,
+ Name: "amp",
+ MediaType: media.Builtin.HTMLType,
BaseName: "index",
Path: "amp",
Rel: "amphtml",
@@ -97,8 +89,8 @@ var (
}
CalendarFormat = Format{
- Name: "Calendar",
- MediaType: media.CalendarType,
+ Name: "calendar",
+ MediaType: media.Builtin.CalendarType,
IsPlainText: true,
Protocol: "webcal://",
BaseName: "index",
@@ -106,24 +98,24 @@ var (
}
CSSFormat = Format{
- Name: "CSS",
- MediaType: media.CSSType,
+ Name: "css",
+ MediaType: media.Builtin.CSSType,
BaseName: "styles",
IsPlainText: true,
Rel: "stylesheet",
NotAlternative: true,
}
CSVFormat = Format{
- Name: "CSV",
- MediaType: media.CSVType,
+ Name: "csv",
+ MediaType: media.Builtin.CSVType,
BaseName: "index",
IsPlainText: true,
Rel: "alternate",
}
HTMLFormat = Format{
- Name: "HTML",
- MediaType: media.HTMLType,
+ Name: "html",
+ MediaType: media.Builtin.HTMLType,
BaseName: "index",
Rel: "canonical",
IsHTML: true,
@@ -135,24 +127,24 @@ var (
}
MarkdownFormat = Format{
- Name: "MARKDOWN",
- MediaType: media.MarkdownType,
+ Name: "markdown",
+ MediaType: media.Builtin.MarkdownType,
BaseName: "index",
Rel: "alternate",
IsPlainText: true,
}
JSONFormat = Format{
- Name: "JSON",
- MediaType: media.JSONType,
+ Name: "json",
+ MediaType: media.Builtin.JSONType,
BaseName: "index",
IsPlainText: true,
Rel: "alternate",
}
WebAppManifestFormat = Format{
- Name: "WebAppManifest",
- MediaType: media.WebAppManifestType,
+ Name: "webappmanifest",
+ MediaType: media.Builtin.WebAppManifestType,
BaseName: "manifest",
IsPlainText: true,
NotAlternative: true,
@@ -160,24 +152,24 @@ var (
}
RobotsTxtFormat = Format{
- Name: "ROBOTS",
- MediaType: media.TextType,
+ Name: "robots",
+ MediaType: media.Builtin.TextType,
BaseName: "robots",
IsPlainText: true,
Rel: "alternate",
}
RSSFormat = Format{
- Name: "RSS",
- MediaType: media.RSSType,
+ Name: "rss",
+ MediaType: media.Builtin.RSSType,
BaseName: "index",
NoUgly: true,
Rel: "alternate",
}
SitemapFormat = Format{
- Name: "Sitemap",
- MediaType: media.XMLType,
+ Name: "sitemap",
+ MediaType: media.Builtin.XMLType,
BaseName: "sitemap",
NoUgly: true,
Rel: "sitemap",
@@ -204,6 +196,7 @@ func init() {
}
// Formats is a slice of Format.
+// <docsmeta>{ "name": "OutputFormats" }</docsmeta>
type Formats []Format
func (formats Formats) Len() int { return len(formats) }
@@ -298,102 +291,6 @@ func (formats Formats) FromFilename(filename string) (f Format, found bool) {
return
}
-// DecodeFormats takes a list of output format configurations and merges those,
-// in the order given, with the Hugo defaults as the last resort.
-func DecodeFormats(mediaTypes media.Types, maps ...map[string]any) (Formats, error) {
- f := make(Formats, len(DefaultFormats))
- copy(f, DefaultFormats)
-
- for _, m := range maps {
- for k, v := range m {
- found := false
- for i, vv := range f {
- if strings.EqualFold(k, vv.Name) {
- // Merge it with the existing
- if err := decode(mediaTypes, v, &f[i]); err != nil {
- return f, err
- }
- found = true
- }
- }
- if !found {
- var newOutFormat Format
- newOutFormat.Name = k
- if err := decode(mediaTypes, v, &newOutFormat); err != nil {
- return f, err
- }
-
- // We need values for these
- if newOutFormat.BaseName == "" {
- newOutFormat.BaseName = "index"
- }
- if newOutFormat.Rel == "" {
- newOutFormat.Rel = "alternate"
- }
-
- f = append(f, newOutFormat)
-
- }
- }
- }
-
- sort.Sort(f)
-
- return f, nil
-}
-
-func decode(mediaTypes media.Types, input any, output *Format) error {
- config := &mapstructure.DecoderConfig{
- Metadata: nil,
- Result: output,
- WeaklyTypedInput: true,
- DecodeHook: func(a reflect.Type, b reflect.Type, c any) (any, error) {
- if a.Kind() == reflect.Map {
- dataVal := reflect.Indirect(reflect.ValueOf(c))
- for _, key := range dataVal.MapKeys() {
- keyStr, ok := key.Interface().(string)
- if !ok {
- // Not a string key
- continue
- }
- if strings.EqualFold(keyStr, "mediaType") {
- // If mediaType is a string, look it up and replace it
- // in the map.
- vv := dataVal.MapIndex(key)
- vvi := vv.Interface()
-
- switch vviv := vvi.(type) {
- case media.Type:
- // OK
- case string:
- mediaType, found := mediaTypes.GetByType(vviv)
- if !found {
- return c, fmt.Errorf("media type %q not found", vviv)
- }
- dataVal.SetMapIndex(key, reflect.ValueOf(mediaType))
- default:
- return nil, fmt.Errorf("invalid output format configuration; wrong type for media type, expected string (e.g. text/html), got %T", vvi)
- }
- }
- }
- }
- return c, nil
- },
- }
-
- decoder, err := mapstructure.NewDecoder(config)
- if err != nil {
- return err
- }
-
- if err = decoder.Decode(input); err != nil {
- return fmt.Errorf("failed to decode output format configuration: %w", err)
- }
-
- return nil
-
-}
-
// BaseFilename returns the base filename of f including an extension (ie.
// "index.xml").
func (f Format) BaseFilename() string {
diff --git a/output/outputFormat_test.go b/output/outputFormat_test.go
index c5c4534bf..13e24af3b 100644
--- a/output/outputFormat_test.go
+++ b/output/outputFormat_test.go
@@ -23,46 +23,46 @@ import (
func TestDefaultTypes(t *testing.T) {
c := qt.New(t)
- c.Assert(CalendarFormat.Name, qt.Equals, "Calendar")
- c.Assert(CalendarFormat.MediaType, qt.Equals, media.CalendarType)
+ c.Assert(CalendarFormat.Name, qt.Equals, "calendar")
+ c.Assert(CalendarFormat.MediaType, qt.Equals, media.Builtin.CalendarType)
c.Assert(CalendarFormat.Protocol, qt.Equals, "webcal://")
c.Assert(CalendarFormat.Path, qt.HasLen, 0)
c.Assert(CalendarFormat.IsPlainText, qt.Equals, true)
c.Assert(CalendarFormat.IsHTML, qt.Equals, false)
- c.Assert(CSSFormat.Name, qt.Equals, "CSS")
- c.Assert(CSSFormat.MediaType, qt.Equals, media.CSSType)
+ c.Assert(CSSFormat.Name, qt.Equals, "css")
+ c.Assert(CSSFormat.MediaType, qt.Equals, media.Builtin.CSSType)
c.Assert(CSSFormat.Path, qt.HasLen, 0)
c.Assert(CSSFormat.Protocol, qt.HasLen, 0) // Will inherit the BaseURL protocol.
c.Assert(CSSFormat.IsPlainText, qt.Equals, true)
c.Assert(CSSFormat.IsHTML, qt.Equals, false)
- c.Assert(CSVFormat.Name, qt.Equals, "CSV")
- c.Assert(CSVFormat.MediaType, qt.Equals, media.CSVType)
+ c.Assert(CSVFormat.Name, qt.Equals, "csv")
+ c.Assert(CSVFormat.MediaType, qt.Equals, media.Builtin.CSVType)
c.Assert(CSVFormat.Path, qt.HasLen, 0)
c.Assert(CSVFormat.Protocol, qt.HasLen, 0)
c.Assert(CSVFormat.IsPlainText, qt.Equals, true)
c.Assert(CSVFormat.IsHTML, qt.Equals, false)
c.Assert(CSVFormat.Permalinkable, qt.Equals, false)
- c.Assert(HTMLFormat.Name, qt.Equals, "HTML")
- c.Assert(HTMLFormat.MediaType, qt.Equals, media.HTMLType)
+ c.Assert(HTMLFormat.Name, qt.Equals, "html")
+ c.Assert(HTMLFormat.MediaType, qt.Equals, media.Builtin.HTMLType)
c.Assert(HTMLFormat.Path, qt.HasLen, 0)
c.Assert(HTMLFormat.Protocol, qt.HasLen, 0)
c.Assert(HTMLFormat.IsPlainText, qt.Equals, false)
c.Assert(HTMLFormat.IsHTML, qt.Equals, true)
c.Assert(AMPFormat.Permalinkable, qt.Equals, true)
- c.Assert(AMPFormat.Name, qt.Equals, "AMP")
- c.Assert(AMPFormat.MediaType, qt.Equals, media.HTMLType)
+ c.Assert(AMPFormat.Name, qt.Equals, "amp")
+ c.Assert(AMPFormat.MediaType, qt.Equals, media.Builtin.HTMLType)
c.Assert(AMPFormat.Path, qt.Equals, "amp")
c.Assert(AMPFormat.Protocol, qt.HasLen, 0)
c.Assert(AMPFormat.IsPlainText, qt.Equals, false)
c.Assert(AMPFormat.IsHTML, qt.Equals, true)
c.Assert(AMPFormat.Permalinkable, qt.Equals, true)
- c.Assert(RSSFormat.Name, qt.Equals, "RSS")
- c.Assert(RSSFormat.MediaType, qt.Equals, media.RSSType)
+ c.Assert(RSSFormat.Name, qt.Equals, "rss")
+ c.Assert(RSSFormat.MediaType, qt.Equals, media.Builtin.RSSType)
c.Assert(RSSFormat.Path, qt.HasLen, 0)
c.Assert(RSSFormat.IsPlainText, qt.Equals, false)
c.Assert(RSSFormat.NoUgly, qt.Equals, true)
@@ -101,10 +101,10 @@ func TestGetFormatByExt(t *testing.T) {
func TestGetFormatByFilename(t *testing.T) {
c := qt.New(t)
- noExtNoDelimMediaType := media.TextType
+ noExtNoDelimMediaType := media.Builtin.TextType
noExtNoDelimMediaType.Delimiter = ""
- noExtMediaType := media.TextType
+ noExtMediaType := media.Builtin.TextType
var (
noExtDelimFormat = Format{
@@ -138,117 +138,10 @@ func TestGetFormatByFilename(t *testing.T) {
c.Assert(found, qt.Equals, false)
}
-func TestDecodeFormats(t *testing.T) {
- c := qt.New(t)
-
- mediaTypes := media.Types{media.JSONType, media.XMLType}
-
- tests := []struct {
- name string
- maps []map[string]any
- shouldError bool
- assert func(t *testing.T, name string, f Formats)
- }{
- {
- "Redefine JSON",
- []map[string]any{
- {
- "JsON": map[string]any{
- "baseName": "myindex",
- "isPlainText": "false",
- },
- },
- },
- false,
- func(t *testing.T, name string, f Formats) {
- msg := qt.Commentf(name)
- c.Assert(len(f), qt.Equals, len(DefaultFormats), msg)
- json, _ := f.GetByName("JSON")
- c.Assert(json.BaseName, qt.Equals, "myindex")
- c.Assert(json.MediaType, qt.Equals, media.JSONType)
- c.Assert(json.IsPlainText, qt.Equals, false)
- },
- },
- {
- "Add XML format with string as mediatype",
- []map[string]any{
- {
- "MYXMLFORMAT": map[string]any{
- "baseName": "myxml",
- "mediaType": "application/xml",
- },
- },
- },
- false,
- func(t *testing.T, name string, f Formats) {
- c.Assert(len(f), qt.Equals, len(DefaultFormats)+1)
- xml, found := f.GetByName("MYXMLFORMAT")
- c.Assert(found, qt.Equals, true)
- c.Assert(xml.BaseName, qt.Equals, "myxml")
- c.Assert(xml.MediaType, qt.Equals, media.XMLType)
-
- // Verify that we haven't changed the DefaultFormats slice.
- json, _ := f.GetByName("JSON")
- c.Assert(json.BaseName, qt.Equals, "index")
- },
- },
- {
- "Add format unknown mediatype",
- []map[string]any{
- {
- "MYINVALID": map[string]any{
- "baseName": "mymy",
- "mediaType": "application/hugo",
- },
- },
- },
- true,
- func(t *testing.T, name string, f Formats) {
- },
- },
- {
- "Add and redefine XML format",
- []map[string]any{
- {
- "MYOTHERXMLFORMAT": map[string]any{
- "baseName": "myotherxml",
- "mediaType": media.XMLType,
- },
- },
- {
- "MYOTHERXMLFORMAT": map[string]any{
- "baseName": "myredefined",
- },
- },
- },
- false,
- func(t *testing.T, name string, f Formats) {
- c.Assert(len(f), qt.Equals, len(DefaultFormats)+1)
- xml, found := f.GetByName("MYOTHERXMLFORMAT")
- c.Assert(found, qt.Equals, true)
- c.Assert(xml.BaseName, qt.Equals, "myredefined")
- c.Assert(xml.MediaType, qt.Equals, media.XMLType)
- },
- },
- }
-
- for _, test := range tests {
- result, err := DecodeFormats(mediaTypes, test.maps...)
- msg := qt.Commentf(test.name)
-
- if test.shouldError {
- c.Assert(err, qt.Not(qt.IsNil), msg)
- } else {
- c.Assert(err, qt.IsNil, msg)
- test.assert(t, test.name, result)
- }
- }
-}
-
func TestSort(t *testing.T) {
c := qt.New(t)
- c.Assert(DefaultFormats[0].Name, qt.Equals, "HTML")
- c.Assert(DefaultFormats[1].Name, qt.Equals, "AMP")
+ c.Assert(DefaultFormats[0].Name, qt.Equals, "html")
+ c.Assert(DefaultFormats[1].Name, qt.Equals, "amp")
json := JSONFormat
json.Weight = 1
@@ -261,7 +154,7 @@ func TestSort(t *testing.T) {
sort.Sort(formats)
- c.Assert(formats[0].Name, qt.Equals, "JSON")
- c.Assert(formats[1].Name, qt.Equals, "HTML")
- c.Assert(formats[2].Name, qt.Equals, "AMP")
+ c.Assert(formats[0].Name, qt.Equals, "json")
+ c.Assert(formats[1].Name, qt.Equals, "html")
+ c.Assert(formats[2].Name, qt.Equals, "amp")
}