2018-10-03 08:58:09 -04:00
// Copyright 2018 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
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 (
_ TemplateExecutor = ( * TemplateAdapter ) ( nil )
)
// 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
2017-02-17 07:30:50 -05:00
MarkReady ( )
2017-03-27 14:43:49 -04:00
RebuildClone ( )
}
// TemplateFinder finds templates.
type TemplateFinder 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
}
// Template is the common interface between text/template and html/template.
type Template interface {
Execute ( wr io . Writer , data interface { } ) error
Name ( ) string
}
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
// 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 {
2018-10-17 02:24:45 -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
}
var identifiersRe = regexp . MustCompile ( "at \\<(.*?)\\>:" )
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 {
f , realFilename , err := t . fileAndFilename ( t . Name ( ) )
if err != nil {
return err
}
defer f . Close ( )
master , hasMaster := t . NameBaseTemplateName [ name ]
2018-10-17 02:24:45 -04:00
ferr1 := errors . Wrapf ( inerr , "execute of template %q failed" , realFilename )
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.
lineMatcher := func ( le herrors . FileError , lineNumber int , line string ) bool {
if le . LineNumber ( ) != lineNumber {
return false
}
if ! hasMaster {
return true
}
identifiers := t . extractIdentifiers ( le . Error ( ) )
for _ , id := range identifiers {
if strings . Contains ( line , id ) {
return true
}
}
return false
}
// TODO(bep) 2errors text vs HTML
2018-10-17 02:24:45 -04:00
fe , ok := herrors . WithFileContext ( ferr1 , f , "go-html-template" , 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-17 02:24:45 -04:00
ferr2 := errors . Wrapf ( inerr , "execute of template %q failed" , realFilename )
fe , ok = herrors . WithFileContext ( ferr2 , f , "go-html-template" , lineMatcher )
if ! ok {
// Return the most specific.
return ferr1
}
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 {
return nil , "" , errors . Wrapf ( err , "failed to Stat %q" , filename )
}
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
}