source: code/trunk/zs.go@ 32

Last change on this file since 32 was 32, checked in by zaitsev.serge, 10 years ago

dead end with template functions

File size: 6.7 KB
RevLine 
[1]1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "log"
9 "os"
10 "path"
11 "path/filepath"
12 "strings"
[19]13 "text/template"
[1]14 "time"
15
[23]16 "github.com/eknkc/amber"
[1]17 "github.com/russross/blackfriday"
[18]18 "github.com/yosssi/gcss"
[1]19)
20
21const (
22 ZSDIR = ".zs"
23 PUBDIR = ".pub"
24)
25
[18]26type Vars map[string]string
[24]27type Funcs template.FuncMap
[4]28
[18]29// Parses markdown content. Returns parsed header variables and content
[20]30func md(path string, globals Vars) (Vars, string, error) {
[18]31 b, err := ioutil.ReadFile(path)
32 if err != nil {
33 return nil, "", err
34 }
35 s := string(b)
[7]36 url := path[:len(path)-len(filepath.Ext(path))] + ".html"
[18]37 v := Vars{
[29]38 "title": "",
39 "description": "",
40 "keywords": "",
[7]41 }
[30]42 for name, value := range globals {
43 v[name] = value
44 }
[21]45 if _, err := os.Stat(filepath.Join(ZSDIR, "layout.amber")); err == nil {
46 v["layout"] = "layout.amber"
47 } else {
48 v["layout"] = "layout.html"
49 }
[30]50 v["file"] = path
51 v["url"] = url
52 v["output"] = filepath.Join(PUBDIR, url)
[21]53
[3]54 if strings.Index(s, "\n\n") == -1 {
[20]55 return v, s, nil
[3]56 }
[1]57 header, body := split2(s, "\n\n")
58 for _, line := range strings.Split(header, "\n") {
59 key, value := split2(line, ":")
60 v[strings.ToLower(strings.TrimSpace(key))] = strings.TrimSpace(value)
61 }
[7]62 if strings.HasPrefix(v["url"], "./") {
63 v["url"] = v["url"][2:]
64 }
[18]65 return v, body, nil
[1]66}
67
[19]68// Use standard Go templates
[24]69func render(s string, funcs Funcs, vars Vars) (string, error) {
[32]70 f := makeFuncs(funcs, vars)
[24]71 tmpl, err := template.New("").Funcs(template.FuncMap(f)).Parse(s)
[19]72 if err != nil {
73 return "", err
74 }
75 out := &bytes.Buffer{}
76 if err := tmpl.Execute(out, vars); err != nil {
77 return "", err
78 }
79 return string(out.Bytes()), nil
[1]80}
81
[19]82// Renders markdown with the given layout into html expanding all the macros
[24]83func buildMarkdown(path string, w io.Writer, funcs Funcs, vars Vars) error {
[20]84 v, body, err := md(path, vars)
[1]85 if err != nil {
86 return err
87 }
[19]88 content, err := render(body, funcs, v)
[1]89 if err != nil {
90 return err
91 }
[32]92 v["content"] = string(blackfriday.MarkdownCommon([]byte(content)))
[24]93 if w == nil {
94 out, err := os.Create(filepath.Join(PUBDIR, renameExt(path, "", ".html")))
95 if err != nil {
96 return err
97 }
98 defer out.Close()
99 w = out
100 }
[19]101 if strings.HasSuffix(v["layout"], ".amber") {
[24]102 return buildAmber(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
[19]103 } else {
[24]104 return buildHTML(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
[19]105 }
[15]106}
107
[19]108// Renders text file expanding all variable macros inside it
[24]109func buildHTML(path string, w io.Writer, funcs Funcs, vars Vars) error {
110 b, err := ioutil.ReadFile(path)
[1]111 if err != nil {
112 return err
113 }
[19]114 content, err := render(string(b), funcs, vars)
[1]115 if err != nil {
116 return err
117 }
[25]118 if w == nil {
119 f, err := os.Create(filepath.Join(PUBDIR, path))
120 if err != nil {
121 return err
122 }
123 defer f.Close()
124 w = f
[15]125 }
[25]126 _, err = io.WriteString(w, content)
127 return err
[1]128}
129
[19]130// Renders .amber file into .html
[24]131func buildAmber(path string, w io.Writer, funcs Funcs, vars Vars) error {
[18]132 a := amber.New()
[24]133 err := a.ParseFile(path)
[18]134 if err != nil {
135 return err
136 }
[25]137
138 data := map[string]interface{}{}
139 for k, v := range vars {
140 data[k] = v
141 }
[32]142 for k, v := range makeFuncs(funcs, Vars{}) {
[25]143 data[k] = v
144 }
145
[18]146 t, err := a.Compile()
147 if err != nil {
148 return err
149 }
[24]150 if w == nil {
151 f, err := os.Create(filepath.Join(PUBDIR, renameExt(path, ".amber", ".html")))
152 if err != nil {
153 return err
154 }
155 defer f.Close()
156 w = f
[18]157 }
[25]158 return t.Execute(w, data)
[18]159}
160
[19]161// Compiles .gcss into .css
[24]162func buildGCSS(path string, w io.Writer) error {
[19]163 f, err := os.Open(path)
164 if err != nil {
165 return err
166 }
167 defer f.Close()
168
[24]169 if w == nil {
170 s := strings.TrimSuffix(path, ".gcss") + ".css"
171 css, err := os.Create(filepath.Join(PUBDIR, s))
172 if err != nil {
173 return err
[1]174 }
[24]175 defer css.Close()
176 w = css
[1]177 }
[24]178 _, err = gcss.Compile(w, f)
[8]179 return err
[1]180}
181
[24]182// Copies file as is from path to writer
183func buildRaw(path string, w io.Writer) error {
184 in, err := os.Open(path)
185 if err != nil {
186 return err
[19]187 }
[24]188 defer in.Close()
189 if w == nil {
190 if out, err := os.Create(filepath.Join(PUBDIR, path)); err != nil {
191 return err
192 } else {
193 defer out.Close()
194 w = out
[19]195 }
196 }
[24]197 _, err = io.Copy(w, in)
198 return err
[19]199}
200
[24]201func build(path string, w io.Writer, funcs Funcs, vars Vars) error {
202 ext := filepath.Ext(path)
203 if ext == ".md" || ext == ".mkd" {
204 return buildMarkdown(path, w, funcs, vars)
205 } else if ext == ".html" || ext == ".xml" {
206 return buildHTML(path, w, funcs, vars)
207 } else if ext == ".amber" {
208 return buildAmber(path, w, funcs, vars)
209 } else if ext == ".gcss" {
210 return buildGCSS(path, w)
211 } else {
212 return buildRaw(path, w)
[21]213 }
214}
215
[24]216func buildAll(watch bool) {
[20]217 lastModified := time.Unix(0, 0)
218 modified := false
219
220 vars := globals()
[1]221 for {
222 os.Mkdir(PUBDIR, 0755)
[24]223 funcs := builtins()
[1]224 err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
225 // ignore hidden files and directories
226 if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") {
227 return nil
228 }
[31]229 // inform user about fs walk errors, but continue iteration
230 if err != nil {
231 log.Println("ERROR:", err)
232 return nil
233 }
[1]234
[8]235 if info.IsDir() {
236 os.Mkdir(filepath.Join(PUBDIR, path), 0755)
237 return nil
238 } else if info.ModTime().After(lastModified) {
239 if !modified {
240 // About to be modified, so run pre-build hook
[19]241 // FIXME on windows it might not work well
[8]242 run(filepath.Join(ZSDIR, "pre"), []string{}, nil, nil)
243 modified = true
244 }
[24]245 log.Println("build: ", path)
246 return build(path, nil, funcs, vars)
[1]247 }
248 return nil
249 })
250 if err != nil {
251 log.Println("ERROR:", err)
252 }
[8]253 if modified {
254 // Something was modified, so post-build hook
[19]255 // FIXME on windows it might not work well
[8]256 run(filepath.Join(ZSDIR, "post"), []string{}, nil, nil)
257 modified = false
258 }
[24]259 if !watch {
[1]260 break
261 }
[24]262 lastModified = time.Now()
[1]263 time.Sleep(1 * time.Second)
264 }
265}
266
267func main() {
268 if len(os.Args) == 1 {
269 fmt.Println(os.Args[0], "<command> [args]")
270 return
271 }
272 cmd := os.Args[1]
273 args := os.Args[2:]
274 switch cmd {
275 case "build":
[25]276 if len(args) == 0 {
277 buildAll(false)
278 } else if len(args) == 1 {
279 if err := build(args[0], os.Stdout, builtins(), globals()); err != nil {
280 fmt.Println("ERROR: " + err.Error())
281 }
282 } else {
283 fmt.Println("ERROR: too many arguments")
284 }
[24]285 case "watch":
[1]286 buildAll(true)
[24]287 case "var":
[32]288 fmt.Println(Var(args...))
[24]289 case "lorem":
[32]290 fmt.Println(Lorem(args...))
[24]291 case "dateparse":
[32]292 fmt.Println(DateParse(args...))
[24]293 case "datefmt":
[32]294 fmt.Println(DateFmt(args...))
[26]295 case "wc":
[32]296 fmt.Println(WordCount(args...))
297 case "ttr":
298 fmt.Println(TimeToRead(args...))
299 case "ls":
300 fmt.Println(strings.Join(List(args...), "\n"))
301 case "sort":
302 fmt.Println(strings.Join(Sort(args...), "\n"))
303 case "exec":
304 // TODO
[1]305 default:
[28]306 err := run(path.Join(ZSDIR, cmd), args, globals(), os.Stdout)
[5]307 if err != nil {
[21]308 log.Println("ERROR:", err)
[1]309 }
310 }
311}
Note: See TracBrowser for help on using the repository browser.