mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
5c5efa03d2
Before this commit, Hugo used `html/template` for all Go templates. While this is a fine choice for HTML and maybe also RSS feeds, it is painful for plain text formats such as CSV, JSON etc. This commit fixes that by using the `IsPlainText` attribute on the output format to decide what to use. A couple of notes: * The above requires a nonambiguous template name to type mapping. I.e. `/layouts/_default/list.json` will only work if there is only one JSON output format, `/layouts/_default/list.mytype.json` will always work. * Ambiguous types will fall back to HTML. * Partials inherits the text vs HTML identificator of the container template. This also means that plain text templates can only include plain text partials. * Shortcode templates are, by definition, currently HTML templates only. Fixes #3221
211 lines
4.9 KiB
Go
211 lines
4.9 KiB
Go
// Copyright 2017-present 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"
|
|
"strings"
|
|
|
|
"github.com/spf13/hugo/media"
|
|
)
|
|
|
|
var (
|
|
// An ordered list of built-in output formats
|
|
//
|
|
// See https://www.ampproject.org/learn/overview/
|
|
AMPFormat = Format{
|
|
Name: "AMP",
|
|
MediaType: media.HTMLType,
|
|
BaseName: "index",
|
|
Path: "amp",
|
|
Rel: "amphtml",
|
|
IsHTML: true,
|
|
}
|
|
|
|
// CalendarFormat is AAA
|
|
CalendarFormat = Format{
|
|
Name: "Calendar",
|
|
MediaType: media.CalendarType,
|
|
IsPlainText: true,
|
|
Protocol: "webcal://",
|
|
BaseName: "index",
|
|
Rel: "alternate",
|
|
}
|
|
|
|
CSSFormat = Format{
|
|
Name: "CSS",
|
|
MediaType: media.CSSType,
|
|
BaseName: "styles",
|
|
IsPlainText: true,
|
|
Rel: "stylesheet",
|
|
}
|
|
CSVFormat = Format{
|
|
Name: "CSV",
|
|
MediaType: media.CSVType,
|
|
BaseName: "index",
|
|
IsPlainText: true,
|
|
Rel: "alternate",
|
|
}
|
|
|
|
HTMLFormat = Format{
|
|
Name: "HTML",
|
|
MediaType: media.HTMLType,
|
|
BaseName: "index",
|
|
Rel: "canonical",
|
|
IsHTML: true,
|
|
}
|
|
|
|
JSONFormat = Format{
|
|
Name: "JSON",
|
|
MediaType: media.JSONType,
|
|
BaseName: "index",
|
|
IsPlainText: true,
|
|
Rel: "alternate",
|
|
}
|
|
|
|
RSSFormat = Format{
|
|
Name: "RSS",
|
|
MediaType: media.RSSType,
|
|
BaseName: "index",
|
|
NoUgly: true,
|
|
Rel: "alternate",
|
|
}
|
|
)
|
|
|
|
var builtInTypes = map[string]Format{
|
|
strings.ToLower(AMPFormat.Name): AMPFormat,
|
|
strings.ToLower(CalendarFormat.Name): CalendarFormat,
|
|
strings.ToLower(CSSFormat.Name): CSSFormat,
|
|
strings.ToLower(HTMLFormat.Name): HTMLFormat,
|
|
strings.ToLower(JSONFormat.Name): JSONFormat,
|
|
strings.ToLower(RSSFormat.Name): RSSFormat,
|
|
}
|
|
|
|
type Formats []Format
|
|
|
|
func (formats Formats) GetByName(name string) (f Format, found bool) {
|
|
for _, ff := range formats {
|
|
if name == ff.Name {
|
|
f = ff
|
|
found = true
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (formats Formats) GetBySuffix(name string) (f Format, found bool) {
|
|
for _, ff := range formats {
|
|
if name == ff.MediaType.Suffix {
|
|
if found {
|
|
// ambiguous
|
|
found = false
|
|
return
|
|
}
|
|
f = ff
|
|
found = true
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (formats Formats) FromFilename(filename string) (f Format, found bool) {
|
|
// mytemplate.amp.html
|
|
// mytemplate.html
|
|
// mytemplate
|
|
var ext, outFormat string
|
|
|
|
parts := strings.Split(filename, ".")
|
|
if len(parts) > 2 {
|
|
outFormat = parts[1]
|
|
ext = parts[2]
|
|
} else if len(parts) > 1 {
|
|
ext = parts[1]
|
|
}
|
|
|
|
if outFormat != "" {
|
|
return formats.GetByName(outFormat)
|
|
}
|
|
|
|
if ext != "" {
|
|
return formats.GetBySuffix(ext)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Format represents an output representation, usually to a file on disk.
|
|
type Format struct {
|
|
// 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.
|
|
Name string
|
|
|
|
MediaType media.Type
|
|
|
|
// Must be set to a value when there are two or more conflicting mediatype for the same resource.
|
|
Path string
|
|
|
|
// The base output file name used when not using "ugly URLs", defaults to "index".
|
|
BaseName string
|
|
|
|
// 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">
|
|
Rel string
|
|
|
|
// The protocol to use, i.e. "webcal://". Defaults to the protocol of the baseURL.
|
|
Protocol string
|
|
|
|
// IsPlainText decides whether to use text/template or html/template
|
|
// as template parser.
|
|
IsPlainText bool
|
|
|
|
// IsHTML returns whether this format is int the HTML family. This includes
|
|
// HTML, AMP etc. This is used to decide when to create alias redirects etc.
|
|
IsHTML bool
|
|
|
|
// Enable to ignore the global uglyURLs setting.
|
|
NoUgly bool
|
|
}
|
|
|
|
func GetFormat(key string) (Format, bool) {
|
|
found, ok := builtInTypes[key]
|
|
if !ok {
|
|
found, ok = builtInTypes[strings.ToLower(key)]
|
|
}
|
|
return found, ok
|
|
}
|
|
|
|
// TODO(bep) outputs rewamp on global config?
|
|
func GetFormats(keys ...string) (Formats, error) {
|
|
var types []Format
|
|
|
|
for _, key := range keys {
|
|
tpe, ok := GetFormat(key)
|
|
if !ok {
|
|
return types, fmt.Errorf("OutputFormat with key %q not found", key)
|
|
}
|
|
types = append(types, tpe)
|
|
}
|
|
|
|
return types, nil
|
|
}
|
|
|
|
func (t Format) BaseFilename() string {
|
|
return t.BaseName + "." + t.MediaType.Suffix
|
|
}
|