github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/output/outputFormat.go (about) 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:"name"` 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 if it doesn't make sense to include this format in an alternative 60 // format listing, CSS being one good example. 61 // Note that we use the term "alternative" and not "alternate" here, as it 62 // does not necessarily replace the other format, it is an alternative representation. 63 NotAlternative bool `json:"notAlternative"` 64 65 // Setting this will make this output format control the value of 66 // .Permalink and .RelPermalink for a rendered Page. 67 // If not set, these values will point to the main (first) output format 68 // configured. That is probably the behaviour you want in most situations, 69 // as you probably don't want to link back to the RSS version of a page, as an 70 // example. AMP would, however, be a good example of an output format where this 71 // behaviour is wanted. 72 Permalinkable bool `json:"permalinkable"` 73 74 // Setting this to a non-zero value will be used as the first sort criteria. 75 Weight int `json:"weight"` 76 } 77 78 // An ordered list of built-in output formats. 79 var ( 80 AMPFormat = Format{ 81 Name: "amp", 82 MediaType: media.Builtin.HTMLType, 83 BaseName: "index", 84 Path: "amp", 85 Rel: "amphtml", 86 IsHTML: true, 87 Permalinkable: true, 88 // See https://www.ampproject.org/learn/overview/ 89 } 90 91 CalendarFormat = Format{ 92 Name: "calendar", 93 MediaType: media.Builtin.CalendarType, 94 IsPlainText: true, 95 Protocol: "webcal://", 96 BaseName: "index", 97 Rel: "alternate", 98 } 99 100 CSSFormat = Format{ 101 Name: "css", 102 MediaType: media.Builtin.CSSType, 103 BaseName: "styles", 104 IsPlainText: true, 105 Rel: "stylesheet", 106 NotAlternative: true, 107 } 108 CSVFormat = Format{ 109 Name: "csv", 110 MediaType: media.Builtin.CSVType, 111 BaseName: "index", 112 IsPlainText: true, 113 Rel: "alternate", 114 } 115 116 HTMLFormat = Format{ 117 Name: "html", 118 MediaType: media.Builtin.HTMLType, 119 BaseName: "index", 120 Rel: "canonical", 121 IsHTML: true, 122 Permalinkable: true, 123 124 // Weight will be used as first sort criteria. HTML will, by default, 125 // be rendered first, but set it to 10 so it's easy to put one above it. 126 Weight: 10, 127 } 128 129 MarkdownFormat = Format{ 130 Name: "markdown", 131 MediaType: media.Builtin.MarkdownType, 132 BaseName: "index", 133 Rel: "alternate", 134 IsPlainText: true, 135 } 136 137 JSONFormat = Format{ 138 Name: "json", 139 MediaType: media.Builtin.JSONType, 140 BaseName: "index", 141 IsPlainText: true, 142 Rel: "alternate", 143 } 144 145 WebAppManifestFormat = Format{ 146 Name: "webappmanifest", 147 MediaType: media.Builtin.WebAppManifestType, 148 BaseName: "manifest", 149 IsPlainText: true, 150 NotAlternative: true, 151 Rel: "manifest", 152 } 153 154 RobotsTxtFormat = Format{ 155 Name: "robots", 156 MediaType: media.Builtin.TextType, 157 BaseName: "robots", 158 IsPlainText: true, 159 Rel: "alternate", 160 } 161 162 RSSFormat = Format{ 163 Name: "rss", 164 MediaType: media.Builtin.RSSType, 165 BaseName: "index", 166 NoUgly: true, 167 Rel: "alternate", 168 } 169 170 SitemapFormat = Format{ 171 Name: "sitemap", 172 MediaType: media.Builtin.XMLType, 173 BaseName: "sitemap", 174 NoUgly: true, 175 Rel: "sitemap", 176 } 177 ) 178 179 // DefaultFormats contains the default output formats supported by Hugo. 180 var DefaultFormats = Formats{ 181 AMPFormat, 182 CalendarFormat, 183 CSSFormat, 184 CSVFormat, 185 HTMLFormat, 186 JSONFormat, 187 MarkdownFormat, 188 WebAppManifestFormat, 189 RobotsTxtFormat, 190 RSSFormat, 191 SitemapFormat, 192 } 193 194 func init() { 195 sort.Sort(DefaultFormats) 196 } 197 198 // Formats is a slice of Format. 199 // <docsmeta>{ "name": "OutputFormats" }</docsmeta> 200 type Formats []Format 201 202 func (formats Formats) Len() int { return len(formats) } 203 func (formats Formats) Swap(i, j int) { formats[i], formats[j] = formats[j], formats[i] } 204 func (formats Formats) Less(i, j int) bool { 205 fi, fj := formats[i], formats[j] 206 if fi.Weight == fj.Weight { 207 return fi.Name < fj.Name 208 } 209 210 if fj.Weight == 0 { 211 return true 212 } 213 214 return fi.Weight > 0 && fi.Weight < fj.Weight 215 } 216 217 // GetBySuffix gets a output format given as suffix, e.g. "html". 218 // It will return false if no format could be found, or if the suffix given 219 // is ambiguous. 220 // The lookup is case insensitive. 221 func (formats Formats) GetBySuffix(suffix string) (f Format, found bool) { 222 for _, ff := range formats { 223 for _, suffix2 := range ff.MediaType.Suffixes() { 224 if strings.EqualFold(suffix, suffix2) { 225 if found { 226 // ambiguous 227 found = false 228 return 229 } 230 f = ff 231 found = true 232 } 233 } 234 } 235 return 236 } 237 238 // GetByName gets a format by its identifier name. 239 func (formats Formats) GetByName(name string) (f Format, found bool) { 240 for _, ff := range formats { 241 if strings.EqualFold(name, ff.Name) { 242 f = ff 243 found = true 244 return 245 } 246 } 247 return 248 } 249 250 // GetByNames gets a list of formats given a list of identifiers. 251 func (formats Formats) GetByNames(names ...string) (Formats, error) { 252 var types []Format 253 254 for _, name := range names { 255 tpe, ok := formats.GetByName(name) 256 if !ok { 257 return types, fmt.Errorf("OutputFormat with key %q not found", name) 258 } 259 types = append(types, tpe) 260 } 261 return types, nil 262 } 263 264 // FromFilename gets a Format given a filename. 265 func (formats Formats) FromFilename(filename string) (f Format, found bool) { 266 // mytemplate.amp.html 267 // mytemplate.html 268 // mytemplate 269 var ext, outFormat string 270 271 parts := strings.Split(filename, ".") 272 if len(parts) > 2 { 273 outFormat = parts[1] 274 ext = parts[2] 275 } else if len(parts) > 1 { 276 ext = parts[1] 277 } 278 279 if outFormat != "" { 280 return formats.GetByName(outFormat) 281 } 282 283 if ext != "" { 284 f, found = formats.GetBySuffix(ext) 285 if !found && len(parts) == 2 { 286 // For extensionless output formats (e.g. Netlify's _redirects) 287 // we must fall back to using the extension as format lookup. 288 f, found = formats.GetByName(ext) 289 } 290 } 291 return 292 } 293 294 // BaseFilename returns the base filename of f including an extension (ie. 295 // "index.xml"). 296 func (f Format) BaseFilename() string { 297 return f.BaseName + f.MediaType.FirstSuffix.FullSuffix 298 } 299 300 // MarshalJSON returns the JSON encoding of f. 301 // For internal use only. 302 func (f Format) MarshalJSON() ([]byte, error) { 303 type Alias Format 304 return json.Marshal(&struct { 305 MediaType string `json:"mediaType"` 306 Alias 307 }{ 308 MediaType: f.MediaType.String(), 309 Alias: (Alias)(f), 310 }) 311 }