#Copyright (c) 2017, Remy Luisant # #Permission to use, copy, modify, and/or distribute this software for any #purpose with or without fee is hereby granted, provided that the above #copyright notice and this permission notice appear in all copies. # #THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH #REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY #AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, #INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM #LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE #OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR #PERFORMANCE OF THIS SOFTWARE. using SHA type Record id :: Integer minifier_path :: String sha :: String size :: Integer depth :: Integer end type Minifier name :: String supports_pipes :: Bool command_namer :: Union{Function, Void} end type Next so_far :: Record minifier :: Minifier end minifiers = [ Minifier("csso", true, nothing), Minifier("cssnano", true, nothing), Minifier("cleancss -O2 all:on", true, nothing) ] crass = Minifier("crass", false, function(input_id, output_id) return "crass $(input_id) --optimize --O1 > $(output_id) 2> /dev/null" end ) push!(minifiers, crass) if length(ARGS) < 1 || length(ARGS) > 3 error("Usage: julia [--color=yes] remynifier.jl target.css [max_depth] [compression]") end if length(ARGS) > 1 depth_limit = parse(Int64, ARGS[2]) else depth_limit = 1 end if length(ARGS) == 3 compression = ARGS[3] else compression = nothing end tick = 0 tick_limit = 40 new_id = 0 records = [] queue = [] hashes = Dict() try mkdir("tmp") catch error("You need to clean up after the previous, seemingly aborted, run.\nYou have a tmp directory present.\nI'm not deleting anything, just in case you spent hours on getting some results that I'd now wreck.") end try cp(ARGS[1], "tmp/0") catch rm("tmp", recursive=true) error("I don't think the file you want me to Remynify exists...") end try cp(ARGS[1], ARGS[1] * ".min") catch rm("tmp", recursive=true) error("Eh, I'm seeing you already have a .min file. You sure about this, boss?") end cd("tmp") starting_point = Record(0, ARGS[1], bytes2hex(sha256(readstring(open("0")))), filesize("0"), 0) best_size = starting_point.size push!(records, starting_point) for minifier in minifiers push!(queue, Next(starting_point, minifier)) end prioritize = false while queue != [] if tick == tick_limit potential = 0 branch_factor = length(minifiers) for item in queue depth = item.so_far.depth potential_item = branch_factor^(depth_limit - min(depth_limit, depth + 1)) potential = potential + potential_item end progress = round(100 * (1.0 - (potential / (branch_factor^(depth_limit))))) tick = 0 println("") println("$(progress)% -- Best: $(best_size) In queue: $(length(queue)) Estimated left: $(potential)") end tick = tick + 1 new_item = shift!(queue) new_id = new_id + 1 f = open("command.sh", "w") if new_item.minifier.supports_pipes println(f, "cat $(new_item.so_far.id) | $(new_item.minifier.name) > $(new_id) 2> /dev/null") else println(f, new_item.minifier.command_namer(new_item.so_far.id, new_id)) end close(f) try run(`timeout 10s bash command.sh`) catch print_with_color(:red, "X") continue end if compression == nothing size_file = "$(new_id)" elseif compression == "g" size_file = "$(new_id).gz" run(`gzip -k -9 $(new_id)`) elseif compression == "z" size_file = "$(new_id).gz" run(`zopfli --i100 $(new_id)`) elseif compression == "b" size_file = "$(new_id).br" run(`bro --window 24 --quality 11 --input $(new_id) --output $(new_id).br`) else error("I have no idea how to perform this kind of compression: $(compression)") end tmp = open("$(new_id)") result = Record(new_id, new_item.so_far.minifier_path * ">" * new_item.minifier.name, bytes2hex(sha256(readstring(tmp))), filesize("$size_file"), new_item.so_far.depth + 1) close(tmp) if haskey(hashes, result.sha) print_with_color(:yellow, "R") continue else push!(records, result) if best_size > result.size prioritize = true best_size = result.size print_with_color(:cyan, "+") cp("$(result.id)", "../$(ARGS[1])" * ".min", remove_destination=true) elseif best_size == result.size print_with_color(:cyan, "@") prioritize = true else if result.depth < depth_limit print_with_color(:green, ".") else print_with_color(:magenta, "D") end prioritize = false end hashes[result.sha] = true if result.depth < depth_limit || prioritize for minifier in minifiers if prioritize == true unshift!(queue, Next(result, minifier)) else push!(queue, Next(result, minifier)) end end end end end println("\nBest size: $(best_size)") for record in records if record.size == best_size println("Minifier sequence: $(record.minifier_path)") end end cd("..") rm("tmp", recursive=true)