mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
hugolib, output: Add Rel to the output format
To make it super-easy to create rel-links.
This commit is contained in:
parent
29d3778ba1
commit
c7dbee2321
3 changed files with 108 additions and 11 deletions
|
@ -14,6 +14,7 @@
|
|||
package hugolib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -116,20 +117,64 @@ type OutputFormats []*OutputFormat
|
|||
|
||||
// And OutputFormat links to a representation of a resource.
|
||||
type OutputFormat struct {
|
||||
// Rel constains a value that can be used to construct a rel link.
|
||||
// This is value is fetched from the output format definition.
|
||||
// Note that for pages with only one output format,
|
||||
// this method will always return "canonical".
|
||||
// TODO(bep) output -- the above may not be correct for CSS etc. Figure out a way around that.
|
||||
// TODO(bep) output -- re the above, maybe add a "alternate" filter to AlternativeOutputFormats.
|
||||
// As an example, the AMP output format will, by default, return "amphtml".
|
||||
//
|
||||
// See:
|
||||
// https://www.ampproject.org/docs/guides/deploy/discovery
|
||||
//
|
||||
// Most other output formats will have "alternate" as value for this.
|
||||
Rel string
|
||||
|
||||
// It may be tempting to export this, but let us hold on to that horse for a while.
|
||||
f output.Format
|
||||
p *Page
|
||||
}
|
||||
|
||||
// Name returns this OutputFormat's name, i.e. HTML, AMP, JSON etc.
|
||||
func (o OutputFormat) Name() string {
|
||||
return o.f.Name
|
||||
}
|
||||
|
||||
// TODO(bep) outputs consider just save this wrapper on Page.
|
||||
// OutputFormats gives the output formats for this Page.
|
||||
func (p *Page) OutputFormats() OutputFormats {
|
||||
var o OutputFormats
|
||||
isCanonical := len(p.outputFormats) == 1
|
||||
for _, f := range p.outputFormats {
|
||||
o = append(o, &OutputFormat{f: f, p: p})
|
||||
rel := f.Rel
|
||||
if isCanonical {
|
||||
rel = "canonical"
|
||||
}
|
||||
o = append(o, &OutputFormat{Rel: rel, f: f, p: p})
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// OutputFormats gives the alternative output formats for this PageOutput.
|
||||
func (p *PageOutput) AlternativeOutputFormats() (OutputFormats, error) {
|
||||
var o OutputFormats
|
||||
for _, of := range p.OutputFormats() {
|
||||
if of.f == p.outputFormat {
|
||||
continue
|
||||
}
|
||||
o = append(o, of)
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// AlternativeOutputFormats is only available on the top level rendering
|
||||
// entry point, and not inside range loops on the Page collections.
|
||||
// This method is just here to inform users of that restriction.
|
||||
func (p *Page) AlternativeOutputFormats() (OutputFormats, error) {
|
||||
return nil, fmt.Errorf("AlternativeOutputFormats only available from the top level template context for page %q", p.Path())
|
||||
}
|
||||
|
||||
// Get gets a OutputFormat given its name, i.e. json, html etc.
|
||||
// It returns nil if not found.
|
||||
func (o OutputFormats) Get(name string) *OutputFormat {
|
||||
|
|
|
@ -15,12 +15,14 @@ package hugolib
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/hugo/helpers"
|
||||
"github.com/spf13/hugo/output"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
@ -47,9 +49,19 @@ func TestDefaultOutputDefinitions(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSiteWithJSONHomepage(t *testing.T) {
|
||||
func TestSiteWithPageOutputs(t *testing.T) {
|
||||
for _, outputs := range [][]string{{"html", "json"}, {"json"}} {
|
||||
t.Run(fmt.Sprintf("%v", outputs), func(t *testing.T) {
|
||||
doTestSiteWithPageOutputs(t, outputs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func doTestSiteWithPageOutputs(t *testing.T, outputs []string) {
|
||||
t.Parallel()
|
||||
|
||||
outputsStr := strings.Replace(fmt.Sprintf("%q", outputs), " ", ", ", -1)
|
||||
|
||||
siteConfig := `
|
||||
baseURL = "http://example.com/blog"
|
||||
|
||||
|
@ -65,19 +77,26 @@ category = "categories"
|
|||
|
||||
pageTemplate := `---
|
||||
title: "%s"
|
||||
outputs: ["html", "json"]
|
||||
outputs: %s
|
||||
---
|
||||
# Doc
|
||||
`
|
||||
|
||||
th, h := newTestSitesFromConfig(t, siteConfig,
|
||||
"layouts/_default/list.json", "List JSON|{{ .Title }}|{{ .Content }}",
|
||||
"layouts/_default/list.json", `List JSON|{{ .Title }}|{{ .Content }}|Alt formats: {{ len .AlternativeOutputFormats -}}|
|
||||
{{- range .AlternativeOutputFormats -}}
|
||||
Alt Output: {{ .Name -}}|
|
||||
{{- end -}}|
|
||||
{{- range .OutputFormats -}}
|
||||
Output/Rel: {{ .Name -}}/{{ .Rel }}|
|
||||
{{- end -}}
|
||||
`,
|
||||
)
|
||||
require.Len(t, h.Sites, 1)
|
||||
|
||||
fs := th.Fs
|
||||
|
||||
writeSource(t, fs, "content/_index.md", fmt.Sprintf(pageTemplate, "JSON Home"))
|
||||
writeSource(t, fs, "content/_index.md", fmt.Sprintf(pageTemplate, "JSON Home", outputsStr))
|
||||
|
||||
err := h.Build(BuildCfg{})
|
||||
|
||||
|
@ -88,17 +107,38 @@ outputs: ["html", "json"]
|
|||
|
||||
require.NotNil(t, home)
|
||||
|
||||
require.Len(t, home.outputFormats, 2)
|
||||
lenOut := len(outputs)
|
||||
|
||||
require.Len(t, home.outputFormats, lenOut)
|
||||
|
||||
// TODO(bep) output assert template/text
|
||||
// There is currently always a JSON output to make it simpler ...
|
||||
altFormats := lenOut - 1
|
||||
hasHTML := helpers.InStringArray(outputs, "html")
|
||||
th.assertFileContent("public/index.json",
|
||||
"List JSON",
|
||||
fmt.Sprintf("Alt formats: %d", altFormats),
|
||||
)
|
||||
|
||||
th.assertFileContent("public/index.json", "List JSON")
|
||||
if hasHTML {
|
||||
th.assertFileContent("public/index.json",
|
||||
"Alt Output: HTML",
|
||||
"Output/Rel: JSON/alternate|",
|
||||
"Output/Rel: HTML/canonical|",
|
||||
)
|
||||
} else {
|
||||
th.assertFileContent("public/index.json",
|
||||
"Output/Rel: JSON/canonical|",
|
||||
)
|
||||
}
|
||||
|
||||
of := home.OutputFormats()
|
||||
require.Len(t, of, 2)
|
||||
require.Len(t, of, lenOut)
|
||||
require.Nil(t, of.Get("Hugo"))
|
||||
require.NotNil(t, of.Get("json"))
|
||||
json := of.Get("JSON")
|
||||
_, err = home.AlternativeOutputFormats()
|
||||
require.Error(t, err)
|
||||
require.NotNil(t, json)
|
||||
require.Equal(t, "/blog/index.json", json.RelPermalink())
|
||||
require.Equal(t, "http://example.com/blog/index.json", json.Permalink())
|
||||
|
|
|
@ -23,26 +23,26 @@ import (
|
|||
var (
|
||||
// An ordered list of built-in output formats
|
||||
// See https://www.ampproject.org/learn/overview/
|
||||
// TODO
|
||||
// <link rel="amphtml" href="{{ .Permalink }}">
|
||||
// canonical
|
||||
AMPType = Format{
|
||||
Name: "AMP",
|
||||
MediaType: media.HTMLType,
|
||||
BaseName: "index",
|
||||
Path: "amp",
|
||||
Rel: "amphtml",
|
||||
}
|
||||
|
||||
CSSType = Format{
|
||||
Name: "CSS",
|
||||
MediaType: media.CSSType,
|
||||
BaseName: "styles",
|
||||
Rel: "stylesheet",
|
||||
}
|
||||
|
||||
HTMLType = Format{
|
||||
Name: "HTML",
|
||||
MediaType: media.HTMLType,
|
||||
BaseName: "index",
|
||||
Rel: "canonical",
|
||||
}
|
||||
|
||||
JSONType = Format{
|
||||
|
@ -50,6 +50,7 @@ var (
|
|||
MediaType: media.JSONType,
|
||||
BaseName: "index",
|
||||
IsPlainText: true,
|
||||
Rel: "alternate",
|
||||
}
|
||||
|
||||
RSSType = Format{
|
||||
|
@ -57,6 +58,7 @@ var (
|
|||
MediaType: media.RSSType,
|
||||
BaseName: "index",
|
||||
NoUgly: true,
|
||||
Rel: "alternate",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -84,6 +86,16 @@ type Format struct {
|
|||
// 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
|
||||
|
||||
|
|
Loading…
Reference in a new issue