Created
May 2, 2010 13:20
-
-
Save kares/387117 to your computer and use it in GitHub Desktop.
rake task for compiling javascripts using google's closure compiler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# configure these defaults based on Your needs : | |
JS_FILES_DIR = 'public/javascripts' # the javascripts base directory | |
EXCLUDED_JS_FILES = %W{ cache/* test/* } # excluded from compilation | |
OUT_FILE_JS_EXT_PREFIX = ".min" # override with `rake ... OUT_EXT=_pack` | |
COMPILER_JAR_PATH = "lib/java/build/compiler.jar" # adjust the jar path ! | |
# only required if running under JRuby - will compile multiple files | |
# faster as it does not need to run system `java -jar ...` commands. | |
#COMPILER_MAIN_CLASS = "com.google.javascript.jscomp.CommandLineRunner" | |
COMPILER_DOWNLOAD_URI = 'http://closure-compiler.googlecode.com/files/compiler-latest.zip' | |
# | |
# a javascript compile rake task (uses google's closure compiler). | |
# | |
# @see http://code.google.com/closure/compiler/ | |
# | |
namespace :javascript do | |
desc "js compilation - minifies given file(s) (specify FILE=some.js or FILES=some.js,another.js)" | |
task :compile do | |
js_files = ENV['FILE'] || ENV['FILES'] | |
raise "no FILE(S) given" if js_files.blank? | |
js_files = js_files.split(',').map do |js_file| | |
if File.exist?(js_file) | |
js_file | |
else | |
Dir[ File.join(JS_FILES_DIR, js_file) ] | |
end | |
end.compact.flatten | |
js_files.each do |js_file| | |
options = build_options(js_file) | |
compile_js_if_necessary(js_file, options) | |
end | |
end | |
desc "js compilation - minifies all /javascripts/**/*.js files" | |
task :compile_all do | |
js_files = Dir[ File.join(JS_FILES_DIR, "**/*[^#{output_ext_prefix}].js") ] | |
js_files = filter_excluded_js_files(js_files) | |
puts "matched #{js_files.size} .js file(s)" | |
if ENV['OUT_FILE'].blank? | |
js_files.each do |js_files| | |
options = build_options(js_files) | |
compile_js_if_necessary(js_files, options) | |
end | |
else | |
options = build_options.merge! :force => true | |
compile_js_if_necessary(js_files, options) | |
end | |
end | |
desc "deletes all (compiled) /javascripts/**/*#{OUT_FILE_JS_EXT_PREFIX}.js files" | |
task :clean_compiled do | |
js_files = Dir[ File.join(JS_FILES_DIR, "**/*#{output_ext_prefix}.js") ] | |
puts "removing #{js_files.size} file(s)" | |
FileUtils.rm js_files | |
end | |
desc "downloads (and extracts) the latest closure compiler.jar into COMPILER_JAR_PATH path (#{COMPILER_JAR_PATH})" | |
task :download_jar do | |
require 'uri'; require 'net/http'; require 'tempfile' | |
uri = URI.parse(COMPILER_DOWNLOAD_URI) | |
puts "downloading compiler jar from: #{COMPILER_DOWNLOAD_URI}" | |
response = Net::HTTP.start(uri.host, uri.port) do |http| | |
http.get(uri.path) | |
end | |
case response | |
when Net::HTTPSuccess | |
file_data, content_type = response.body, response.content_type | |
raise "no data returned from #{uri}" if file_data.nil? || file_data.size == 0 | |
else | |
raise "download from #{uri} failed with response: #{response}" | |
end | |
filename = uri.path.split('/')[-1] | |
Tempfile.open(filename) do |tmpfile| | |
tmpfile << file_data | |
filename = tmpfile.path | |
end | |
if content_type =~ /application\/(x-)?zip/ | |
# compiler-latest.zip with 3 entries : | |
# - compiler.jar | |
# - COPYING | |
# - README | |
extract_path = File.dirname(compiler_jar_path) | |
unless File.exist?(extract_path) | |
FileUtils.mkdir_p(extract_path) | |
end | |
# -u update files, create if necessary : | |
system "unzip -u #{filename} -d #{extract_path}" | |
else | |
raise "unexpected content-type: #{content_type}" | |
end | |
end | |
#======================================================================== | |
def build_options(js_file = nil) | |
options = {} | |
options[:force] = false | |
if force = ENV['FORCE'] | |
options[:force] = (force.to_s == true.to_s) | |
end | |
if out_file = ENV['OUT_FILE'] | |
options[:output] = out_file | |
else | |
options[:output] = output_filename(js_file) | |
end | |
options | |
end | |
def filter_excluded_js_files(js_files) | |
return js_files if EXCLUDED_JS_FILES.blank? | |
js_files.map do |files| | |
excluded = EXCLUDED_JS_FILES.find do |excluded_pattern| | |
expanded_pattern = File.join(JS_FILES_DIR, excluded_pattern) | |
File.fnmatch(expanded_pattern, files) | |
end | |
excluded ? nil : files | |
end.compact | |
end | |
def output_ext_prefix | |
ENV['OUT_EXT'].presence || OUT_FILE_JS_EXT_PREFIX | |
end | |
# /javascript/application.js => /javascript/application.min.js | |
def output_filename(js_file) | |
output_file = File.basename(js_file, File.extname(js_file)) | |
output_file = File.join(File.dirname(js_file), output_file) | |
return output_file + output_ext_prefix + File.extname(js_file) | |
end | |
def compile_js_if_necessary(js_file, options) | |
output_file = options[:output] | |
File.delete(output_file) if options[:force] | |
if ! File.exist?(output_file) || | |
File.mtime(js_file) > File.mtime(output_file) | |
compile_js(js_file, options) | |
else | |
nil # no need to compile otherwise ... | |
end | |
end | |
def compile_js(files, options) | |
unless File.exist?(compiler_jar_path) | |
Rake::Task["javascript:download_jar"].invoke | |
end | |
unless File.exist?(compiler_jar_path) | |
puts "#{compiler_jar_path} not found !" | |
raise "try to run `rake javascript:download_jar` manually to download the compiler jar" | |
end | |
files = [ files ] unless files.is_a?(Array) | |
compiler_options = {} | |
compiler_options['--js'] = files.join(' ') | |
compiler_options['--js_output_file'] = options[:output] | |
compiler_options['--compilation_level'] = ENV['LEVEL'] || 'SIMPLE_OPTIMIZATIONS' | |
puts "compiling #{files.size} javascript file(s) into #{options[:output]}" | |
# NOTE: JRuby "version" disabled as the closure java code does a System.exit ! | |
#if defined?(JRUBY_VERSION) # JRuby style - will be much faster : | |
# require 'java'; require compiler_jar_path | |
# instance_eval "#{COMPILER_MAIN_CLASS}.main( #{compiler_options.to_a.flatten.inspect} )" | |
#else | |
system "java -jar #{compiler_jar_path} #{compiler_options.to_a.join(' ')}" | |
#end | |
end | |
def compiler_jar_path | |
ENV['COMPILER_JAR_PATH'].presence || COMPILER_JAR_PATH | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment