2017-08-18 17:07:01 +02:00
|
|
|
package renderers
|
2017-06-16 19:47:11 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-07-10 00:19:22 +02:00
|
|
|
"crypto/md5" // nolint: gas
|
|
|
|
"fmt"
|
2017-06-16 19:47:11 +02:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2017-07-10 00:19:22 +02:00
|
|
|
"github.com/osteele/gojekyll/cache"
|
2017-07-09 22:17:20 +02:00
|
|
|
"github.com/osteele/gojekyll/utils"
|
2017-07-26 17:34:42 +02:00
|
|
|
"github.com/tdewolff/minify"
|
|
|
|
"github.com/tdewolff/minify/css"
|
2017-06-17 01:17:22 +02:00
|
|
|
|
2017-06-16 19:47:11 +02:00
|
|
|
libsass "github.com/wellington/go-libsass"
|
|
|
|
)
|
|
|
|
|
2017-08-27 19:40:54 +02:00
|
|
|
const sassMIMEType = "text/css"
|
|
|
|
const sassDirName = "_sass"
|
|
|
|
|
|
|
|
// copySASSFileIncludes copies sass partials into a temporary directory,
|
2017-06-16 19:47:11 +02:00
|
|
|
// removing initial underscores.
|
2017-08-27 19:40:54 +02:00
|
|
|
func (p *Manager) copySASSFileIncludes() error {
|
2017-07-24 14:09:14 +02:00
|
|
|
// TODO delete the temp directory when done?
|
2017-06-16 19:47:11 +02:00
|
|
|
// TODO use libsass.ImportsOption instead?
|
2017-08-27 19:40:54 +02:00
|
|
|
// FIXME this doesn't delete stale css files
|
|
|
|
if err := p.makeSASSTempDir(); err != nil {
|
|
|
|
return err
|
2017-06-16 19:47:11 +02:00
|
|
|
}
|
2021-06-16 09:48:07 +02:00
|
|
|
h := md5.New() // nolint: gas
|
2017-07-24 14:09:14 +02:00
|
|
|
if p.ThemeDir != "" {
|
2017-08-27 19:40:54 +02:00
|
|
|
if err := p.copySASSFiles(filepath.Join(p.ThemeDir, sassDirName), p.sassTempDir, h); err != nil {
|
2017-07-24 14:09:14 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-08-27 19:40:54 +02:00
|
|
|
if err := p.copySASSFiles(filepath.Join(p.sourceDir(), p.cfg.Sass.Dir), p.sassTempDir, h); err != nil {
|
2017-07-24 14:09:14 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.sassHash = fmt.Sprintf("%x", h.Sum(nil))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-08-27 19:40:54 +02:00
|
|
|
func (p *Manager) makeSASSTempDir() error {
|
|
|
|
if p.sassTempDir == "" {
|
|
|
|
dir, err := ioutil.TempDir(os.TempDir(), "_sass")
|
|
|
|
if p.cfg.Verbose {
|
|
|
|
fmt.Println("create", dir)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.sassTempDir = dir
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Manager) copySASSFiles(src, dst string, h io.Writer) error {
|
|
|
|
if p.cfg.Verbose {
|
|
|
|
fmt.Printf("copy sass directory %s to %s\n", src, dst)
|
|
|
|
}
|
2017-06-22 01:17:44 +02:00
|
|
|
err := filepath.Walk(src, func(from string, info os.FileInfo, err error) error {
|
2017-06-16 19:47:11 +02:00
|
|
|
if err != nil || info.IsDir() {
|
|
|
|
return err
|
|
|
|
}
|
2017-07-09 22:17:20 +02:00
|
|
|
rel := utils.MustRel(src, from)
|
2017-06-16 21:41:03 +02:00
|
|
|
to := filepath.Join(dst, strings.TrimPrefix(rel, "_"))
|
2017-08-27 19:40:54 +02:00
|
|
|
if p.cfg.Verbose {
|
|
|
|
fmt.Printf("copy sass file %s to %s\n", src, to)
|
|
|
|
}
|
2017-07-10 00:19:22 +02:00
|
|
|
in, err := os.Open(from)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer in.Close() // nolint: errcheck
|
2017-08-27 19:40:54 +02:00
|
|
|
fmt.Fprintf(h, "--- sass file: %s ---\n", rel)
|
2017-07-24 14:09:14 +02:00
|
|
|
if _, err = io.Copy(h, in); err != nil {
|
2017-07-10 00:19:22 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-07-09 22:17:20 +02:00
|
|
|
return utils.CopyFileContents(to, from, 0644)
|
2017-06-16 19:47:11 +02:00
|
|
|
})
|
2017-06-22 01:17:44 +02:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
2017-06-16 19:47:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SassIncludePaths returns an array of sass include directories.
|
2017-08-18 17:07:01 +02:00
|
|
|
func (p *Manager) SassIncludePaths() []string {
|
2017-06-24 19:30:01 +02:00
|
|
|
return []string{p.sassTempDir}
|
2017-06-16 19:47:11 +02:00
|
|
|
}
|
2017-06-19 04:48:33 +02:00
|
|
|
|
2017-06-22 15:36:39 +02:00
|
|
|
// WriteSass converts a SASS file and writes it to w.
|
2017-08-18 17:07:01 +02:00
|
|
|
func (p *Manager) WriteSass(w io.Writer, b []byte) error {
|
2017-07-10 00:19:22 +02:00
|
|
|
s, err := cache.WithFile(fmt.Sprintf("sass: %s", p.sassHash), string(b), func() (s string, err error) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
comp, err := libsass.New(buf, bytes.NewBuffer(b))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = comp.Option(libsass.IncludePaths(p.SassIncludePaths()))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if err = comp.Run(); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2017-07-26 17:34:42 +02:00
|
|
|
m := minify.New()
|
2017-08-27 19:40:54 +02:00
|
|
|
m.AddFunc(sassMIMEType, css.Minify)
|
2017-07-26 17:34:42 +02:00
|
|
|
min := bytes.NewBuffer(make([]byte, 0, buf.Len()))
|
2017-08-27 19:40:54 +02:00
|
|
|
if err := m.Minify(sassMIMEType, min, bytes.NewBuffer(buf.Bytes())); err != nil {
|
2017-07-26 17:34:42 +02:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return min.String(), nil
|
2017-07-10 00:19:22 +02:00
|
|
|
})
|
2017-06-19 04:48:33 +02:00
|
|
|
if err != nil {
|
2017-07-09 21:21:11 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-07-16 03:25:35 +02:00
|
|
|
_, err = io.WriteString(w, s)
|
2017-07-09 21:21:11 +02:00
|
|
|
return err
|
2017-06-19 04:48:33 +02:00
|
|
|
}
|