added amber and gcss compilers

This commit is contained in:
Serge A. Zaitsev 2015-08-29 17:47:16 +02:00
parent 9803539c0f
commit 49a47d065d
6 changed files with 103 additions and 22 deletions

5
testdata/sugar/.test/index.html vendored Normal file
View File

@ -0,0 +1,5 @@
<html>
<body>
<p>Hello world</p>
</body>
</html>

1
testdata/sugar/.test/styles.css vendored Normal file
View File

@ -0,0 +1 @@
body{font:100% Helvetica, sans-serif;color:blue;}

3
testdata/sugar/index.amber vendored Normal file
View File

@ -0,0 +1,3 @@
html
body
p Hello world

6
testdata/sugar/styles.gcss vendored Normal file
View File

@ -0,0 +1,6 @@
$base-font: Helvetica, sans-serif
$main-color: blue
body
font: 100% $base-font
color: $main-color

93
zs.go
View File

@ -13,7 +13,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/eknkc/amber"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
"github.com/yosssi/gcss"
) )
const ( const (
@ -21,8 +23,12 @@ const (
PUBDIR = ".pub" PUBDIR = ".pub"
) )
type EvalFn func(args []string, vars map[string]string) (string, error) type Vars map[string]string
type EvalFn func(args []string, vars Vars) (string, error)
// Splits a string in exactly two parts by delimiter
// If no delimiter is found - the second string is be empty
func split2(s, delim string) (string, string) { func split2(s, delim string) (string, string) {
parts := strings.SplitN(s, delim, 2) parts := strings.SplitN(s, delim, 2)
if len(parts) == 2 { if len(parts) == 2 {
@ -32,16 +38,22 @@ func split2(s, delim string) (string, string) {
} }
} }
func md(path, s string) (map[string]string, string) { // Parses markdown content. Returns parsed header variables and content
func md(path string) (Vars, string, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, "", err
}
s := string(b)
url := path[:len(path)-len(filepath.Ext(path))] + ".html" url := path[:len(path)-len(filepath.Ext(path))] + ".html"
v := map[string]string{ v := Vars{
"file": path, "file": path,
"url": url, "url": url,
"output": filepath.Join(PUBDIR, url), "output": filepath.Join(PUBDIR, url),
"layout": "index.html", "layout": "index.html",
} }
if strings.Index(s, "\n\n") == -1 { if strings.Index(s, "\n\n") == -1 {
return map[string]string{}, s return Vars{}, s, nil
} }
header, body := split2(s, "\n\n") header, body := split2(s, "\n\n")
for _, line := range strings.Split(header, "\n") { for _, line := range strings.Split(header, "\n") {
@ -51,10 +63,10 @@ func md(path, s string) (map[string]string, string) {
if strings.HasPrefix(v["url"], "./") { if strings.HasPrefix(v["url"], "./") {
v["url"] = v["url"][2:] v["url"] = v["url"][2:]
} }
return v, body return v, body, nil
} }
func render(s string, vars map[string]string, eval EvalFn) (string, error) { func render(s string, vars Vars, eval EvalFn) (string, error) {
delim_open := "{{" delim_open := "{{"
delim_close := "}}" delim_close := "}}"
@ -87,7 +99,8 @@ func render(s string, vars map[string]string, eval EvalFn) (string, error) {
} }
} }
func env(vars map[string]string) []string { // Converts zs markdown variables into environment variables
func env(vars Vars) []string {
env := []string{"ZS=" + os.Args[0], "ZS_OUTDIR=" + PUBDIR} env := []string{"ZS=" + os.Args[0], "ZS_OUTDIR=" + PUBDIR}
env = append(env, os.Environ()...) env = append(env, os.Environ()...)
if vars != nil { if vars != nil {
@ -98,7 +111,9 @@ func env(vars map[string]string) []string {
return env return env
} }
func run(cmd string, args []string, vars map[string]string, output io.Writer) error { // Runs command with given arguments and variables, intercepts stderr and
// redirects stdout into the given writer
func run(cmd string, args []string, vars Vars, output io.Writer) error {
var errbuf bytes.Buffer var errbuf bytes.Buffer
c := exec.Command(cmd, args...) c := exec.Command(cmd, args...)
c.Env = env(vars) c.Env = env(vars)
@ -117,7 +132,7 @@ func run(cmd string, args []string, vars map[string]string, output io.Writer) er
return nil return nil
} }
func eval(cmd []string, vars map[string]string) (string, error) { func eval(cmd []string, vars Vars) (string, error) {
outbuf := bytes.NewBuffer(nil) outbuf := bytes.NewBuffer(nil)
err := run(path.Join(ZSDIR, cmd[0]), cmd[1:], vars, outbuf) err := run(path.Join(ZSDIR, cmd[0]), cmd[1:], vars, outbuf)
if err != nil { if err != nil {
@ -135,11 +150,10 @@ func eval(cmd []string, vars map[string]string) (string, error) {
} }
func buildMarkdown(path string) error { func buildMarkdown(path string) error {
b, err := ioutil.ReadFile(path) v, body, err := md(path)
if err != nil { if err != nil {
return err return err
} }
v, body := md(path, string(b))
content, err := render(body, v, eval) content, err := render(body, v, eval)
if err != nil { if err != nil {
return err return err
@ -148,7 +162,7 @@ func buildMarkdown(path string) error {
return buildPlain(filepath.Join(ZSDIR, v["layout"]), v) return buildPlain(filepath.Join(ZSDIR, v["layout"]), v)
} }
func buildPlain(path string, vars map[string]string) error { func buildPlain(path string, vars Vars) error {
b, err := ioutil.ReadFile(path) b, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return err return err
@ -168,6 +182,45 @@ func buildPlain(path string, vars map[string]string) error {
return nil return nil
} }
func buildGCSS(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
s := strings.TrimSuffix(path, ".gcss") + ".css"
log.Println(s)
css, err := os.Create(filepath.Join(PUBDIR, s))
if err != nil {
return err
}
defer f.Close()
defer css.Close()
_, err = gcss.Compile(css, f)
return err
}
func buildAmber(path string, vars Vars) error {
a := amber.New()
err := a.ParseFile(path)
if err != nil {
return err
}
t, err := a.Compile()
if err != nil {
return err
}
//amber.FuncMap = amber.FuncMap
s := strings.TrimSuffix(path, ".amber") + ".html"
f, err := os.Create(filepath.Join(PUBDIR, s))
if err != nil {
return err
}
defer f.Close()
return t.Execute(f, vars)
}
func copyFile(path string) (err error) { func copyFile(path string) (err error) {
var in, out *os.File var in, out *os.File
if in, err = os.Open(path); err == nil { if in, err = os.Open(path); err == nil {
@ -202,10 +255,17 @@ func buildAll(once bool) {
} }
ext := filepath.Ext(path) ext := filepath.Ext(path)
if ext == ".md" || ext == ".mkd" { if ext == ".md" || ext == ".mkd" {
log.Println("mkd: ", path) log.Println("md: ", path)
return buildMarkdown(path) return buildMarkdown(path)
} else if ext == ".html" || ext == ".xml" { } else if ext == ".html" || ext == ".xml" {
return buildPlain(path, map[string]string{}) log.Println("html: ", path)
return buildPlain(path, Vars{})
} else if ext == ".amber" {
log.Println("html: ", path)
return buildAmber(path, Vars{})
} else if ext == ".gcss" {
log.Println("css: ", path)
return buildGCSS(path)
} else { } else {
log.Println("raw: ", path) log.Println("raw: ", path)
return copyFile(path) return copyFile(path)
@ -246,8 +306,7 @@ func main() {
log.Println("ERROR: filename expected") log.Println("ERROR: filename expected")
return return
} }
if b, err := ioutil.ReadFile(args[0]); err == nil { if vars, _, err := md(args[0]); err == nil {
vars, _ := md(args[0], string(b))
if len(args) > 1 { if len(args) > 1 {
for _, a := range args[1:] { for _, a := range args[1:] {
fmt.Println(vars[a]) fmt.Println(vars[a])
@ -261,7 +320,7 @@ func main() {
log.Println(err) log.Println(err)
} }
default: default:
err := run(path.Join(ZSDIR, cmd), args, map[string]string{}, os.Stdout) err := run(path.Join(ZSDIR, cmd), args, Vars{}, os.Stdout)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }

View File

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@ -31,14 +32,20 @@ func TestSplit2(t *testing.T) {
} }
} }
func tmpfile(path, s string) string {
ioutil.WriteFile(path, []byte(s), 0644)
return path
}
func TestMD(t *testing.T) { func TestMD(t *testing.T) {
v, body := md("foo.md", ` defer os.Remove("foo.md")
v, body, _ := md(tmpfile("foo.md", `
title: Hello, world! title: Hello, world!
keywords: foo, bar, baz keywords: foo, bar, baz
empty: empty:
bayan: [:|||:] bayan: [:|||:]
this: is a content`) this: is a content`))
if v["title"] != "Hello, world!" { if v["title"] != "Hello, world!" {
t.Error() t.Error()
} }
@ -56,20 +63,20 @@ this: is a content`)
} }
// Test empty md // Test empty md
v, body = md("foo.md", "") v, body, _ = md(tmpfile("foo.md", ""))
if len(v) != 0 || len(body) != 0 { if len(v) != 0 || len(body) != 0 {
t.Error(v, body) t.Error(v, body)
} }
// Test empty header // Test empty header
v, body = md("foo.md", "Hello") v, body, _ = md(tmpfile("foo.md", "Hello"))
if len(v) != 0 || body != "Hello" { if len(v) != 0 || body != "Hello" {
t.Error(v, body) t.Error(v, body)
} }
} }
func TestRender(t *testing.T) { func TestRender(t *testing.T) {
eval := func(a []string, vars map[string]string) (string, error) { eval := func(a []string, vars Vars) (string, error) {
return "hello", nil return "hello", nil
} }
vars := map[string]string{"foo": "bar"} vars := map[string]string{"foo": "bar"}