2019-01-02 06:33:26 -05:00
|
|
|
// Copyright 2019 The Hugo Authors. All rights reserved.
|
2017-03-02 09:35:25 -05:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2023-02-18 15:47:35 -05:00
|
|
|
// Package media contains Media Type (MIME type) related types and functions.
|
2017-03-02 09:35:25 -05:00
|
|
|
package media
|
|
|
|
|
|
|
|
import (
|
2017-04-05 10:18:53 -04:00
|
|
|
"encoding/json"
|
2017-03-02 09:35:25 -05:00
|
|
|
"fmt"
|
2021-12-16 09:12:13 -05:00
|
|
|
"net/http"
|
2017-04-03 11:00:23 -04:00
|
|
|
"strings"
|
2017-04-03 16:39:37 -04:00
|
|
|
)
|
2017-04-03 11:00:23 -04:00
|
|
|
|
2021-12-21 04:35:33 -05:00
|
|
|
var zero Type
|
|
|
|
|
2017-06-20 02:45:52 -04:00
|
|
|
const (
|
2023-01-04 12:24:36 -05:00
|
|
|
DefaultDelimiter = "."
|
2017-06-20 02:45:52 -04:00
|
|
|
)
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// MediaType (also known as MIME type and content type) is a two-part identifier for
|
2017-03-02 09:35:25 -05:00
|
|
|
// file formats and format contents transmitted on the Internet.
|
|
|
|
// For Hugo's use case, we use the top-level type name / subtype name + suffix.
|
2018-07-10 05:55:22 -04:00
|
|
|
// One example would be application/svg+xml
|
2017-03-02 09:35:25 -05:00
|
|
|
// If suffix is not provided, the sub type will be used.
|
2023-01-04 12:24:36 -05:00
|
|
|
// <docsmeta>{ "name": "MediaType" }</docsmeta>
|
2017-03-02 09:35:25 -05:00
|
|
|
type Type struct {
|
2023-01-04 12:24:36 -05:00
|
|
|
// The full MIME type string, e.g. "application/rss+xml".
|
|
|
|
Type string `json:"-"`
|
2021-03-11 03:18:01 -05:00
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// The top-level type name, e.g. "application".
|
|
|
|
MainType string `json:"mainType"`
|
|
|
|
// The subtype name, e.g. "rss".
|
|
|
|
SubType string `json:"subType"`
|
|
|
|
// The delimiter before the suffix, e.g. ".".
|
|
|
|
Delimiter string `json:"delimiter"`
|
|
|
|
|
|
|
|
// FirstSuffix holds the first suffix defined for this MediaType.
|
|
|
|
FirstSuffix SuffixInfo `json:"-"`
|
2018-07-10 05:55:22 -04:00
|
|
|
|
2018-08-28 08:18:12 -04:00
|
|
|
// This is the optional suffix after the "+" in the MIME type,
|
2020-12-16 06:11:32 -05:00
|
|
|
// e.g. "xml" in "application/rss+xml".
|
2018-08-28 08:18:12 -04:00
|
|
|
mimeSuffix string
|
2018-07-10 05:55:22 -04:00
|
|
|
|
2021-03-11 03:18:01 -05:00
|
|
|
// E.g. "jpg,jpeg"
|
|
|
|
// Stored as a string to make Type comparable.
|
2023-01-04 12:24:36 -05:00
|
|
|
// For internal use only.
|
|
|
|
SuffixesCSV string `json:"-"`
|
2021-03-11 03:18:01 -05:00
|
|
|
}
|
2018-07-10 05:55:22 -04:00
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// SuffixInfo holds information about a Media Type's suffix.
|
2021-03-11 03:18:01 -05:00
|
|
|
type SuffixInfo struct {
|
2023-01-04 12:24:36 -05:00
|
|
|
// Suffix is the suffix without the delimiter, e.g. "xml".
|
|
|
|
Suffix string `json:"suffix"`
|
|
|
|
|
|
|
|
// FullSuffix is the suffix with the delimiter, e.g. ".xml".
|
2021-03-11 03:18:01 -05:00
|
|
|
FullSuffix string `json:"fullSuffix"`
|
2017-03-02 09:35:25 -05:00
|
|
|
}
|
|
|
|
|
2021-12-16 09:12:13 -05:00
|
|
|
// FromContent resolve the Type primarily using http.DetectContentType.
|
|
|
|
// If http.DetectContentType resolves to application/octet-stream, a zero Type is returned.
|
|
|
|
// If http.DetectContentType resolves to text/plain or application/xml, we try to get more specific using types and ext.
|
2021-12-21 04:35:33 -05:00
|
|
|
func FromContent(types Types, extensionHints []string, content []byte) Type {
|
2021-12-16 09:12:13 -05:00
|
|
|
t := strings.Split(http.DetectContentType(content), ";")[0]
|
|
|
|
if t == "application/octet-stream" {
|
2021-12-21 04:35:33 -05:00
|
|
|
return zero
|
2021-12-16 09:12:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
var found bool
|
2021-12-21 04:35:33 -05:00
|
|
|
m, found := types.GetByType(t)
|
2021-12-16 09:12:13 -05:00
|
|
|
if !found {
|
|
|
|
if t == "text/xml" {
|
|
|
|
// This is how it's configured in Hugo by default.
|
|
|
|
m, found = types.GetByType("application/xml")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-21 04:35:33 -05:00
|
|
|
if !found {
|
|
|
|
return zero
|
|
|
|
}
|
|
|
|
|
|
|
|
var mm Type
|
|
|
|
|
|
|
|
for _, extension := range extensionHints {
|
|
|
|
extension = strings.TrimPrefix(extension, ".")
|
|
|
|
mm, _, found = types.GetFirstBySuffix(extension)
|
|
|
|
if found {
|
|
|
|
break
|
|
|
|
}
|
2021-12-16 09:12:13 -05:00
|
|
|
}
|
|
|
|
|
2021-12-21 04:35:33 -05:00
|
|
|
if found {
|
|
|
|
if m == mm {
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
if m.IsText() && mm.IsText() {
|
|
|
|
// http.DetectContentType isn't brilliant when it comes to common text formats, so we need to do better.
|
|
|
|
// For now we say that if it's detected to be a text format and the extension/content type in header reports
|
|
|
|
// it to be a text format, then we use that.
|
2021-12-16 09:12:13 -05:00
|
|
|
return mm
|
|
|
|
}
|
2021-12-21 04:35:33 -05:00
|
|
|
|
|
|
|
// E.g. an image with a *.js extension.
|
|
|
|
return zero
|
2021-12-16 09:12:13 -05:00
|
|
|
}
|
2021-12-21 04:35:33 -05:00
|
|
|
|
2021-12-16 09:12:13 -05:00
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2021-03-11 03:18:01 -05:00
|
|
|
// FromStringAndExt creates a Type from a MIME string and a given extension.
|
2018-07-10 05:55:22 -04:00
|
|
|
func FromStringAndExt(t, ext string) (Type, error) {
|
2023-01-16 05:05:28 -05:00
|
|
|
tp, err := FromString(t)
|
2018-07-10 05:55:22 -04:00
|
|
|
if err != nil {
|
|
|
|
return tp, err
|
|
|
|
}
|
2023-01-04 12:24:36 -05:00
|
|
|
tp.SuffixesCSV = strings.TrimPrefix(ext, ".")
|
|
|
|
tp.Delimiter = DefaultDelimiter
|
2021-03-11 03:18:01 -05:00
|
|
|
tp.init()
|
2018-07-10 05:55:22 -04:00
|
|
|
return tp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FromString creates a new Type given a type string on the form MainType/SubType and
|
2017-04-03 16:39:37 -04:00
|
|
|
// an optional suffix, e.g. "text/html" or "text/html+html".
|
2023-01-16 05:05:28 -05:00
|
|
|
func FromString(t string) (Type, error) {
|
2017-04-03 16:39:37 -04:00
|
|
|
t = strings.ToLower(t)
|
|
|
|
parts := strings.Split(t, "/")
|
|
|
|
if len(parts) != 2 {
|
|
|
|
return Type{}, fmt.Errorf("cannot parse %q as a media type", t)
|
|
|
|
}
|
|
|
|
mainType := parts[0]
|
|
|
|
subParts := strings.Split(parts[1], "+")
|
|
|
|
|
Add Hugo Piper with SCSS support and much more
Before this commit, you would have to use page bundles to do image processing etc. in Hugo.
This commit adds
* A new `/assets` top-level project or theme dir (configurable via `assetDir`)
* A new template func, `resources.Get` which can be used to "get a resource" that can be further processed.
This means that you can now do this in your templates (or shortcodes):
```bash
{{ $sunset := (resources.Get "images/sunset.jpg").Fill "300x200" }}
```
This also adds a new `extended` build tag that enables powerful SCSS/SASS support with source maps. To compile this from source, you will also need a C compiler installed:
```
HUGO_BUILD_TAGS=extended mage install
```
Note that you can use output of the SCSS processing later in a non-SCSSS-enabled Hugo.
The `SCSS` processor is a _Resource transformation step_ and it can be chained with the many others in a pipeline:
```bash
{{ $css := resources.Get "styles.scss" | resources.ToCSS | resources.PostCSS | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
The transformation funcs above have aliases, so it can be shortened to:
```bash
{{ $css := resources.Get "styles.scss" | toCSS | postCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
A quick tip would be to avoid the fingerprinting part, and possibly also the not-superfast `postCSS` when you're doing development, as it allows Hugo to be smarter about the rebuilding.
Documentation will follow, but have a look at the demo repo in https://github.com/bep/hugo-sass-test
New functions to create `Resource` objects:
* `resources.Get` (see above)
* `resources.FromString`: Create a Resource from a string.
New `Resource` transformation funcs:
* `resources.ToCSS`: Compile `SCSS` or `SASS` into `CSS`.
* `resources.PostCSS`: Process your CSS with PostCSS. Config file support (project or theme or passed as an option).
* `resources.Minify`: Currently supports `css`, `js`, `json`, `html`, `svg`, `xml`.
* `resources.Fingerprint`: Creates a fingerprinted version of the given Resource with Subresource Integrity..
* `resources.Concat`: Concatenates a list of Resource objects. Think of this as a poor man's bundler.
* `resources.ExecuteAsTemplate`: Parses and executes the given Resource and data context (e.g. .Site) as a Go template.
Fixes #4381
Fixes #4903
Fixes #4858
2018-02-20 04:02:14 -05:00
|
|
|
subType := strings.Split(subParts[0], ";")[0]
|
|
|
|
|
2017-04-03 16:39:37 -04:00
|
|
|
var suffix string
|
|
|
|
|
2018-07-10 05:55:22 -04:00
|
|
|
if len(subParts) > 1 {
|
2017-04-03 16:39:37 -04:00
|
|
|
suffix = subParts[1]
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
var typ string
|
|
|
|
if suffix != "" {
|
|
|
|
typ = mainType + "/" + subType + "+" + suffix
|
|
|
|
} else {
|
|
|
|
typ = mainType + "/" + subType
|
2018-07-10 05:55:22 -04:00
|
|
|
}
|
2023-01-04 12:24:36 -05:00
|
|
|
|
|
|
|
return Type{Type: typ, MainType: mainType, SubType: subType, mimeSuffix: suffix}, nil
|
2017-03-02 09:35:25 -05:00
|
|
|
}
|
|
|
|
|
2022-04-21 04:59:13 -04:00
|
|
|
// For internal use.
|
2017-03-02 09:35:25 -05:00
|
|
|
func (m Type) String() string {
|
2023-01-04 12:24:36 -05:00
|
|
|
return m.Type
|
2017-03-02 09:35:25 -05:00
|
|
|
}
|
|
|
|
|
2021-03-11 03:18:01 -05:00
|
|
|
// Suffixes returns all valid file suffixes for this type.
|
|
|
|
func (m Type) Suffixes() []string {
|
2023-01-04 12:24:36 -05:00
|
|
|
if m.SuffixesCSV == "" {
|
2021-03-11 03:18:01 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
return strings.Split(m.SuffixesCSV, ",")
|
2018-07-10 05:55:22 -04:00
|
|
|
}
|
|
|
|
|
2021-12-16 09:12:13 -05:00
|
|
|
// IsText returns whether this Type is a text format.
|
|
|
|
// Note that this may currently return false negatives.
|
|
|
|
// TODO(bep) improve
|
2023-01-04 12:24:36 -05:00
|
|
|
// For internal use.
|
2021-12-16 09:12:13 -05:00
|
|
|
func (m Type) IsText() bool {
|
|
|
|
if m.MainType == "text" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
switch m.SubType {
|
2023-01-04 12:24:36 -05:00
|
|
|
case "javascript", "json", "rss", "xml", "svg", "toml", "yml", "yaml":
|
2021-12-16 09:12:13 -05:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
func InitMediaType(m *Type) {
|
|
|
|
m.init()
|
|
|
|
}
|
|
|
|
|
2021-03-11 03:18:01 -05:00
|
|
|
func (m *Type) init() {
|
|
|
|
m.FirstSuffix.FullSuffix = ""
|
|
|
|
m.FirstSuffix.Suffix = ""
|
|
|
|
if suffixes := m.Suffixes(); suffixes != nil {
|
|
|
|
m.FirstSuffix.Suffix = suffixes[0]
|
|
|
|
m.FirstSuffix.FullSuffix = m.Delimiter + m.FirstSuffix.Suffix
|
2018-07-10 05:55:22 -04:00
|
|
|
}
|
2021-03-11 03:18:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func newMediaType(main, sub string, suffixes []string) Type {
|
2023-01-04 12:24:36 -05:00
|
|
|
t := Type{MainType: main, SubType: sub, SuffixesCSV: strings.Join(suffixes, ","), Delimiter: DefaultDelimiter}
|
2021-03-11 03:18:01 -05:00
|
|
|
t.init()
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMediaTypeWithMimeSuffix(main, sub, mimeSuffix string, suffixes []string) Type {
|
|
|
|
mt := newMediaType(main, sub, suffixes)
|
|
|
|
mt.mimeSuffix = mimeSuffix
|
|
|
|
mt.init()
|
|
|
|
return mt
|
2017-06-20 02:45:52 -04:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:24:23 -04:00
|
|
|
// Types is a slice of media types.
|
2023-01-04 12:24:36 -05:00
|
|
|
// <docsmeta>{ "name": "MediaTypes" }</docsmeta>
|
2017-04-03 16:39:37 -04:00
|
|
|
type Types []Type
|
|
|
|
|
|
|
|
func (t Types) Len() int { return len(t) }
|
|
|
|
func (t Types) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
2023-01-04 12:24:36 -05:00
|
|
|
func (t Types) Less(i, j int) bool { return t[i].Type < t[j].Type }
|
2017-04-03 16:39:37 -04:00
|
|
|
|
2018-09-06 18:24:23 -04:00
|
|
|
// GetByType returns a media type for tp.
|
2017-04-03 16:39:37 -04:00
|
|
|
func (t Types) GetByType(tp string) (Type, bool) {
|
|
|
|
for _, tt := range t {
|
2023-01-04 12:24:36 -05:00
|
|
|
if strings.EqualFold(tt.Type, tp) {
|
2017-04-03 16:39:37 -04:00
|
|
|
return tt, true
|
|
|
|
}
|
|
|
|
}
|
2018-07-10 05:55:22 -04:00
|
|
|
|
|
|
|
if !strings.Contains(tp, "+") {
|
|
|
|
// Try with the main and sub type
|
|
|
|
parts := strings.Split(tp, "/")
|
|
|
|
if len(parts) == 2 {
|
|
|
|
return t.GetByMainSubType(parts[0], parts[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-03 16:39:37 -04:00
|
|
|
return Type{}, false
|
|
|
|
}
|
|
|
|
|
2018-08-28 08:18:12 -04:00
|
|
|
// BySuffix will return all media types matching a suffix.
|
|
|
|
func (t Types) BySuffix(suffix string) []Type {
|
2021-03-11 03:18:01 -05:00
|
|
|
suffix = strings.ToLower(suffix)
|
2018-08-28 08:18:12 -04:00
|
|
|
var types []Type
|
|
|
|
for _, tt := range t {
|
2021-03-11 03:18:01 -05:00
|
|
|
if tt.hasSuffix(suffix) {
|
2018-08-28 08:18:12 -04:00
|
|
|
types = append(types, tt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return types
|
|
|
|
}
|
|
|
|
|
2021-03-11 03:18:01 -05:00
|
|
|
// GetFirstBySuffix will return the first type matching the given suffix.
|
|
|
|
func (t Types) GetFirstBySuffix(suffix string) (Type, SuffixInfo, bool) {
|
|
|
|
suffix = strings.ToLower(suffix)
|
Add Hugo Piper with SCSS support and much more
Before this commit, you would have to use page bundles to do image processing etc. in Hugo.
This commit adds
* A new `/assets` top-level project or theme dir (configurable via `assetDir`)
* A new template func, `resources.Get` which can be used to "get a resource" that can be further processed.
This means that you can now do this in your templates (or shortcodes):
```bash
{{ $sunset := (resources.Get "images/sunset.jpg").Fill "300x200" }}
```
This also adds a new `extended` build tag that enables powerful SCSS/SASS support with source maps. To compile this from source, you will also need a C compiler installed:
```
HUGO_BUILD_TAGS=extended mage install
```
Note that you can use output of the SCSS processing later in a non-SCSSS-enabled Hugo.
The `SCSS` processor is a _Resource transformation step_ and it can be chained with the many others in a pipeline:
```bash
{{ $css := resources.Get "styles.scss" | resources.ToCSS | resources.PostCSS | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
The transformation funcs above have aliases, so it can be shortened to:
```bash
{{ $css := resources.Get "styles.scss" | toCSS | postCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
A quick tip would be to avoid the fingerprinting part, and possibly also the not-superfast `postCSS` when you're doing development, as it allows Hugo to be smarter about the rebuilding.
Documentation will follow, but have a look at the demo repo in https://github.com/bep/hugo-sass-test
New functions to create `Resource` objects:
* `resources.Get` (see above)
* `resources.FromString`: Create a Resource from a string.
New `Resource` transformation funcs:
* `resources.ToCSS`: Compile `SCSS` or `SASS` into `CSS`.
* `resources.PostCSS`: Process your CSS with PostCSS. Config file support (project or theme or passed as an option).
* `resources.Minify`: Currently supports `css`, `js`, `json`, `html`, `svg`, `xml`.
* `resources.Fingerprint`: Creates a fingerprinted version of the given Resource with Subresource Integrity..
* `resources.Concat`: Concatenates a list of Resource objects. Think of this as a poor man's bundler.
* `resources.ExecuteAsTemplate`: Parses and executes the given Resource and data context (e.g. .Site) as a Go template.
Fixes #4381
Fixes #4903
Fixes #4858
2018-02-20 04:02:14 -05:00
|
|
|
for _, tt := range t {
|
2021-03-11 03:18:01 -05:00
|
|
|
if tt.hasSuffix(suffix) {
|
|
|
|
return tt, SuffixInfo{
|
|
|
|
FullSuffix: tt.Delimiter + suffix,
|
|
|
|
Suffix: suffix,
|
|
|
|
}, true
|
Add Hugo Piper with SCSS support and much more
Before this commit, you would have to use page bundles to do image processing etc. in Hugo.
This commit adds
* A new `/assets` top-level project or theme dir (configurable via `assetDir`)
* A new template func, `resources.Get` which can be used to "get a resource" that can be further processed.
This means that you can now do this in your templates (or shortcodes):
```bash
{{ $sunset := (resources.Get "images/sunset.jpg").Fill "300x200" }}
```
This also adds a new `extended` build tag that enables powerful SCSS/SASS support with source maps. To compile this from source, you will also need a C compiler installed:
```
HUGO_BUILD_TAGS=extended mage install
```
Note that you can use output of the SCSS processing later in a non-SCSSS-enabled Hugo.
The `SCSS` processor is a _Resource transformation step_ and it can be chained with the many others in a pipeline:
```bash
{{ $css := resources.Get "styles.scss" | resources.ToCSS | resources.PostCSS | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
The transformation funcs above have aliases, so it can be shortened to:
```bash
{{ $css := resources.Get "styles.scss" | toCSS | postCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
A quick tip would be to avoid the fingerprinting part, and possibly also the not-superfast `postCSS` when you're doing development, as it allows Hugo to be smarter about the rebuilding.
Documentation will follow, but have a look at the demo repo in https://github.com/bep/hugo-sass-test
New functions to create `Resource` objects:
* `resources.Get` (see above)
* `resources.FromString`: Create a Resource from a string.
New `Resource` transformation funcs:
* `resources.ToCSS`: Compile `SCSS` or `SASS` into `CSS`.
* `resources.PostCSS`: Process your CSS with PostCSS. Config file support (project or theme or passed as an option).
* `resources.Minify`: Currently supports `css`, `js`, `json`, `html`, `svg`, `xml`.
* `resources.Fingerprint`: Creates a fingerprinted version of the given Resource with Subresource Integrity..
* `resources.Concat`: Concatenates a list of Resource objects. Think of this as a poor man's bundler.
* `resources.ExecuteAsTemplate`: Parses and executes the given Resource and data context (e.g. .Site) as a Go template.
Fixes #4381
Fixes #4903
Fixes #4858
2018-02-20 04:02:14 -05:00
|
|
|
}
|
|
|
|
}
|
2021-03-11 03:18:01 -05:00
|
|
|
return Type{}, SuffixInfo{}, false
|
Add Hugo Piper with SCSS support and much more
Before this commit, you would have to use page bundles to do image processing etc. in Hugo.
This commit adds
* A new `/assets` top-level project or theme dir (configurable via `assetDir`)
* A new template func, `resources.Get` which can be used to "get a resource" that can be further processed.
This means that you can now do this in your templates (or shortcodes):
```bash
{{ $sunset := (resources.Get "images/sunset.jpg").Fill "300x200" }}
```
This also adds a new `extended` build tag that enables powerful SCSS/SASS support with source maps. To compile this from source, you will also need a C compiler installed:
```
HUGO_BUILD_TAGS=extended mage install
```
Note that you can use output of the SCSS processing later in a non-SCSSS-enabled Hugo.
The `SCSS` processor is a _Resource transformation step_ and it can be chained with the many others in a pipeline:
```bash
{{ $css := resources.Get "styles.scss" | resources.ToCSS | resources.PostCSS | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
The transformation funcs above have aliases, so it can be shortened to:
```bash
{{ $css := resources.Get "styles.scss" | toCSS | postCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $styles.RelPermalink }}" integrity="{{ $styles.Data.Digest }}" media="screen">
```
A quick tip would be to avoid the fingerprinting part, and possibly also the not-superfast `postCSS` when you're doing development, as it allows Hugo to be smarter about the rebuilding.
Documentation will follow, but have a look at the demo repo in https://github.com/bep/hugo-sass-test
New functions to create `Resource` objects:
* `resources.Get` (see above)
* `resources.FromString`: Create a Resource from a string.
New `Resource` transformation funcs:
* `resources.ToCSS`: Compile `SCSS` or `SASS` into `CSS`.
* `resources.PostCSS`: Process your CSS with PostCSS. Config file support (project or theme or passed as an option).
* `resources.Minify`: Currently supports `css`, `js`, `json`, `html`, `svg`, `xml`.
* `resources.Fingerprint`: Creates a fingerprinted version of the given Resource with Subresource Integrity..
* `resources.Concat`: Concatenates a list of Resource objects. Think of this as a poor man's bundler.
* `resources.ExecuteAsTemplate`: Parses and executes the given Resource and data context (e.g. .Site) as a Go template.
Fixes #4381
Fixes #4903
Fixes #4858
2018-02-20 04:02:14 -05:00
|
|
|
}
|
|
|
|
|
2017-04-03 16:39:37 -04:00
|
|
|
// GetBySuffix gets a media type given as suffix, e.g. "html".
|
|
|
|
// It will return false if no format could be found, or if the suffix given
|
|
|
|
// is ambiguous.
|
|
|
|
// The lookup is case insensitive.
|
2021-03-11 03:18:01 -05:00
|
|
|
func (t Types) GetBySuffix(suffix string) (tp Type, si SuffixInfo, found bool) {
|
|
|
|
suffix = strings.ToLower(suffix)
|
2017-04-03 16:39:37 -04:00
|
|
|
for _, tt := range t {
|
2021-03-11 03:18:01 -05:00
|
|
|
if tt.hasSuffix(suffix) {
|
2017-04-03 16:39:37 -04:00
|
|
|
if found {
|
|
|
|
// ambiguous
|
|
|
|
found = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
tp = tt
|
2021-03-11 03:18:01 -05:00
|
|
|
si = SuffixInfo{
|
|
|
|
FullSuffix: tt.Delimiter + suffix,
|
|
|
|
Suffix: suffix,
|
|
|
|
}
|
2017-04-03 16:39:37 -04:00
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
func (t Types) IsTextSuffix(suffix string) bool {
|
|
|
|
suffix = strings.ToLower(suffix)
|
|
|
|
for _, tt := range t {
|
|
|
|
if tt.hasSuffix(suffix) {
|
|
|
|
return tt.IsText()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-03-11 03:18:01 -05:00
|
|
|
func (m Type) hasSuffix(suffix string) bool {
|
2023-01-04 12:24:36 -05:00
|
|
|
return strings.Contains(","+m.SuffixesCSV+",", ","+suffix+",")
|
2018-07-10 05:55:22 -04:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:24:23 -04:00
|
|
|
// GetByMainSubType gets a media type given a main and a sub type e.g. "text" and "plain".
|
2018-07-10 05:55:22 -04:00
|
|
|
// It will return false if no format could be found, or if the combination given
|
|
|
|
// is ambiguous.
|
|
|
|
// The lookup is case insensitive.
|
|
|
|
func (t Types) GetByMainSubType(mainType, subType string) (tp Type, found bool) {
|
|
|
|
for _, tt := range t {
|
|
|
|
if strings.EqualFold(mainType, tt.MainType) && strings.EqualFold(subType, tt.SubType) {
|
|
|
|
if found {
|
|
|
|
// ambiguous
|
|
|
|
found = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tp = tt
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-21 11:59:03 -04:00
|
|
|
// IsZero reports whether this Type represents a zero value.
|
2022-04-21 04:59:13 -04:00
|
|
|
// For internal use.
|
2020-07-21 11:59:03 -04:00
|
|
|
func (m Type) IsZero() bool {
|
|
|
|
return m.SubType == ""
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:24:23 -04:00
|
|
|
// MarshalJSON returns the JSON encoding of m.
|
2022-04-21 04:59:13 -04:00
|
|
|
// For internal use.
|
2017-09-25 22:25:33 -04:00
|
|
|
func (m Type) MarshalJSON() ([]byte, error) {
|
2017-04-05 10:18:53 -04:00
|
|
|
type Alias Type
|
|
|
|
return json.Marshal(&struct {
|
|
|
|
Alias
|
2021-03-11 03:18:01 -05:00
|
|
|
Type string `json:"type"`
|
|
|
|
String string `json:"string"`
|
|
|
|
Suffixes []string `json:"suffixes"`
|
2017-04-05 10:18:53 -04:00
|
|
|
}{
|
2021-03-11 03:18:01 -05:00
|
|
|
Alias: (Alias)(m),
|
2023-01-04 12:24:36 -05:00
|
|
|
Type: m.Type,
|
2021-03-11 03:18:01 -05:00
|
|
|
String: m.String(),
|
2023-01-04 12:24:36 -05:00
|
|
|
Suffixes: strings.Split(m.SuffixesCSV, ","),
|
2017-04-05 10:18:53 -04:00
|
|
|
})
|
|
|
|
}
|