2019-01-02 06:33:26 -05:00
// Copyright 2019 The Hugo Authors. All rights reserved.
2017-03-27 14:43:49 -04: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.
2014-11-20 12:32:21 -05:00
package tpl
2013-08-31 20:35:17 -04:00
import (
2018-10-03 08:58:09 -04:00
"fmt"
2017-04-02 08:20:34 -04:00
"io"
2018-10-03 08:58:09 -04:00
"path/filepath"
"regexp"
"strings"
2017-09-26 14:03:04 -04:00
"time"
2017-03-27 14:43:49 -04:00
2019-01-02 06:33:26 -05:00
"github.com/gohugoio/hugo/output"
2018-10-03 08:58:09 -04:00
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"
2017-03-27 14:43:49 -04:00
"html/template"
texttemplate "text/template"
2018-10-03 08:58:09 -04:00
"text/template/parse"
2017-03-27 14:43:49 -04:00
2017-06-13 12:42:45 -04:00
bp "github.com/gohugoio/hugo/bufferpool"
2017-09-26 14:03:04 -04:00
"github.com/gohugoio/hugo/metrics"
2018-10-03 08:58:09 -04:00
"github.com/pkg/errors"
2017-03-27 14:43:49 -04:00
)
2017-03-27 14:43:49 -04:00
var (
2019-01-02 06:33:26 -05:00
_ TemplateExecutor = ( * TemplateAdapter ) ( nil )
_ TemplateInfoProvider = ( * TemplateAdapter ) ( nil )
2017-03-27 14:43:49 -04:00
)
// TemplateHandler manages the collection of templates.
type TemplateHandler interface {
TemplateFinder
2017-02-17 07:30:50 -05:00
AddTemplate ( name , tpl string ) error
2017-03-27 14:43:49 -04:00
AddLateTemplate ( name , tpl string ) error
2018-10-03 08:58:09 -04:00
LoadTemplates ( prefix string ) error
2017-03-27 14:43:49 -04:00
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
NewTextTemplate ( ) TemplateParseFinder
2019-04-16 03:13:55 -04:00
MarkReady ( ) error
2017-03-27 14:43:49 -04:00
RebuildClone ( )
}
2019-01-02 06:33:26 -05:00
// TemplateVariants describes the possible variants of a template.
// All of these may be empty.
type TemplateVariants struct {
Language string
OutputFormat output . Format
}
2017-03-27 14:43:49 -04:00
// TemplateFinder finds templates.
type TemplateFinder interface {
2019-01-02 06:33:26 -05:00
TemplateLookup
TemplateLookupVariant
}
type TemplateLookup interface {
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
Lookup ( name string ) ( Template , bool )
2017-03-27 14:43:49 -04:00
}
2019-01-02 06:33:26 -05:00
type TemplateLookupVariant interface {
// TODO(bep) this currently only works for shortcodes.
// We may unify and expand this variant pattern to the
// other templates, but we need this now for the shortcodes to
// quickly determine if a shortcode has a template for a given
// output format.
// It returns the template, if it was found or not and if there are
// alternative representations (output format, language).
// We are currently only interested in output formats, so we should improve
// this for speed.
LookupVariant ( name string , variants TemplateVariants ) ( Template , bool , bool )
}
2017-03-27 14:43:49 -04:00
// Template is the common interface between text/template and html/template.
type Template interface {
Execute ( wr io . Writer , data interface { } ) error
Name ( ) string
}
2019-01-02 06:33:26 -05:00
// TemplateInfoProvider provides some contextual information about a template.
type TemplateInfoProvider interface {
TemplateInfo ( ) Info
}
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
// TemplateParser is used to parse ad-hoc templates, e.g. in the Resource chain.
type TemplateParser interface {
Parse ( name , tpl string ) ( Template , error )
}
// TemplateParseFinder provides both parsing and finding.
type TemplateParseFinder interface {
TemplateParser
TemplateFinder
}
2017-03-27 14:43:49 -04:00
// TemplateExecutor adds some extras to Template.
type TemplateExecutor interface {
Template
ExecuteToString ( data interface { } ) ( string , error )
Tree ( ) string
}
2017-05-06 14:15:28 -04:00
// TemplateDebugger prints some debug info to stdoud.
type TemplateDebugger interface {
Debug ( )
}
2017-03-27 14:43:49 -04:00
// TemplateAdapter implements the TemplateExecutor interface.
type TemplateAdapter struct {
Template
2017-09-26 14:03:04 -04:00
Metrics metrics . Provider
2018-10-03 08:58:09 -04:00
2019-01-02 06:33:26 -05:00
Info Info
2018-10-03 08:58:09 -04:00
// The filesystem where the templates are stored.
Fs afero . Fs
// Maps to base template if relevant.
NameBaseTemplateName map [ string ] string
}
var baseOfRe = regexp . MustCompile ( "template: (.*?):" )
func extractBaseOf ( err string ) string {
m := baseOfRe . FindStringSubmatch ( err )
if len ( m ) == 2 {
return m [ 1 ]
}
return ""
2017-03-27 14:43:49 -04:00
}
2017-08-19 03:57:37 -04:00
// Execute executes the current template. The actual execution is performed
// by the embedded text or html template, but we add an implementation here so
// we can add a timer for some metrics.
2018-10-03 08:58:09 -04:00
func ( t * TemplateAdapter ) Execute ( w io . Writer , data interface { } ) ( execErr error ) {
defer func ( ) {
// Panics in templates are a little bit too common (nil pointers etc.)
2018-10-17 02:24:45 -04:00
// See https://github.com/gohugoio/hugo/issues/5327
2018-10-03 08:58:09 -04:00
if r := recover ( ) ; r != nil {
2019-03-24 05:11:16 -04:00
execErr = t . addFileContext ( t . Name ( ) , fmt . Errorf ( ` panic in Execute: %s. See "https://github.com/gohugoio/hugo/issues/5327" for the reason why we cannot provide a better error message for this ` , r ) )
2018-10-03 08:58:09 -04:00
}
} ( )
2017-09-26 14:03:04 -04:00
if t . Metrics != nil {
defer t . Metrics . MeasureSince ( t . Name ( ) , time . Now ( ) )
}
2018-10-03 08:58:09 -04:00
execErr = t . Template . Execute ( w , data )
if execErr != nil {
execErr = t . addFileContext ( t . Name ( ) , execErr )
}
return
}
2019-01-02 06:33:26 -05:00
func ( t * TemplateAdapter ) TemplateInfo ( ) Info {
return t . Info
}
2018-10-24 07:32:46 -04:00
// The identifiers may be truncated in the log, e.g.
// "executing "main" at <$scaled.SRelPermalin...>: can't evaluate field SRelPermalink in type *resource.Image"
2019-03-24 05:11:16 -04:00
var identifiersRe = regexp . MustCompile ( ` at \<(.*?)(\. { 3})?\>: ` )
2018-10-03 08:58:09 -04:00
func ( t * TemplateAdapter ) extractIdentifiers ( line string ) [ ] string {
m := identifiersRe . FindAllStringSubmatch ( line , - 1 )
identifiers := make ( [ ] string , len ( m ) )
for i := 0 ; i < len ( m ) ; i ++ {
identifiers [ i ] = m [ i ] [ 1 ]
}
return identifiers
}
func ( t * TemplateAdapter ) addFileContext ( name string , inerr error ) error {
2018-10-21 06:20:21 -04:00
if strings . HasPrefix ( t . Name ( ) , "_internal" ) {
return inerr
}
2018-10-03 08:58:09 -04:00
f , realFilename , err := t . fileAndFilename ( t . Name ( ) )
if err != nil {
2018-10-21 06:20:21 -04:00
return inerr
2018-10-03 08:58:09 -04:00
}
defer f . Close ( )
master , hasMaster := t . NameBaseTemplateName [ name ]
2018-10-21 06:20:21 -04:00
ferr := errors . Wrap ( inerr , "execute of template failed" )
2018-10-03 08:58:09 -04:00
// Since this can be a composite of multiple template files (single.html + baseof.html etc.)
// we potentially need to look in both -- and cannot rely on line number alone.
2018-10-23 02:54:10 -04:00
lineMatcher := func ( m herrors . LineMatcher ) bool {
2018-11-01 06:28:30 -04:00
if m . Position . LineNumber != m . LineNumber {
2018-10-03 08:58:09 -04:00
return false
}
if ! hasMaster {
return true
}
2018-11-01 06:28:30 -04:00
identifiers := t . extractIdentifiers ( m . Error . Error ( ) )
2018-10-03 08:58:09 -04:00
for _ , id := range identifiers {
2018-10-23 02:54:10 -04:00
if strings . Contains ( m . Line , id ) {
2018-10-03 08:58:09 -04:00
return true
}
}
return false
}
2018-10-23 08:37:09 -04:00
2018-10-21 06:20:21 -04:00
fe , ok := herrors . WithFileContext ( ferr , realFilename , f , lineMatcher )
2018-10-03 08:58:09 -04:00
if ok || ! hasMaster {
return fe
}
// Try the base template if relevant
f , realFilename , err = t . fileAndFilename ( master )
if err != nil {
return err
}
defer f . Close ( )
2018-10-21 06:20:21 -04:00
fe , ok = herrors . WithFileContext ( ferr , realFilename , f , lineMatcher )
2018-10-17 02:24:45 -04:00
if ! ok {
// Return the most specific.
2018-10-21 06:20:21 -04:00
return ferr
2018-10-17 02:24:45 -04:00
}
2018-10-03 08:58:09 -04:00
return fe
}
func ( t * TemplateAdapter ) fileAndFilename ( name string ) ( afero . File , string , error ) {
fs := t . Fs
filename := filepath . FromSlash ( name )
fi , err := fs . Stat ( filename )
if err != nil {
2018-10-21 06:20:21 -04:00
return nil , "" , err
2018-10-03 08:58:09 -04:00
}
f , err := fs . Open ( filename )
if err != nil {
return nil , "" , errors . Wrapf ( err , "failed to open template file %q:" , filename )
}
return f , fi . ( hugofs . RealFilenameInfo ) . RealFilename ( ) , nil
2017-08-19 03:57:37 -04:00
}
2017-03-27 14:43:49 -04:00
// ExecuteToString executes the current template and returns the result as a
// string.
func ( t * TemplateAdapter ) ExecuteToString ( data interface { } ) ( string , error ) {
b := bp . GetBuffer ( )
defer bp . PutBuffer ( b )
if err := t . Execute ( b , data ) ; err != nil {
return "" , err
}
return b . String ( ) , nil
}
// Tree returns the template Parse tree as a string.
// Note: this isn't safe for parallel execution on the same template
// vs Lookup and Execute.
func ( t * TemplateAdapter ) Tree ( ) string {
var tree * parse . Tree
switch tt := t . Template . ( type ) {
case * template . Template :
tree = tt . Tree
case * texttemplate . Template :
tree = tt . Tree
default :
panic ( "Unknown template" )
}
2017-04-05 16:11:24 -04:00
if tree == nil || tree . Root == nil {
2017-03-27 14:43:49 -04:00
return ""
}
s := tree . Root . String ( )
return s
}
2017-08-02 08:25:05 -04:00
// TemplateFuncsGetter allows to get a map of functions.
2017-04-30 15:52:56 -04:00
type TemplateFuncsGetter interface {
GetFuncs ( ) map [ string ] interface { }
}
2017-03-27 14:43:49 -04:00
// TemplateTestMocker adds a way to override some template funcs during tests.
// The interface is named so it's not used in regular application code.
type TemplateTestMocker interface {
SetFuncs ( funcMap map [ string ] interface { } )
2015-01-30 18:56:25 -05:00
}