outputFormat.go - hugo - [fork] hugo port for 9front
HTML git clone https://git.drkhsh.at/hugo.git
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
outputFormat.go (9610B)
---
1 // Copyright 2019 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 // Package output contains Output Format types and functions.
15 package output
16
17 import (
18 "encoding/json"
19 "fmt"
20 "sort"
21 "strings"
22
23 "github.com/gohugoio/hugo/media"
24 )
25
26 // Format represents an output representation, usually to a file on disk.
27 // <docsmeta>{ "name": "OutputFormat" }</docsmeta>
28 type Format struct {
29 // The Name is used as an identifier. Internal output formats (i.e. html and rss)
30 // can be overridden by providing a new definition for those types.
31 // <docsmeta>{ "identifiers": ["html", "rss"] }</docsmeta>
32 Name string `json:"-"`
33
34 MediaType media.Type `json:"-"`
35
36 // Must be set to a value when there are two or more conflicting mediatype for the same resource.
37 Path string `json:"path"`
38
39 // The base output file name used when not using "ugly URLs", defaults to "index".
40 BaseName string `json:"baseName"`
41
42 // The value to use for rel links.
43 Rel string `json:"rel"`
44
45 // The protocol to use, i.e. "webcal://". Defaults to the protocol of the baseURL.
46 Protocol string `json:"protocol"`
47
48 // IsPlainText decides whether to use text/template or html/template
49 // as template parser.
50 IsPlainText bool `json:"isPlainText"`
51
52 // IsHTML returns whether this format is int the HTML family. This includes
53 // HTML, AMP etc. This is used to decide when to create alias redirects etc.
54 IsHTML bool `json:"isHTML"`
55
56 // Enable to ignore the global uglyURLs setting.
57 NoUgly bool `json:"noUgly"`
58
59 // Enable to override the global uglyURLs setting.
60 Ugly bool `json:"ugly"`
61
62 // Enable if it doesn't make sense to include this format in an alternative
63 // format listing, CSS being one good example.
64 // Note that we use the term "alternative" and not "alternate" here, as it
65 // does not necessarily replace the other format, it is an alternative representation.
66 NotAlternative bool `json:"notAlternative"`
67
68 // Eneable if this is a resource which path always starts at the root,
69 // e.g. /robots.txt.
70 Root bool `json:"root"`
71
72 // Setting this will make this output format control the value of
73 // .Permalink and .RelPermalink for a rendered Page.
74 // If not set, these values will point to the main (first) output format
75 // configured. That is probably the behavior you want in most situations,
76 // as you probably don't want to link back to the RSS version of a page, as an
77 // example. AMP would, however, be a good example of an output format where this
78 // behavior is wanted.
79 Permalinkable bool `json:"permalinkable"`
80
81 // Setting this to a non-zero value will be used as the first sort criteria.
82 Weight int `json:"weight"`
83 }
84
85 // Built-in output formats.
86 var (
87 AMPFormat = Format{
88 Name: "amp",
89 MediaType: media.Builtin.HTMLType,
90 BaseName: "index",
91 Path: "amp",
92 Rel: "amphtml",
93 IsHTML: true,
94 Permalinkable: true,
95 // See https://www.ampproject.org/learn/overview/
96 }
97
98 CalendarFormat = Format{
99 Name: "calendar",
100 MediaType: media.Builtin.CalendarType,
101 IsPlainText: true,
102 Protocol: "webcal://",
103 BaseName: "index",
104 Rel: "alternate",
105 }
106
107 CSSFormat = Format{
108 Name: "css",
109 MediaType: media.Builtin.CSSType,
110 BaseName: "styles",
111 IsPlainText: true,
112 Rel: "stylesheet",
113 NotAlternative: true,
114 }
115 CSVFormat = Format{
116 Name: "csv",
117 MediaType: media.Builtin.CSVType,
118 BaseName: "index",
119 IsPlainText: true,
120 Rel: "alternate",
121 }
122
123 HTMLFormat = Format{
124 Name: "html",
125 MediaType: media.Builtin.HTMLType,
126 BaseName: "index",
127 Rel: "canonical",
128 IsHTML: true,
129 Permalinkable: true,
130
131 // Weight will be used as first sort criteria. HTML will, by default,
132 // be rendered first, but set it to 10 so it's easy to put one above it.
133 Weight: 10,
134 }
135
136 // Alias is the output format used for alias redirects.
137 AliasHTMLFormat = Format{
138 Name: "alias",
139 MediaType: media.Builtin.HTMLType,
140 IsHTML: true,
141 Ugly: true,
142 Permalinkable: false,
143 }
144
145 MarkdownFormat = Format{
146 Name: "markdown",
147 MediaType: media.Builtin.MarkdownType,
148 BaseName: "index",
149 Rel: "alternate",
150 IsPlainText: true,
151 }
152
153 JSONFormat = Format{
154 Name: "json",
155 MediaType: media.Builtin.JSONType,
156 BaseName: "index",
157 IsPlainText: true,
158 Rel: "alternate",
159 }
160
161 WebAppManifestFormat = Format{
162 Name: "webappmanifest",
163 MediaType: media.Builtin.WebAppManifestType,
164 BaseName: "manifest",
165 IsPlainText: true,
166 NotAlternative: true,
167 Rel: "manifest",
168 }
169
170 RobotsTxtFormat = Format{
171 Name: "robots",
172 MediaType: media.Builtin.TextType,
173 BaseName: "robots",
174 IsPlainText: true,
175 Root: true,
176 Rel: "alternate",
177 }
178
179 RSSFormat = Format{
180 Name: "rss",
181 MediaType: media.Builtin.RSSType,
182 BaseName: "index",
183 NoUgly: true,
184 Rel: "alternate",
185 }
186
187 SitemapFormat = Format{
188 Name: "sitemap",
189 MediaType: media.Builtin.XMLType,
190 BaseName: "sitemap",
191 Ugly: true,
192 Rel: "sitemap",
193 }
194
195 SitemapIndexFormat = Format{
196 Name: "sitemapindex",
197 MediaType: media.Builtin.XMLType,
198 BaseName: "sitemap",
199 Ugly: true,
200 Root: true,
201 Rel: "sitemap",
202 }
203
204 GotmplFormat = Format{
205 Name: "gotmpl",
206 MediaType: media.Builtin.GotmplType,
207 IsPlainText: true,
208 NotAlternative: true,
209 }
210
211 // I'm not sure having a 404 format is a good idea,
212 // for one, we would want to have multiple formats for this.
213 HTTPStatus404HTMLFormat = Format{
214 Name: "404",
215 MediaType: media.Builtin.HTMLType,
216 NotAlternative: true,
217 Ugly: true,
218 IsHTML: true,
219 Permalinkable: true,
220 }
221 )
222
223 // DefaultFormats contains the default output formats supported by Hugo.
224 var DefaultFormats = Formats{
225 AMPFormat,
226 CalendarFormat,
227 CSSFormat,
228 CSVFormat,
229 HTMLFormat,
230 GotmplFormat,
231 HTTPStatus404HTMLFormat,
232 AliasHTMLFormat,
233 JSONFormat,
234 MarkdownFormat,
235 WebAppManifestFormat,
236 RobotsTxtFormat,
237 RSSFormat,
238 SitemapFormat,
239 SitemapIndexFormat,
240 }
241
242 func init() {
243 sort.Sort(DefaultFormats)
244 }
245
246 // Formats is a slice of Format.
247 // <docsmeta>{ "name": "OutputFormats" }</docsmeta>
248 type Formats []Format
249
250 func (formats Formats) Len() int { return len(formats) }
251 func (formats Formats) Swap(i, j int) { formats[i], formats[j] = formats[j], formats[i] }
252 func (formats Formats) Less(i, j int) bool {
253 fi, fj := formats[i], formats[j]
254 if fi.Weight == fj.Weight {
255 return fi.Name < fj.Name
256 }
257
258 if fj.Weight == 0 {
259 return true
260 }
261
262 return fi.Weight > 0 && fi.Weight < fj.Weight
263 }
264
265 // GetBySuffix gets a output format given as suffix, e.g. "html".
266 // It will return false if no format could be found, or if the suffix given
267 // is ambiguous.
268 // The lookup is case insensitive.
269 func (formats Formats) GetBySuffix(suffix string) (f Format, found bool) {
270 for _, ff := range formats {
271 for _, suffix2 := range ff.MediaType.Suffixes() {
272 if strings.EqualFold(suffix, suffix2) {
273 if found {
274 // ambiguous
275 found = false
276 return
277 }
278 f = ff
279 found = true
280 }
281 }
282 }
283 return
284 }
285
286 // GetByName gets a format by its identifier name.
287 func (formats Formats) GetByName(name string) (f Format, found bool) {
288 for _, ff := range formats {
289 if strings.EqualFold(name, ff.Name) {
290 f = ff
291 found = true
292 return
293 }
294 }
295 return
296 }
297
298 // GetByNames gets a list of formats given a list of identifiers.
299 func (formats Formats) GetByNames(names ...string) (Formats, error) {
300 var types []Format
301
302 for _, name := range names {
303 tpe, ok := formats.GetByName(name)
304 if !ok {
305 return types, fmt.Errorf("OutputFormat with key %q not found", name)
306 }
307 types = append(types, tpe)
308 }
309 return types, nil
310 }
311
312 // FromFilename gets a Format given a filename.
313 func (formats Formats) FromFilename(filename string) (f Format, found bool) {
314 // mytemplate.amp.html
315 // mytemplate.html
316 // mytemplate
317 var ext, outFormat string
318
319 parts := strings.Split(filename, ".")
320 if len(parts) > 2 {
321 outFormat = parts[1]
322 ext = parts[2]
323 } else if len(parts) > 1 {
324 ext = parts[1]
325 }
326
327 if outFormat != "" {
328 return formats.GetByName(outFormat)
329 }
330
331 if ext != "" {
332 f, found = formats.GetBySuffix(ext)
333 if !found && len(parts) == 2 {
334 // For extensionless output formats (e.g. Netlify's _redirects)
335 // we must fall back to using the extension as format lookup.
336 f, found = formats.GetByName(ext)
337 }
338 }
339 return
340 }
341
342 // BaseFilename returns the base filename of f including an extension (ie.
343 // "index.xml").
344 func (f Format) BaseFilename() string {
345 return f.BaseName + f.MediaType.FirstSuffix.FullSuffix
346 }
347
348 // IsZero returns true if f represents a zero value.
349 func (f Format) IsZero() bool {
350 return f.Name == ""
351 }
352
353 // MarshalJSON returns the JSON encoding of f.
354 // For internal use only.
355 func (f Format) MarshalJSON() ([]byte, error) {
356 type Alias Format
357 return json.Marshal(&struct {
358 MediaType string `json:"mediaType"`
359 Alias
360 }{
361 MediaType: f.MediaType.String(),
362 Alias: (Alias)(f),
363 })
364 }