#!/usr/bin/env ruby require 'rubygems' require 'pathname' begin gem_home = Pathname.new(ENV["GEM_HOME"]).realpath.to_s current_dir = File.dirname(Pathname.new(__FILE__).realpath.to_s) if current_dir.index(gem_home) != 0 && File.exists?(current_dir + '/../lib/jsus.rb') # If we are outside gem home, # override whatever they got there # with local version of jsus $:.unshift File.expand_path(current_dir + "/../lib") end rescue Exception => e # Something weird happened during our checks, # but it's probably nothing. end require 'jsus' require "fileutils" require "optparse" module Jsus class CLI class < e puts "You need to install fssm gem for --watch option." puts "You may also want to install rb-fsevent for OS X" raise e end end attr_accessor :options def initialize(options = Jsus::CLI.cli_options) @options = options end def launch checkpoint(:start) setup_output_directory preload_pool load_package compile_package post_process if options[:postproc] compress_package if options[:compress] package_filename = File.join(@output_dir, @package.filename) File.open(package_filename, 'w') {|f| f << @package_content } generate_supplemental_files validate_sources generate_includes if options[:generate_includes] generate_docs if options[:documented_classes] && !options[:documented_classes].empty? output_benchmarks end def setup_output_directory @output_dir = options[:output_dir] FileUtils.mkdir_p(@output_dir) end def preload_pool @pool = if options[:deps_dir] Jsus::Pool.new(options[:deps_dir]) else Jsus::Pool.new end checkpoint(:pool) end def load_package @package = Jsus::Package.new(options[:input_dir], :pool => @pool) @package.include_dependencies! checkpoint(:dependencies) end def compile_package @package_content = @package.compile(nil) checkpoint(:compilation) end def post_process options[:postproc].each do |processor| case processor.strip when /^moocompat12$/i @package_content.gsub!(/\/\/<1.2compat>.*?\/\/<\/1.2compat>/m, '') @package_content.gsub!(/\/\*<1.2compat>\*\/.*?\/\*<\/1.2compat>\*\//m, '') when /^mooltie8$/i @package_content.gsub!(/\/\/.*?\/\/<\/ltIE8>/m, '') @package_content.gsub!(/\/\*\*\/.*?\/\*<\/ltIE8>\*\//m, '') else $stderr.puts "Unknown post-processor: #{processor}" end end checkpoint(:postproc) end def compress_package require 'yui/compressor' compressor = YUI::JavaScriptCompressor.new(:munge => true) compressed_content = compressor.compress(@package_content) if compressed_content != "" compression_ratio = compressed_content.size.to_f / @package_content.size.to_f @package_content = compressed_content else compression_ratio = 1.00 puts "ERROR: YUI compressor could not parse input. Falling back to uncompressed version" puts "Compressor command used: #{compressor.command.join(' ')}" end puts "Compression ratio: #{sprintf("%.2f%%", compression_ratio * 100)}" if Jsus.verbose? checkpoint(:compress) rescue LoadError puts 'ERROR: You need "yui-compressor" gem in order to use --compress option' end def generate_supplemental_files @package.generate_scripts_info(@output_dir) unless options[:without_scripts_info] @package.generate_tree(@output_dir) unless options[:without_tree_info] checkpoint(:supplemental_files) end def generate_includes includes_root = options[:includes_root] || @output_dir File.open(File.join(@output_dir, "includes.js"), "w") do |f| c = Jsus::Container.new(*(@package.source_files.to_a + @package.linked_external_dependencies.to_a)) script = %{ (function(prefix, loader) { var sources = %sources%; if (!loader) loader = function(path) { document.write(''); } for (var i = 0, j = sources.length; i < j; i++) loader(sources[i]); })(window.prefix, window.loader);}.sub("%sources%", JSON.pretty_generate(c.required_files(includes_root))) f.puts script end checkpoint(:includes) end def generate_docs documenter = Jsus::Util::Documenter.new(:highlight_source => !options[:no_syntax_highlight]) @package.source_files.each {|source| documenter << source } @pool.sources.each {|source| documenter << source } documenter.only(options[:documented_classes]).generate(@output_dir + "/docs") checkpoint(:documentation) end def validate_sources validators_map = {"mooforge" => Jsus::Util::Validator::Mooforge} (options[:validators] || []).each do |validator_name| if validator = validators_map[validator_name] errors = validator.new(@pool.sources.to_a & @package.source_files.to_a).validation_errors unless errors.empty? puts "Validator #{validator_name} found errors: " errors.each {|e| puts " * #{e}"} end else puts "No such validator: #{validator_name}" end end checkpoint(:validators) end def output_benchmarks if options[:benchmark] puts "Benchmarking results:" puts "Total execution time: #{formatted_time_for(:all)}" puts "" puts "Of them:" puts "Pool preloading time: #{formatted_time_for(:pool)}" puts "Docs generation time: #{formatted_time_for(:documentation)}" if options[:documented_classes] && !options[:documented_classes].empty? puts "Total compilation time: #{formatted_time_for(:compilation)}" puts "Post-processing time: #{formatted_time_for(:postproc)}" if options[:postproc] puts "Compression time: #{formatted_time_for(:compress)}" if options[:compress] end end def checkpoint(checkpoint_name) @checkpoints ||= {} @time_for ||= {} @checkpoints[checkpoint_name] = Time.now if @last_checkpoint @time_for[checkpoint_name] = @checkpoints[checkpoint_name] - @last_checkpoint end @last_checkpoint = Time.now end def checkpoint?(checkpoint_name) @checkpoints[checkpoint_name] end def time_for(checkpoint_name) if checkpoint_name == :all @last_checkpoint - @checkpoints[:start] else @time_for[checkpoint_name] end end def formatted_time_for(checkpoint_name) "#{format("%.3f", time_for(checkpoint_name))}s" end end end Jsus::CLI.run!