/
main.go
150 lines (139 loc) · 3.62 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
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/tdewolff/minify"
"github.com/tdewolff/minify/css"
"github.com/tdewolff/minify/html"
"github.com/tdewolff/minify/js"
"github.com/tdewolff/minify/json"
"github.com/tdewolff/minify/svg"
"github.com/tdewolff/minify/xml"
)
var extMime = map[string]string{
".css": "text/css",
".htm": "text/html",
".html": "text/html",
".js": "application/javascript",
".json": "application/json",
".svg": "image/svg+xml",
".xml": "text/xml",
}
func main() {
input := ""
output := ""
filetype := ""
directory := ""
recursive := false
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] [file]\nOptions:\n", os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&output, "o", "", "Output file (stdout when empty)")
flag.StringVar(&filetype, "type", "", "Filetype (css, html, js, json, svg or xml), optional for input files")
flag.StringVar(&directory, "d", "", "Directory to search for files")
flag.BoolVar(&recursive, "r", false, "Recursively minify everything")
flag.Parse()
if len(flag.Args()) > 0 {
input = flag.Arg(0)
}
mediatype := ""
r := io.Reader(os.Stdin)
w := io.Writer(os.Stdout)
m := minify.New()
m.AddFunc("text/css", css.Minify)
m.AddFunc("text/html", html.Minify)
m.AddFunc("application/javascript", js.Minify)
m.AddFunc("image/svg+xml", svg.Minify)
m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
filenames := make(map[string]string)
if directory != "" {
filenames = dirFilenames(directory, recursive)
} else {
filenames[input] = output
}
for input, output := range filenames {
if input != "" {
in, err := os.Open(input)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
defer in.Close()
r = in
if input == output {
b := &bytes.Buffer{}
io.Copy(b, r)
r = b
}
if filetype == "" {
filetype = filepath.Ext(input)[1:]
}
}
if output != "" {
out, err := os.Create(output)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
defer out.Close()
w = out
}
if filetype != "" {
mediatype, _ = extMime[filetype]
}
if err := m.Minify(mediatype, w, r); err != nil {
if err == minify.ErrNotExist {
io.Copy(w, r)
} else {
fmt.Println("Error:", err)
os.Exit(1)
}
}
}
}
// dirFilenames returns a map of input paths and output paths.
func dirFilenames(root string, recursive bool) map[string]string {
names := map[string]string{}
if recursive {
filepath.Walk(root, func(path string, info os.FileInfo, _ error) error {
if isValidFile(info) {
names[path] = insertMinExt(path)
}
return nil
})
} else if infos, err := ioutil.ReadDir(root); err == nil {
for _, info := range infos {
if isValidFile(info) {
path := filepath.Join(root, info.Name())
names[path] = insertMinExt(path)
}
}
}
return names
}
// isValidFile checks to see if a file is a directory, hidden, already has the
// minified extension, or if it's one of the minifiable extensions.
func isValidFile(info os.FileInfo) bool {
if info.IsDir() || len(info.Name()) > 0 && (info.Name()[0] == '.' || strings.Contains(info.Name(), ".min.")) {
return false
}
_, exists := extMime[strings.ToLower(filepath.Ext(info.Name()))]
return exists
}
// insertMinExt adds .min before a file's extension. If a file doesn't have an
// extension then .min will become the file's extension.
func insertMinExt(path string) string {
if dot := strings.LastIndex(path, "."); dot != -1 {
return path[:dot] + ".min" + path[dot:]
}
return path + ".min"
}