2017-06-24 20:00:19 +02:00
|
|
|
package pipelines
|
2017-06-24 19:30:01 +02:00
|
|
|
|
|
|
|
import (
|
2017-07-09 20:31:47 +02:00
|
|
|
"bytes"
|
2017-06-24 19:30:01 +02:00
|
|
|
"io"
|
|
|
|
|
|
|
|
"github.com/osteele/gojekyll/config"
|
2017-07-01 01:40:52 +02:00
|
|
|
"github.com/osteele/gojekyll/filters"
|
|
|
|
"github.com/osteele/gojekyll/tags"
|
2017-06-24 19:30:01 +02:00
|
|
|
"github.com/osteele/gojekyll/templates"
|
2017-07-09 22:17:20 +02:00
|
|
|
"github.com/osteele/gojekyll/utils"
|
2017-07-01 01:37:31 +02:00
|
|
|
"github.com/osteele/liquid"
|
2017-06-24 19:30:01 +02:00
|
|
|
"github.com/russross/blackfriday"
|
|
|
|
)
|
|
|
|
|
2017-06-24 20:00:19 +02:00
|
|
|
// PipelineInterface applies transformations to a document.
|
|
|
|
type PipelineInterface interface {
|
2017-07-01 05:56:29 +02:00
|
|
|
ApplyLayout(string, []byte, map[string]interface{}) ([]byte, error)
|
2017-06-24 20:00:19 +02:00
|
|
|
OutputExt(pathname string) string
|
2017-07-09 17:57:20 +02:00
|
|
|
Render(io.Writer, []byte, string, int, map[string]interface{}) ([]byte, error)
|
2017-06-24 20:00:19 +02:00
|
|
|
}
|
|
|
|
|
2017-06-24 19:30:01 +02:00
|
|
|
// Pipeline applies a rendering transformation to a file.
|
|
|
|
type Pipeline struct {
|
2017-06-29 13:41:14 +02:00
|
|
|
PipelineOptions
|
2017-06-24 19:30:01 +02:00
|
|
|
config config.Config
|
2017-07-10 15:17:31 +02:00
|
|
|
liquidEngine *liquid.Engine
|
2017-06-24 19:30:01 +02:00
|
|
|
sassTempDir string
|
2017-07-10 00:19:22 +02:00
|
|
|
sassHash string
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// PipelineOptions configures a pipeline.
|
|
|
|
type PipelineOptions struct {
|
2017-07-01 01:37:31 +02:00
|
|
|
RelativeFilenameToURL tags.LinkTagHandler
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewPipeline makes a rendering pipeline.
|
2017-06-29 13:41:14 +02:00
|
|
|
func NewPipeline(c config.Config, options PipelineOptions) (*Pipeline, error) {
|
|
|
|
p := Pipeline{PipelineOptions: options, config: c}
|
2017-06-29 13:27:43 +02:00
|
|
|
p.liquidEngine = p.makeLiquidEngine()
|
2017-06-24 19:30:01 +02:00
|
|
|
if err := p.CopySassFileIncludes(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &p, nil
|
|
|
|
}
|
|
|
|
|
2017-07-01 05:10:58 +02:00
|
|
|
// SourceDir returns the site source directory. Seeing how far we can bend
|
|
|
|
// the Law of Demeter.
|
2017-07-01 03:06:12 +02:00
|
|
|
func (p *Pipeline) SourceDir() string {
|
|
|
|
return p.config.Source
|
|
|
|
}
|
|
|
|
|
2017-07-01 01:40:52 +02:00
|
|
|
// TemplateEngine returns the Liquid engine.
|
2017-07-10 15:17:31 +02:00
|
|
|
func (p *Pipeline) TemplateEngine() *liquid.Engine {
|
2017-06-30 18:53:34 +02:00
|
|
|
return p.liquidEngine
|
|
|
|
}
|
|
|
|
|
2017-06-24 19:30:01 +02:00
|
|
|
// OutputExt returns the output extension.
|
|
|
|
func (p *Pipeline) OutputExt(pathname string) string {
|
|
|
|
return p.config.OutputExt(pathname)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render returns nil iff it wrote to the writer
|
2017-07-09 17:57:20 +02:00
|
|
|
func (p *Pipeline) Render(w io.Writer, b []byte, filename string, lineNo int, e map[string]interface{}) ([]byte, error) {
|
2017-07-09 15:37:23 +02:00
|
|
|
if p.config.IsSASSPath(filename) {
|
2017-07-09 20:31:47 +02:00
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
if err := p.WriteSass(buf, b); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
2017-07-09 17:57:20 +02:00
|
|
|
b, err := p.renderTemplate(b, e, filename, lineNo)
|
2017-06-24 19:30:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if p.config.IsMarkdown(filename) {
|
|
|
|
b = blackfriday.MarkdownCommon(b)
|
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
2017-07-09 17:57:20 +02:00
|
|
|
func (p *Pipeline) renderTemplate(src []byte, b map[string]interface{}, filename string, lineNo int) ([]byte, error) {
|
2017-07-04 23:13:47 +02:00
|
|
|
tpl, err := p.liquidEngine.ParseTemplate(src)
|
|
|
|
if err != nil {
|
2017-07-12 13:27:46 +02:00
|
|
|
return nil, utils.WrapPathError(err, filename)
|
2017-07-04 23:13:47 +02:00
|
|
|
}
|
2017-07-09 17:57:20 +02:00
|
|
|
tpl.SetSourceLocation(filename, lineNo)
|
2017-07-04 23:13:47 +02:00
|
|
|
out, err := tpl.Render(b)
|
2017-06-24 19:30:01 +02:00
|
|
|
if err != nil {
|
2017-07-12 13:27:46 +02:00
|
|
|
return nil, utils.WrapPathError(err, filename)
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
2017-07-02 05:54:12 +02:00
|
|
|
return out, err
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
|
|
|
|
2017-06-28 04:56:41 +02:00
|
|
|
// ApplyLayout applies the named layout to the data.
|
2017-07-01 05:56:29 +02:00
|
|
|
func (p *Pipeline) ApplyLayout(name string, data []byte, e map[string]interface{}) ([]byte, error) {
|
2017-06-24 19:30:01 +02:00
|
|
|
for name != "" {
|
2017-07-01 05:56:29 +02:00
|
|
|
var lfm map[string]interface{}
|
2017-07-09 17:57:20 +02:00
|
|
|
tpl, err := p.FindLayout(name, &lfm)
|
2017-06-24 19:30:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-07-09 22:17:20 +02:00
|
|
|
b := utils.MergeStringMaps(e, map[string]interface{}{
|
2017-06-28 04:56:41 +02:00
|
|
|
"content": string(data),
|
2017-06-24 19:30:01 +02:00
|
|
|
"layout": lfm,
|
|
|
|
})
|
2017-07-09 17:57:20 +02:00
|
|
|
data, err = tpl.Render(b)
|
2017-06-24 19:30:01 +02:00
|
|
|
if err != nil {
|
2017-07-12 13:27:46 +02:00
|
|
|
return nil, utils.WrapPathError(err, name)
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
2017-07-01 05:56:29 +02:00
|
|
|
name = templates.VariableMap(lfm).String("layout", "")
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
2017-06-28 04:56:41 +02:00
|
|
|
return data, nil
|
2017-06-24 19:30:01 +02:00
|
|
|
}
|
|
|
|
|
2017-07-10 15:17:31 +02:00
|
|
|
func (p *Pipeline) makeLiquidEngine() *liquid.Engine {
|
2017-06-29 13:27:43 +02:00
|
|
|
engine := liquid.NewEngine()
|
2017-07-10 15:17:31 +02:00
|
|
|
filters.AddJekyllFilters(engine, &p.config)
|
|
|
|
tags.AddJekyllTags(engine, &p.config, p.RelativeFilenameToURL)
|
2017-06-24 19:30:01 +02:00
|
|
|
return engine
|
|
|
|
}
|