aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/tdewolff/minify/cmd/minify/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tdewolff/minify/cmd/minify/main.go')
-rw-r--r--vendor/github.com/tdewolff/minify/cmd/minify/main.go650
1 files changed, 0 insertions, 650 deletions
diff --git a/vendor/github.com/tdewolff/minify/cmd/minify/main.go b/vendor/github.com/tdewolff/minify/cmd/minify/main.go
deleted file mode 100644
index 2ed981c..0000000
--- a/vendor/github.com/tdewolff/minify/cmd/minify/main.go
+++ /dev/null
@@ -1,650 +0,0 @@
-package main
-
-import (
- "bufio"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/url"
- "os"
- "os/signal"
- "path"
- "path/filepath"
- "regexp"
- "runtime"
- "sort"
- "strings"
- "time"
-
- humanize "github.com/dustin/go-humanize"
- "github.com/matryer/try"
- flag "github.com/spf13/pflag"
- min "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 Version = "master"
-var Commit = ""
-var Date = ""
-
-var filetypeMime = map[string]string{
- "css": "text/css",
- "htm": "text/html",
- "html": "text/html",
- "js": "text/javascript",
- "json": "application/json",
- "svg": "image/svg+xml",
- "xml": "text/xml",
-}
-
-var (
- help bool
- hidden bool
- list bool
- m *min.M
- pattern *regexp.Regexp
- recursive bool
- verbose bool
- version bool
- watch bool
-)
-
-type Task struct {
- srcs []string
- srcDir string
- dst string
-}
-
-var (
- Error *log.Logger
- Info *log.Logger
-)
-
-func main() {
- output := ""
- mimetype := ""
- filetype := ""
- match := ""
- siteurl := ""
-
- cssMinifier := &css.Minifier{}
- htmlMinifier := &html.Minifier{}
- jsMinifier := &js.Minifier{}
- jsonMinifier := &json.Minifier{}
- svgMinifier := &svg.Minifier{}
- xmlMinifier := &xml.Minifier{}
-
- flag := flag.NewFlagSet("minify", flag.ContinueOnError)
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "Usage: %s [options] [input]\n\nOptions:\n", os.Args[0])
- flag.PrintDefaults()
- fmt.Fprintf(os.Stderr, "\nInput:\n Files or directories, leave blank to use stdin\n")
- }
-
- flag.BoolVarP(&help, "help", "h", false, "Show usage")
- flag.StringVarP(&output, "output", "o", "", "Output file or directory (must have trailing slash), leave blank to use stdout")
- flag.StringVar(&mimetype, "mime", "", "Mimetype (eg. text/css), optional for input filenames, has precedence over -type")
- flag.StringVar(&filetype, "type", "", "Filetype (eg. css), optional for input filenames")
- flag.StringVar(&match, "match", "", "Filename pattern matching using regular expressions")
- flag.BoolVarP(&recursive, "recursive", "r", false, "Recursively minify directories")
- flag.BoolVarP(&hidden, "all", "a", false, "Minify all files, including hidden files and files in hidden directories")
- flag.BoolVarP(&list, "list", "l", false, "List all accepted filetypes")
- flag.BoolVarP(&verbose, "verbose", "v", false, "Verbose")
- flag.BoolVarP(&watch, "watch", "w", false, "Watch files and minify upon changes")
- flag.BoolVarP(&version, "version", "", false, "Version")
-
- flag.StringVar(&siteurl, "url", "", "URL of file to enable URL minification")
- flag.IntVar(&cssMinifier.Decimals, "css-decimals", -1, "Number of decimals to preserve in numbers, -1 is all")
- flag.BoolVar(&htmlMinifier.KeepConditionalComments, "html-keep-conditional-comments", false, "Preserve all IE conditional comments")
- flag.BoolVar(&htmlMinifier.KeepDefaultAttrVals, "html-keep-default-attrvals", false, "Preserve default attribute values")
- flag.BoolVar(&htmlMinifier.KeepDocumentTags, "html-keep-document-tags", false, "Preserve html, head and body tags")
- flag.BoolVar(&htmlMinifier.KeepEndTags, "html-keep-end-tags", false, "Preserve all end tags")
- flag.BoolVar(&htmlMinifier.KeepWhitespace, "html-keep-whitespace", false, "Preserve whitespace characters but still collapse multiple into one")
- flag.IntVar(&svgMinifier.Decimals, "svg-decimals", -1, "Number of decimals to preserve in numbers, -1 is all")
- flag.BoolVar(&xmlMinifier.KeepWhitespace, "xml-keep-whitespace", false, "Preserve whitespace characters but still collapse multiple into one")
- if err := flag.Parse(os.Args[1:]); err != nil {
- fmt.Printf("Error: %v\n\n", err)
- flag.Usage()
- os.Exit(2)
- }
- rawInputs := flag.Args()
-
- Error = log.New(os.Stderr, "ERROR: ", 0)
- if verbose {
- Info = log.New(os.Stderr, "INFO: ", 0)
- } else {
- Info = log.New(ioutil.Discard, "INFO: ", 0)
- }
-
- if help {
- flag.Usage()
- os.Exit(0)
- }
-
- if version {
- if Version == "devel" {
- fmt.Printf("minify version devel+%.7s %s\n", Commit, Date)
- } else {
- fmt.Printf("minify version %s\n", Version)
- }
- os.Exit(0)
- }
-
- if list {
- var keys []string
- for k := range filetypeMime {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- fmt.Println(k + "\t" + filetypeMime[k])
- }
- os.Exit(0)
- }
-
- useStdin := len(rawInputs) == 0
- mimetype = getMimetype(mimetype, filetype, useStdin)
-
- var err error
- if match != "" {
- pattern, err = regexp.Compile(match)
- if err != nil {
- Error.Fatalln(err)
- }
- }
-
- if watch && (useStdin || output == "") {
- Error.Fatalln("watch doesn't work on stdin and stdout, specify input and output")
- }
-
- if recursive && (useStdin || output == "") {
- Error.Fatalln("recursive minification doesn't work on stdin and stdout, specify input and output")
- }
-
- ////////////////
-
- dirDst := false
- if output != "" {
- output = sanitizePath(output)
- if output[len(output)-1] == '/' {
- dirDst = true
- if err := os.MkdirAll(output, 0777); err != nil {
- Error.Fatalln(err)
- }
- }
- }
-
- tasks, ok := expandInputs(rawInputs, dirDst)
- if !ok {
- os.Exit(1)
- }
-
- if ok = expandOutputs(output, &tasks); !ok {
- os.Exit(1)
- }
-
- if len(tasks) == 0 {
- tasks = append(tasks, Task{[]string{""}, "", output}) // stdin
- }
-
- m = min.New()
- m.Add("text/css", cssMinifier)
- m.Add("text/html", htmlMinifier)
- m.Add("text/javascript", jsMinifier)
- m.Add("image/svg+xml", svgMinifier)
- m.AddRegexp(regexp.MustCompile("[/+]json$"), jsonMinifier)
- m.AddRegexp(regexp.MustCompile("[/+]xml$"), xmlMinifier)
-
- if m.URL, err = url.Parse(siteurl); err != nil {
- Error.Fatalln(err)
- }
-
- start := time.Now()
-
- chanTasks := make(chan Task, 100)
- chanFails := make(chan int, 100)
-
- numWorkers := 1
- if !verbose && len(tasks) > 1 {
- numWorkers = 4
- if n := runtime.NumCPU(); n > numWorkers {
- numWorkers = n
- }
- }
-
- for n := 0; n < numWorkers; n++ {
- go minifyWorker(mimetype, chanTasks, chanFails)
- }
-
- for _, task := range tasks {
- chanTasks <- task
- }
-
- if watch {
- watcher, err := NewRecursiveWatcher(recursive)
- if err != nil {
- Error.Fatalln(err)
- }
- defer watcher.Close()
-
- watcherTasks := make(map[string]Task, len(rawInputs))
- for _, task := range tasks {
- for _, src := range task.srcs {
- watcherTasks[src] = task
- watcher.AddPath(src)
- }
- }
-
- c := make(chan os.Signal, 1)
- signal.Notify(c, os.Interrupt)
-
- skip := make(map[string]int)
- changes := watcher.Run()
- for changes != nil {
- select {
- case <-c:
- watcher.Close()
- fmt.Printf("\n")
- case file, ok := <-changes:
- if !ok {
- changes = nil
- break
- }
- file = sanitizePath(file)
-
- if skip[file] > 0 {
- skip[file]--
- continue
- }
-
- var t Task
- if t, ok = watcherTasks[file]; ok {
- if !verbose {
- Info.Println(file, "changed")
- }
- for _, src := range t.srcs {
- if src == t.dst {
- skip[file] = 2 // minify creates both a CREATE and WRITE on the file
- break
- }
- }
- chanTasks <- t
- }
- }
- }
- }
-
- fails := 0
- close(chanTasks)
- for n := 0; n < numWorkers; n++ {
- fails += <-chanFails
- }
-
- if verbose {
- Info.Println(time.Since(start), "total")
- }
- if fails > 0 {
- os.Exit(1)
- }
- os.Exit(0)
-}
-
-func minifyWorker(mimetype string, chanTasks <-chan Task, chanFails chan<- int) {
- fails := 0
- for task := range chanTasks {
- if ok := minify(mimetype, task); !ok {
- fails++
- }
- }
- chanFails <- fails
-}
-
-func getMimetype(mimetype, filetype string, useStdin bool) string {
- if mimetype == "" && filetype != "" {
- var ok bool
- if mimetype, ok = filetypeMime[filetype]; !ok {
- Error.Fatalln("cannot find mimetype for filetype", filetype)
- }
- }
- if mimetype == "" && useStdin {
- Error.Fatalln("must specify mimetype or filetype for stdin")
- }
-
- if verbose {
- if mimetype == "" {
- Info.Println("infer mimetype from file extensions")
- } else {
- Info.Println("use mimetype", mimetype)
- }
- }
- return mimetype
-}
-
-func sanitizePath(p string) string {
- p = filepath.ToSlash(p)
- isDir := p[len(p)-1] == '/'
- p = path.Clean(p)
- if isDir {
- p += "/"
- } else if info, err := os.Stat(p); err == nil && info.Mode().IsDir() {
- p += "/"
- }
- return p
-}
-
-func validFile(info os.FileInfo) bool {
- if info.Mode().IsRegular() && len(info.Name()) > 0 && (hidden || info.Name()[0] != '.') {
- if pattern != nil && !pattern.MatchString(info.Name()) {
- return false
- }
-
- ext := path.Ext(info.Name())
- if len(ext) > 0 {
- ext = ext[1:]
- }
-
- if _, ok := filetypeMime[ext]; !ok {
- return false
- }
- return true
- }
- return false
-}
-
-func validDir(info os.FileInfo) bool {
- return info.Mode().IsDir() && len(info.Name()) > 0 && (hidden || info.Name()[0] != '.')
-}
-
-func expandInputs(inputs []string, dirDst bool) ([]Task, bool) {
- ok := true
- tasks := []Task{}
- for _, input := range inputs {
- input = sanitizePath(input)
- info, err := os.Stat(input)
- if err != nil {
- Error.Println(err)
- ok = false
- continue
- }
-
- if info.Mode().IsRegular() {
- tasks = append(tasks, Task{[]string{filepath.ToSlash(input)}, "", ""})
- } else if info.Mode().IsDir() {
- expandDir(input, &tasks, &ok)
- } else {
- Error.Println("not a file or directory", input)
- ok = false
- }
- }
-
- if len(tasks) > 1 && !dirDst {
- // concatenate
- tasks[0].srcDir = ""
- for _, task := range tasks[1:] {
- tasks[0].srcs = append(tasks[0].srcs, task.srcs[0])
- }
- tasks = tasks[:1]
- }
-
- if verbose && ok {
- if len(inputs) == 0 {
- Info.Println("minify from stdin")
- } else if len(tasks) == 1 {
- if len(tasks[0].srcs) > 1 {
- Info.Println("minify and concatenate", len(tasks[0].srcs), "input files")
- } else {
- Info.Println("minify input file", tasks[0].srcs[0])
- }
- } else {
- Info.Println("minify", len(tasks), "input files")
- }
- }
- return tasks, ok
-}
-
-func expandDir(input string, tasks *[]Task, ok *bool) {
- if !recursive {
- if verbose {
- Info.Println("expanding directory", input)
- }
-
- infos, err := ioutil.ReadDir(input)
- if err != nil {
- Error.Println(err)
- *ok = false
- }
- for _, info := range infos {
- if validFile(info) {
- *tasks = append(*tasks, Task{[]string{path.Join(input, info.Name())}, input, ""})
- }
- }
- } else {
- if verbose {
- Info.Println("expanding directory", input, "recursively")
- }
-
- err := filepath.Walk(input, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if validFile(info) {
- *tasks = append(*tasks, Task{[]string{filepath.ToSlash(path)}, input, ""})
- } else if info.Mode().IsDir() && !validDir(info) && info.Name() != "." && info.Name() != ".." { // check for IsDir, so we don't skip the rest of the directory when we have an invalid file
- return filepath.SkipDir
- }
- return nil
- })
- if err != nil {
- Error.Println(err)
- *ok = false
- }
- }
-}
-
-func expandOutputs(output string, tasks *[]Task) bool {
- if verbose {
- if output == "" {
- Info.Println("minify to stdout")
- } else if output[len(output)-1] != '/' {
- Info.Println("minify to output file", output)
- } else if output == "./" {
- Info.Println("minify to current working directory")
- } else {
- Info.Println("minify to output directory", output)
- }
- }
-
- if output == "" {
- return true
- }
-
- ok := true
- for i, t := range *tasks {
- var err error
- (*tasks)[i].dst, err = getOutputFilename(output, t)
- if err != nil {
- Error.Println(err)
- ok = false
- }
- }
- return ok
-}
-
-func getOutputFilename(output string, t Task) (string, error) {
- if len(output) > 0 && output[len(output)-1] == '/' {
- rel, err := filepath.Rel(t.srcDir, t.srcs[0])
- if err != nil {
- return "", err
- }
- return path.Clean(filepath.ToSlash(path.Join(output, rel))), nil
- }
- return output, nil
-}
-
-func openInputFile(input string) (io.ReadCloser, error) {
- var r *os.File
- if input == "" {
- r = os.Stdin
- } else {
- err := try.Do(func(attempt int) (bool, error) {
- var ferr error
- r, ferr = os.Open(input)
- return attempt < 5, ferr
- })
- if err != nil {
- return nil, err
- }
- }
- return r, nil
-}
-
-func openOutputFile(output string) (*os.File, error) {
- var w *os.File
- if output == "" {
- w = os.Stdout
- } else {
- if err := os.MkdirAll(path.Dir(output), 0777); err != nil {
- return nil, err
- }
- err := try.Do(func(attempt int) (bool, error) {
- var ferr error
- w, ferr = os.OpenFile(output, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
- return attempt < 5, ferr
- })
- if err != nil {
- return nil, err
- }
- }
- return w, nil
-}
-
-func minify(mimetype string, t Task) bool {
- if mimetype == "" {
- for _, src := range t.srcs {
- if len(path.Ext(src)) > 0 {
- srcMimetype, ok := filetypeMime[path.Ext(src)[1:]]
- if !ok {
- Error.Println("cannot infer mimetype from extension in", src)
- return false
- }
- if mimetype == "" {
- mimetype = srcMimetype
- } else if srcMimetype != mimetype {
- Error.Println("inferred mimetype", srcMimetype, "of", src, "for concatenation unequal to previous mimetypes", mimetype)
- return false
- }
- }
- }
- }
-
- srcName := strings.Join(t.srcs, " + ")
- if len(t.srcs) > 1 {
- srcName = "(" + srcName + ")"
- }
- if srcName == "" {
- srcName = "stdin"
- }
- dstName := t.dst
- if dstName == "" {
- dstName = "stdin"
- } else {
- // rename original when overwriting
- for i := range t.srcs {
- if t.srcs[i] == t.dst {
- t.srcs[i] += ".bak"
- err := try.Do(func(attempt int) (bool, error) {
- ferr := os.Rename(t.dst, t.srcs[i])
- return attempt < 5, ferr
- })
- if err != nil {
- Error.Println(err)
- return false
- }
- break
- }
- }
- }
-
- fr, err := NewConcatFileReader(t.srcs, openInputFile)
- if err != nil {
- Error.Println(err)
- return false
- }
- if mimetype == filetypeMime["js"] {
- fr.SetSeparator([]byte("\n"))
- }
- r := NewCountingReader(fr)
-
- fw, err := openOutputFile(t.dst)
- if err != nil {
- Error.Println(err)
- fr.Close()
- return false
- }
- var w *countingWriter
- if fw == os.Stdout {
- w = NewCountingWriter(fw)
- } else {
- w = NewCountingWriter(bufio.NewWriter(fw))
- }
-
- success := true
- startTime := time.Now()
- if err = m.Minify(mimetype, w, r); err != nil {
- Error.Println("cannot minify "+srcName+":", err)
- success = false
- }
- if verbose {
- dur := time.Since(startTime)
- speed := "Inf MB"
- if dur > 0 {
- speed = humanize.Bytes(uint64(float64(r.N) / dur.Seconds()))
- }
- ratio := 1.0
- if r.N > 0 {
- ratio = float64(w.N) / float64(r.N)
- }
-
- stats := fmt.Sprintf("(%9v, %6v, %5.1f%%, %6v/s)", dur, humanize.Bytes(uint64(w.N)), ratio*100, speed)
- if srcName != dstName {
- Info.Println(stats, "-", srcName, "to", dstName)
- } else {
- Info.Println(stats, "-", srcName)
- }
- }
-
- fr.Close()
- if bw, ok := w.Writer.(*bufio.Writer); ok {
- bw.Flush()
- }
- fw.Close()
-
- // remove original that was renamed, when overwriting files
- for i := range t.srcs {
- if t.srcs[i] == t.dst+".bak" {
- if err == nil {
- if err = os.Remove(t.srcs[i]); err != nil {
- Error.Println(err)
- return false
- }
- } else {
- if err = os.Remove(t.dst); err != nil {
- Error.Println(err)
- return false
- } else if err = os.Rename(t.srcs[i], t.dst); err != nil {
- Error.Println(err)
- return false
- }
- }
- t.srcs[i] = t.dst
- break
- }
- }
- return success
-}