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 |
} |