2017-06-17 16:51:32 +02:00
|
|
|
package gojekyll
|
2017-06-17 01:34:39 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
|
2017-06-17 02:11:52 +02:00
|
|
|
"github.com/osteele/gojekyll/helpers"
|
2017-06-21 15:12:48 +02:00
|
|
|
"github.com/osteele/gojekyll/liquid"
|
2017-06-17 01:34:39 +02:00
|
|
|
"github.com/russross/blackfriday"
|
|
|
|
|
|
|
|
yaml "gopkg.in/yaml.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DynamicPage is a static page, that includes frontmatter.
|
|
|
|
type DynamicPage struct {
|
|
|
|
pageFields
|
2017-06-20 22:17:59 +02:00
|
|
|
raw []byte
|
|
|
|
processed *[]byte
|
2017-06-17 01:34:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Static returns a bool indicating that the page is a not static page.
|
2017-06-19 04:26:49 +02:00
|
|
|
func (page *DynamicPage) Static() bool { return false }
|
2017-06-17 01:34:39 +02:00
|
|
|
|
|
|
|
// NewDynamicPage reads the front matter from a file to create a new DynamicPage.
|
|
|
|
func NewDynamicPage(fields pageFields) (p *DynamicPage, err error) {
|
2017-06-17 02:06:55 +02:00
|
|
|
data, err := ioutil.ReadFile(filepath.Join(fields.site.Source, fields.relpath))
|
2017-06-17 01:34:39 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2017-06-17 02:49:28 +02:00
|
|
|
// Replace Windows linefeeds. This allows regular expressions to work.
|
|
|
|
data = bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1)
|
2017-06-17 01:34:39 +02:00
|
|
|
|
|
|
|
frontMatter, err := readFrontMatter(&data)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fields.frontMatter = MergeVariableMaps(fields.frontMatter, frontMatter)
|
|
|
|
return &DynamicPage{
|
|
|
|
pageFields: fields,
|
2017-06-20 22:17:59 +02:00
|
|
|
raw: data,
|
2017-06-17 01:34:39 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func readFrontMatter(sourcePtr *[]byte) (frontMatter VariableMap, err error) {
|
|
|
|
var (
|
|
|
|
source = *sourcePtr
|
|
|
|
start = 0
|
|
|
|
)
|
|
|
|
if match := frontMatterMatcher.FindSubmatchIndex(source); match != nil {
|
|
|
|
start = match[1]
|
|
|
|
if err = yaml.Unmarshal(source[match[2]:match[3]], &frontMatter); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else if match := emptyFontMatterMatcher.FindSubmatchIndex(source); match != nil {
|
|
|
|
start = match[1]
|
|
|
|
}
|
|
|
|
// This fixes the line numbers for template errors
|
2017-06-17 02:06:55 +02:00
|
|
|
// TODO find a less hack-ey solution
|
2017-06-17 01:34:39 +02:00
|
|
|
*sourcePtr = append(
|
|
|
|
regexp.MustCompile(`[^\n\r]+`).ReplaceAllLiteral(source[:start], []byte{}),
|
|
|
|
source[start:]...)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-20 22:17:59 +02:00
|
|
|
// Variables returns the attributes of the template page object.
|
|
|
|
func (page *DynamicPage) Variables() VariableMap {
|
2017-06-17 01:34:39 +02:00
|
|
|
var (
|
2017-06-19 04:26:49 +02:00
|
|
|
relpath = page.relpath
|
2017-06-17 02:06:55 +02:00
|
|
|
ext = filepath.Ext(relpath)
|
2017-06-21 15:33:10 +02:00
|
|
|
root = helpers.TrimExt(page.relpath)
|
2017-06-17 02:06:55 +02:00
|
|
|
base = filepath.Base(root)
|
2017-06-20 22:17:59 +02:00
|
|
|
content = page.processed
|
2017-06-17 01:34:39 +02:00
|
|
|
)
|
|
|
|
|
2017-06-20 22:17:59 +02:00
|
|
|
if content == nil {
|
|
|
|
content = &[]byte{}
|
|
|
|
}
|
|
|
|
|
2017-06-17 01:34:39 +02:00
|
|
|
data := VariableMap{
|
2017-06-20 22:17:59 +02:00
|
|
|
"path": relpath,
|
|
|
|
"url": page.Permalink(),
|
|
|
|
"content": content,
|
|
|
|
// TODO output
|
2017-06-17 01:34:39 +02:00
|
|
|
|
|
|
|
// not documented, but present in both collection and non-collection pages
|
2017-06-19 04:26:49 +02:00
|
|
|
"permalink": page.Permalink(),
|
2017-06-17 01:34:39 +02:00
|
|
|
|
|
|
|
// TODO only in non-collection pages:
|
|
|
|
// TODO dir
|
|
|
|
// TODO name
|
|
|
|
// TODO next previous
|
|
|
|
|
|
|
|
// TODO Documented as present in all pages, but de facto only defined for collection pages
|
|
|
|
"id": base,
|
|
|
|
"title": base, // TODO capitalize
|
|
|
|
// TODO date (of the collection?) 2017-06-15 07:44:21 -0400
|
|
|
|
// TODO excerpt category? categories tags
|
|
|
|
// TODO slug
|
2017-06-21 15:12:48 +02:00
|
|
|
"categories": []string{},
|
|
|
|
"tags": []string{},
|
2017-06-17 01:34:39 +02:00
|
|
|
|
|
|
|
// TODO Only present in collection pages https://jekyllrb.com/docs/collections/#documents
|
2017-06-19 04:26:49 +02:00
|
|
|
"relative_path": page.Path(),
|
2017-06-17 01:34:39 +02:00
|
|
|
// TODO collection(name)
|
|
|
|
|
|
|
|
// TODO undocumented; only present in collection pages:
|
|
|
|
"ext": ext,
|
|
|
|
}
|
2017-06-19 04:26:49 +02:00
|
|
|
for k, v := range page.frontMatter {
|
2017-06-17 01:34:39 +02:00
|
|
|
switch k {
|
|
|
|
// doc implies these aren't present, but they appear to be present in a collection page:
|
|
|
|
// case "layout", "published":
|
|
|
|
case "permalink":
|
|
|
|
// omit this, in order to use the value above
|
|
|
|
default:
|
|
|
|
data[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2017-06-20 22:17:59 +02:00
|
|
|
// Context returns the local variables for template evaluation
|
|
|
|
func (page *DynamicPage) Context() VariableMap {
|
2017-06-17 01:34:39 +02:00
|
|
|
return VariableMap{
|
2017-06-20 22:17:59 +02:00
|
|
|
"page": page.Variables(),
|
2017-06-19 04:26:49 +02:00
|
|
|
"site": page.site.Variables,
|
2017-06-17 01:34:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-20 14:59:54 +02:00
|
|
|
// Output returns a bool indicating whether the page should be written.
|
|
|
|
func (page *DynamicPage) Output() bool {
|
|
|
|
return page.pageFields.Output() && (page.collection == nil || page.collection.Output)
|
|
|
|
}
|
|
|
|
|
2017-06-17 01:34:39 +02:00
|
|
|
// Write applies Liquid and Markdown, as appropriate.
|
2017-06-19 04:26:49 +02:00
|
|
|
func (page *DynamicPage) Write(w io.Writer) (err error) {
|
2017-06-20 22:17:59 +02:00
|
|
|
body, err := page.site.LiquidEngine().ParseAndRender(page.raw, page.Context())
|
2017-06-17 01:34:39 +02:00
|
|
|
if err != nil {
|
2017-06-21 15:12:48 +02:00
|
|
|
switch err := err.(type) {
|
|
|
|
case *liquid.RenderError:
|
|
|
|
if err.Filename == "" {
|
|
|
|
err.Filename = page.Source()
|
|
|
|
}
|
|
|
|
if rel, e := filepath.Rel(page.site.Source, err.Filename); e == nil {
|
|
|
|
err.Filename = rel
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
default:
|
|
|
|
return helpers.PathError(err, "Liquid Error", page.Source())
|
|
|
|
}
|
2017-06-17 01:34:39 +02:00
|
|
|
}
|
|
|
|
|
2017-06-19 04:26:49 +02:00
|
|
|
if page.IsMarkdown() {
|
2017-06-17 01:34:39 +02:00
|
|
|
body = blackfriday.MarkdownCommon(body)
|
2017-06-19 04:26:49 +02:00
|
|
|
body, err = page.applyLayout(page.frontMatter, body)
|
2017-06-17 01:34:39 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-19 04:26:49 +02:00
|
|
|
if page.Site().IsSassPath(page.relpath) {
|
|
|
|
return page.writeSass(w, body)
|
2017-06-17 01:34:39 +02:00
|
|
|
}
|
|
|
|
|
2017-06-20 22:17:59 +02:00
|
|
|
page.processed = &body
|
2017-06-17 01:34:39 +02:00
|
|
|
_, err = w.Write(body)
|
|
|
|
return
|
|
|
|
}
|