1 | package gcss
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bytes"
|
---|
5 | "io"
|
---|
6 | "io/ioutil"
|
---|
7 | "path/filepath"
|
---|
8 | "strings"
|
---|
9 | )
|
---|
10 |
|
---|
11 | // extensions
|
---|
12 | const (
|
---|
13 | extCSS = ".css"
|
---|
14 | extGCSS = ".gcss"
|
---|
15 | )
|
---|
16 |
|
---|
17 | // cssFilePath converts path's extenstion into a CSS file extension.
|
---|
18 | var cssFilePath = func(path string) string {
|
---|
19 | return convertExt(path, extCSS)
|
---|
20 | }
|
---|
21 |
|
---|
22 | // Compile compiles GCSS data which is read from src and
|
---|
23 | // Writes the result CSS data to the dst.
|
---|
24 | func Compile(dst io.Writer, src io.Reader) (int, error) {
|
---|
25 | data, err := ioutil.ReadAll(src)
|
---|
26 |
|
---|
27 | if err != nil {
|
---|
28 | return 0, err
|
---|
29 | }
|
---|
30 |
|
---|
31 | bc, berrc := compileBytes(data)
|
---|
32 |
|
---|
33 | bf := new(bytes.Buffer)
|
---|
34 |
|
---|
35 | BufWriteLoop:
|
---|
36 | for {
|
---|
37 | select {
|
---|
38 | case b, ok := <-bc:
|
---|
39 | if !ok {
|
---|
40 | break BufWriteLoop
|
---|
41 | }
|
---|
42 |
|
---|
43 | bf.Write(b)
|
---|
44 | case err := <-berrc:
|
---|
45 | return 0, err
|
---|
46 | }
|
---|
47 | }
|
---|
48 |
|
---|
49 | return dst.Write(bf.Bytes())
|
---|
50 | }
|
---|
51 |
|
---|
52 | // CompileFile parses the GCSS file specified by the path parameter,
|
---|
53 | // generates a CSS file and returns the path of the generated CSS file
|
---|
54 | // and an error when it occurs.
|
---|
55 | func CompileFile(path string) (string, error) {
|
---|
56 | data, err := ioutil.ReadFile(path)
|
---|
57 |
|
---|
58 | if err != nil {
|
---|
59 | return "", err
|
---|
60 | }
|
---|
61 |
|
---|
62 | cssPath := cssFilePath(path)
|
---|
63 |
|
---|
64 | bc, berrc := compileBytes(data)
|
---|
65 |
|
---|
66 | done, werrc := write(cssPath, bc, berrc)
|
---|
67 |
|
---|
68 | select {
|
---|
69 | case <-done:
|
---|
70 | case err := <-werrc:
|
---|
71 | return "", err
|
---|
72 | }
|
---|
73 |
|
---|
74 | return cssPath, nil
|
---|
75 | }
|
---|
76 |
|
---|
77 | // compileBytes parses the GCSS byte array passed as the s parameter,
|
---|
78 | // generates a CSS byte array and returns the two channels: the first
|
---|
79 | // one returns the CSS byte array and the last one returns an error
|
---|
80 | // when it occurs.
|
---|
81 | func compileBytes(b []byte) (<-chan []byte, <-chan error) {
|
---|
82 | lines := strings.Split(formatLF(string(b)), lf)
|
---|
83 |
|
---|
84 | bc := make(chan []byte, len(lines))
|
---|
85 | errc := make(chan error)
|
---|
86 |
|
---|
87 | go func() {
|
---|
88 | ctx := newContext()
|
---|
89 |
|
---|
90 | elemc, pErrc := parse(lines)
|
---|
91 |
|
---|
92 | for {
|
---|
93 | select {
|
---|
94 | case elem, ok := <-elemc:
|
---|
95 | if !ok {
|
---|
96 | close(bc)
|
---|
97 | return
|
---|
98 | }
|
---|
99 |
|
---|
100 | elem.SetContext(ctx)
|
---|
101 |
|
---|
102 | switch v := elem.(type) {
|
---|
103 | case *mixinDeclaration:
|
---|
104 | ctx.mixins[v.name] = v
|
---|
105 | case *variable:
|
---|
106 | ctx.vars[v.name] = v
|
---|
107 | case *atRule, *declaration, *selector:
|
---|
108 | bf := new(bytes.Buffer)
|
---|
109 | elem.WriteTo(bf)
|
---|
110 | bc <- bf.Bytes()
|
---|
111 | }
|
---|
112 | case err := <-pErrc:
|
---|
113 | errc <- err
|
---|
114 | return
|
---|
115 | }
|
---|
116 | }
|
---|
117 | }()
|
---|
118 |
|
---|
119 | return bc, errc
|
---|
120 | }
|
---|
121 |
|
---|
122 | // Path converts path's extenstion into a GCSS file extension.
|
---|
123 | func Path(path string) string {
|
---|
124 | return convertExt(path, extGCSS)
|
---|
125 | }
|
---|
126 |
|
---|
127 | // convertExt converts path's extension into ext.
|
---|
128 | func convertExt(path string, ext string) string {
|
---|
129 | return strings.TrimSuffix(path, filepath.Ext(path)) + ext
|
---|
130 | }
|
---|