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