From 989116970468ab07936d6b3a946836975a1314a2 Mon Sep 17 00:00:00 2001 From: Parnic Date: Mon, 26 Nov 2018 15:53:01 -0600 Subject: [PATCH] Initial commit --- compile.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ config.go | 8 ++++ main.go | 23 +++++++++++ 3 files changed, 150 insertions(+) create mode 100644 compile.go create mode 100644 config.go create mode 100644 main.go diff --git a/compile.go b/compile.go new file mode 100644 index 0000000..1af9a24 --- /dev/null +++ b/compile.go @@ -0,0 +1,119 @@ +package precompiler + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/tdewolff/minify" + "github.com/tdewolff/minify/css" + "github.com/tdewolff/minify/js" +) + +// FileType represents a supported file extension (without the .) +type FileType string + +// supported file types +const ( + CSS FileType = "css" + JS FileType = "js" +) + +func initMinify() *minify.M { + minifier := minify.New() + minifier.AddFunc("text/css", css.Minify) + minifier.AddFunc("text/js", js.Minify) + return minifier +} + +func getBytes(config Config, minifier *minify.M) (map[FileType]*bytes.Buffer, error) { + buf := make(map[FileType]*bytes.Buffer) + + for _, file := range config.Files { + if file, err := os.Open(file); err == nil { + ext := FileType(filepath.Ext(file.Name())[1:]) + if !supportedFileType(ext) { + fmt.Println("Unsupported file type:", file.Name()) + continue + } + + if buf[ext] == nil { + buf[ext] = &bytes.Buffer{} + } + + if config.Minify { + if err = minifier.Minify("text/"+string(ext), buf[ext], file); err != nil { + return nil, err + } + } else { + if _, err = buf[ext].ReadFrom(file); err != nil { + return nil, err + } + } + } else { + return nil, err + } + } + + return buf, nil +} + +// CompileResult holds the results of compilation +type CompileResult struct { + Bytes []byte + Hash string + OutputPath string +} + +func finalize(config Config, buf map[FileType]*bytes.Buffer) (map[FileType]*CompileResult, error) { + ret := make(map[FileType]*CompileResult, len(buf)) + + for key, b := range buf { + if b.Len() > 0 { + bytes := b.Bytes() + hash := sha256.Sum256(bytes) + ret[key] = &CompileResult{ + Bytes: bytes, + Hash: hex.EncodeToString(hash[:]), + } + + if len(config.OutputDir) > 0 { + ext := "." + string(key) + if config.Minify { + ext = ".min" + ext + } + dir := filepath.Join(config.OutputDir, string(key)) + os.MkdirAll(dir, os.ModeDir) + destFile := filepath.Join(dir, "app-"+ret[key].Hash+ext) + ret[key].OutputPath = destFile + ioutil.WriteFile(destFile, bytes, 0644) + } + } + } + + return ret, nil +} + +// Compile compiles files indicated by the config into a single file per type +func Compile(config Config) (map[FileType]*CompileResult, error) { + var buf map[FileType]*bytes.Buffer + var err error + if buf, err = getBytes(config, initMinify()); err != nil { + return nil, err + } + + return finalize(config, buf) +} + +func supportedFileType(t FileType) bool { + switch t { + case CSS, JS: + return true + } + + return false +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..39c1bee --- /dev/null +++ b/config.go @@ -0,0 +1,8 @@ +package precompiler + +// Config holds information on how to run the compiler +type Config struct { + Files []string + Minify bool + OutputDir string +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..5766193 --- /dev/null +++ b/main.go @@ -0,0 +1,23 @@ +package precompiler + +func main() { + Compile(Config{ + Files: []string{ + "assets/css/bootstrap.min.default.css", + "assets/css/font-awesome.min.css", + "assets/css/tempusdominus-bootstrap-4.min.css", + "assets/css/roboto.css", + "assets/css/application.css", + "assets/js/jquery.min.js", + "assets/js/popper.min.js", + "assets/js/bootstrap.min.js", + "assets/js/linkify.min.js", + "assets/js/linkify-jquery.min.js", + "assets/js/moment.min.js", + "assets/js/tempusdominus-bootstrap-4.min.js", + "assets/js/application.js", + }, + Minify: true, + OutputDir: "assets/", + }) +}