-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
203 lines (180 loc) · 4.67 KB
/
main.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package main
import (
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
)
type Flags struct {
dev bool
file string
dir string
stdin bool
}
func main() {
// flag "--dev" to run the script in dev mode
dev := flag.Bool("dev", false, "Run the script in dev mode")
// flag "--file" to specify the file to sort
file := flag.String("file", "", "Specify the file to sort")
// flag "--dir" to specify the directory to sort
dir := flag.String("dir", "", "Specify the directory to sort")
// flag "--stdin" to read from stdin
stdin := flag.Bool("stdin", false, "Read from stdin")
flag.Parse()
templCSSSort(Flags{
dev: *dev,
file: *file,
dir: *dir,
stdin: *stdin,
})
}
// The main function that runs the templCSSSort.
// Takes in flags to determine what to sort and where to sort it.
// If no flags are set, it will sort all .templ files in the current directory and its subdirectories.
// With the --stdin flag, it will read from stdin and write to stdout.
// With the --file flag, it will only sort the specified file.
// With the --dir flag, it will only sort the specified directory and its subdirectories.
func templCSSSort(flags Flags) {
start := time.Now()
// if stdin flag is set, read from stdin and write to stdout
if flags.stdin {
content, err := io.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
newContent, err := processContent(string(content))
if err != nil {
log.Fatal(err)
}
fmt.Println(newContent)
return
}
// find all .templ files in directory and subdirectories
var files []string
var err error
if flags.file != "" {
// If the file flag is specified, only take in that file
if !strings.HasSuffix(flags.file, ".templ") {
log.Fatal("File must have .templ extension")
}
files = append(files, flags.file)
} else if flags.dir != "" {
// If the dir flag is specified, take in that dir and its subdirectories
err = filepath.Walk(flags.dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), ".templ") {
files = append(files, path)
}
return nil
})
} else {
// If neither flag is specified, go through cwd and all subdirectories for any .templ file
err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), ".templ") {
files = append(files, path)
}
return nil
})
}
if err != nil {
log.Fatal(err)
}
log.Println("Found", len(files), "files")
// parse each file
for _, file := range files {
// read file
content, err := os.ReadFile(file)
if err != nil {
log.Println("couldn't read file: ", file)
continue
}
if len(content) == 0 {
log.Println("file empty: ", file)
continue
}
newContent, err := processContent(string(content))
if err != nil {
if err.Error() == "no_classes_found" {
continue
}
log.Fatal(err)
}
// write the modified content back to the file
err = os.WriteFile(file, []byte(newContent), 0644)
if err != nil {
log.Fatal(err)
}
}
log.Println("Done in", time.Since(start))
}
// Takes a string of content and returns a string of content with the classes sorted
func processContent(content string) (string, error) {
// find all classes in content
re := regexp.MustCompile(`class="([^"]+)"`)
matches := re.FindAllStringSubmatch(content, -1)
if len(matches) == 0 {
return "", errors.New("no_classes_found")
}
for _, match := range matches {
classList := match[1]
if classList == "" {
continue
}
// trim in place
classList = strings.TrimSpace(classList)
if classList == "" {
continue
}
// any whitespace bigger than 1 char, reduce to 1 char
for strings.Contains(classList, " ") {
classList = strings.ReplaceAll(classList, " ", " ")
}
if classList == "" {
continue
}
// split
classes := strings.Split(classList, " ")
if len(classes) == 0 {
continue
}
// sort
sort.Strings(classes)
// remove duplicates
classes = removeDuplicates(classes)
// create new class list string
newClassList := strings.Join(classes, " ")
if newClassList == "" {
continue
}
// replace class list in file
content = strings.Replace(content, match[0], `class="`+newClassList+`"`, -1)
if content == "" {
continue
}
}
return content, nil
}
// Takes a slice of strings and returns a slice of strings with duplicates removed
func removeDuplicates(slice []string) []string {
keys := make(map[string]bool)
list := []string{}
for _, entry := range slice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}