mirror of
https://github.com/danog/gojekyll.git
synced 2024-11-30 08:08:59 +01:00
Implement sass
This commit is contained in:
parent
6a4b048a09
commit
1d9fa428bc
2
build.go
2
build.go
@ -50,7 +50,7 @@ func (s *Site) Build() (count int, err error) {
|
||||
// WritePage writes a page to the destination directory.
|
||||
func (s *Site) WritePage(page Page) error {
|
||||
src := filepath.Join(s.Source, page.Path())
|
||||
dst := filepath.Join(s.Destination, page.Path())
|
||||
dst := filepath.Join(s.Destination, page.Permalink())
|
||||
if !page.Static() && filepath.Ext(dst) == "" {
|
||||
dst = filepath.Join(dst, "/index.html")
|
||||
}
|
||||
|
25
helpers.go
25
helpers.go
@ -17,20 +17,35 @@ var nonAlphanumericSequenceMatcher = regexp.MustCompile(`[^[:alnum:]]+`)
|
||||
// copyFile copies from file src to dst. It's not atomic and doesn't copy permissions or metadata.
|
||||
// This is sufficient for its use within this package.
|
||||
func copyFile(dst, src string, perm os.FileMode) error {
|
||||
inf, err := os.Open(src)
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer inf.Close() // nolint: errcheck, gas
|
||||
outf, err := os.Create(dst)
|
||||
defer in.Close() // nolint: errcheck, gas
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = io.Copy(outf, inf); err != nil {
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
_ = os.Remove(dst) // nolint: gas
|
||||
return err
|
||||
}
|
||||
return outf.Close()
|
||||
return out.Close()
|
||||
}
|
||||
|
||||
// ReadFileMagic returns the first four bytes of the file, with final '\r' replaced by '\n'.
|
||||
func ReadFileMagic(p string) (data []byte, err error) {
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
data = make([]byte, 4)
|
||||
_, err = f.Read(data)
|
||||
if data[3] == '\r' {
|
||||
data[3] = '\n'
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Bool returns m[k] if it's a bool; else defaultValue.
|
||||
|
@ -49,7 +49,10 @@ func XMLEscapeFactory(parameters []core.Value) core.Filter {
|
||||
panic("The xml_escape filter doesn't accept parameters")
|
||||
}
|
||||
return func(input interface{}, data map[string]interface{}) interface{} {
|
||||
s, err := xml.Marshal(input) // FIXME can't handle maps
|
||||
s, err := xml.Marshal(input)
|
||||
// TODO can't handle maps
|
||||
// eval https://github.com/clbanning/mxj
|
||||
// adapt https://stackoverflow.com/questions/30928770/marshall-map-to-xml-in-go
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
103
page.go
103
page.go
@ -61,30 +61,28 @@ func (p *pageFields) setPermalink(permalink string) {
|
||||
}
|
||||
|
||||
// ReadPage reads a Page from a file, using defaults as the default front matter.
|
||||
func ReadPage(path string, defaults VariableMap) (p Page, err error) {
|
||||
// TODO only read the first four bytes unless it's dynamic
|
||||
source, err := ioutil.ReadFile(filepath.Join(site.Source, path))
|
||||
func ReadPage(rel string, defaults VariableMap) (p Page, err error) {
|
||||
magic, err := ReadFileMagic(filepath.Join(site.Source, rel))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
|
||||
data := pageFields{
|
||||
path: path,
|
||||
fields := pageFields{
|
||||
path: rel,
|
||||
frontMatter: defaults,
|
||||
}
|
||||
if string(source[:4]) == "---\n" {
|
||||
p, err = makeDynamicPage(&data, source)
|
||||
if string(magic) == "---\n" {
|
||||
p, err = readDynamicPage(&fields, rel)
|
||||
} else {
|
||||
p = &StaticPage{data}
|
||||
p = &StaticPage{fields}
|
||||
}
|
||||
if p != nil {
|
||||
// Compute this after creating the page, to pick up the the front matter.
|
||||
pattern := data.frontMatter.String("permalink", ":path:output_ext")
|
||||
permalink, err := expandPermalinkPattern(pattern, data.path, data.frontMatter)
|
||||
// Compute this after creating the page, in order to pick up the front matter.
|
||||
pattern := fields.frontMatter.String("permalink", ":path:output_ext")
|
||||
permalink, err := expandPermalinkPattern(pattern, rel, fields.frontMatter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
println(path, pattern, permalink)
|
||||
p.setPermalink(permalink)
|
||||
}
|
||||
return
|
||||
@ -150,17 +148,22 @@ type DynamicPage struct {
|
||||
// Static returns a bool indicating that the page is a not static page.
|
||||
func (p *DynamicPage) Static() bool { return false }
|
||||
|
||||
func makeDynamicPage(data *pageFields, source []byte) (*DynamicPage, error) {
|
||||
frontMatter, err := readFrontMatter(&source)
|
||||
func readDynamicPage(fields *pageFields, rel string) (p *DynamicPage, err error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(site.Source, rel))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
data.frontMatter = mergeVariableMaps(data.frontMatter, frontMatter)
|
||||
p := &DynamicPage{
|
||||
pageFields: *data,
|
||||
Content: source,
|
||||
data = bytes.Replace(data, []byte("\r"), []byte("\n"), -1)
|
||||
|
||||
frontMatter, err := readFrontMatter(&data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return p, nil
|
||||
fields.frontMatter = mergeVariableMaps(fields.frontMatter, frontMatter)
|
||||
return &DynamicPage{
|
||||
pageFields: *fields,
|
||||
Content: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readFrontMatter(sourcePtr *[]byte) (frontMatter VariableMap, err error) {
|
||||
@ -273,39 +276,53 @@ func parseAndApplyTemplate(bs []byte, variables VariableMap) ([]byte, error) {
|
||||
}
|
||||
|
||||
// Write applies Liquid and Markdown, as appropriate.
|
||||
func (p *DynamicPage) Write(w io.Writer) error {
|
||||
func (p *DynamicPage) Write(w io.Writer) (err error) {
|
||||
body, err := parseAndApplyTemplate(p.Content, p.TemplateVariables())
|
||||
if err != nil {
|
||||
err := &os.PathError{Op: "Liquid Error", Path: p.Source(), Err: err}
|
||||
return err
|
||||
err = &os.PathError{Op: "Liquid Error", Path: p.Source(), Err: err}
|
||||
return
|
||||
}
|
||||
|
||||
if isMarkdown(p.path) {
|
||||
body = blackfriday.MarkdownCommon(body)
|
||||
body, err = p.applyLayout(p.frontMatter, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
layoutFrontMatter := p.frontMatter
|
||||
for {
|
||||
layoutName := layoutFrontMatter.String("layout", "")
|
||||
if layoutName == "" {
|
||||
break
|
||||
}
|
||||
template, err := site.FindLayout(layoutName, &layoutFrontMatter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vars := mergeVariableMaps(p.TemplateVariables(), VariableMap{
|
||||
"content": body,
|
||||
"layout": layoutFrontMatter,
|
||||
})
|
||||
body, err = renderTemplate(template, vars)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if isSassPath(p.path) {
|
||||
return p.writeSass(w, body)
|
||||
}
|
||||
|
||||
_, err = w.Write(body)
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
func (p *DynamicPage) applyLayout(frontMatter VariableMap, body []byte) ([]byte, error) {
|
||||
for {
|
||||
layoutName := frontMatter.String("layout", "")
|
||||
if layoutName == "" {
|
||||
break
|
||||
}
|
||||
template, err := site.FindLayout(layoutName, &frontMatter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vars := mergeVariableMaps(p.TemplateVariables(), VariableMap{
|
||||
"content": body,
|
||||
"layout": frontMatter,
|
||||
})
|
||||
body, err = renderTemplate(template, vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func isSassPath(path string) bool {
|
||||
return strings.HasSuffix(path, ".sass") || strings.HasSuffix(path, ".scss")
|
||||
}
|
||||
|
||||
func isMarkdown(path string) bool {
|
||||
|
@ -37,7 +37,6 @@ var templateVariableMatcher = regexp.MustCompile(`:\w+\b`)
|
||||
func permalinkTemplateVariables(path string, frontMatter VariableMap) map[string]string {
|
||||
var (
|
||||
collectionName string
|
||||
localPath = path
|
||||
ext = filepath.Ext(path)
|
||||
outputExt = ext
|
||||
root = path[:len(path)-len(ext)]
|
||||
@ -47,16 +46,16 @@ func permalinkTemplateVariables(path string, frontMatter VariableMap) map[string
|
||||
switch {
|
||||
case isMarkdown(path):
|
||||
outputExt = ".html"
|
||||
case ext == ".scss":
|
||||
outputExt = ".html"
|
||||
case isSassPath(path):
|
||||
outputExt = ".css"
|
||||
}
|
||||
if val, found := frontMatter["collection"]; found {
|
||||
collectionName = val.(string)
|
||||
prefix := "_" + collectionName + "/"
|
||||
if !strings.HasPrefix(localPath, prefix) {
|
||||
panic(fmt.Errorf("Expected %s to start with %s", localPath, prefix))
|
||||
if !strings.HasPrefix(path, prefix) {
|
||||
panic(fmt.Errorf("Expected %s to start with %s", path, prefix))
|
||||
}
|
||||
localPath = localPath[len(prefix):]
|
||||
path = path[len(prefix):]
|
||||
root = root[len(prefix):]
|
||||
}
|
||||
vs := map[string]string{
|
||||
|
71
sass.go
Normal file
71
sass.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
libsass "github.com/wellington/go-libsass"
|
||||
)
|
||||
|
||||
func (p *DynamicPage) writeSass(w io.Writer, data []byte) error {
|
||||
comp, err := libsass.New(w, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = comp.Option(libsass.IncludePaths(site.SassIncludePaths()))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return comp.Run()
|
||||
}
|
||||
|
||||
// CopySassFileIncludes copies sass partials into a temporary directory,
|
||||
// removing initial underscores.
|
||||
// TODO delete the temp directory when done
|
||||
func (s *Site) CopySassFileIncludes() {
|
||||
// TODO use libsass.ImportsOption instead?
|
||||
if site.sassTempDir == "" {
|
||||
d, err := ioutil.TempDir(os.TempDir(), "_sass")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
site.sassTempDir = d
|
||||
}
|
||||
|
||||
src := filepath.Join(s.Source, "_sass")
|
||||
dst := site.sassTempDir
|
||||
err := filepath.Walk(src, func(from string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return err
|
||||
}
|
||||
rel, err := filepath.Rel(src, from)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if strings.HasPrefix(rel, "_") {
|
||||
rel = rel[1:]
|
||||
}
|
||||
to := filepath.Join(dst, rel)
|
||||
if err := copyFile(to, from, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// SassIncludePaths returns an array of sass include directories.
|
||||
func (s *Site) SassIncludePaths() []string {
|
||||
if site.sassTempDir == "" {
|
||||
site.CopySassFileIncludes()
|
||||
}
|
||||
s.CopySassFileIncludes()
|
||||
return []string{site.sassTempDir}
|
||||
}
|
Loading…
Reference in New Issue
Block a user