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  }