require 'fileutils' module HtmlMockup::Release::Processors class Requirejs < Base def initialize(options = {}) @options = { :build_files => {"javascripts/site.build.js" => {:dir => "javascripts"}}, :rjs => "r.js", :node => "node" }.update(options) end # @option options [Hash] :build_files An a hash of files to build (as key) and the target as a hash with either {:dir => "STRING"} or {:file => "STRING"} in the release to put it as value, each one will be built in a separate directory. (default is {"javascripts/site.build.js" => {:dir => "javascripts"}}) # @option options [String] :node The system path for node (defaults to "node" in path) # @option options [String] :rjs The system path to the requirejs optimizer (r.js) (defaults to "../vendor/requirejs/r.js" (relative to source_path)) def call(release, options={}) @options.update(options) begin `#{@options[:node]} -v` rescue Errno::ENOENT raise RuntimeError, "Could not find node in #{@options[:node].inspect}" end rjs_command = rjs_check() @options[:build_files].each do |build_file, target| build_file = release.build_path + build_file if target.kind_of?(Hash) if target[:dir] target = target[:dir] target_type = :dir elsif target[:file] target = target[:file] target_type = :file end else # Old style target_type = :dir end target = release.build_path + target release.log(self, "Optimizing #{build_file}") # Hack to create tempfile in build t = Tempfile.new("requirejs", release.build_path) tmp_build_dir = t.path t.close t.unlink # Run r.js optimizer if target_type == :dir output = `#{rjs_command} -o #{build_file} dir=#{tmp_build_dir}` else output = `#{rjs_command} -o #{build_file} out=#{tmp_build_dir}/out.js` end release.debug(self, output) # Check if r.js succeeded unless $?.success? raise RuntimeError, "Asset compilation with node failed.\nr.js output:\n #{output}" end if File.exist?(target) release.log(self, "Removing target #{target}") FileUtils.rm_rf(target) end # Move the tmp_build_dir to target if target_type == :dir FileUtils.mv(tmp_build_dir, target) end FileUtils.mv("#{tmp_build_dir}/out.js", target) FileUtils.rm_rf(tmp_build_dir) end end # Incase both a file and bin version are availble file version is taken # # @return rjs_command to invoke r.js optimizer with def rjs_check(path = @options[:rjs]) rjs_command = rjs_file(path) || rjs_bin(path) if !(rjs_command) raise RuntimeError, "Could not find r.js optimizer in #{path.inspect} - try updating this by npm install -g requirejs" end rjs_command end protected # Checks if the param is the r.js lib from file system # # @param [String] Path r.js lib may be kept to be invoked with node # @return [String] the cli invokement string def rjs_file(path) if File.exist?(path) "#{@options[:node]} #{path}" else false end end # Checks if r.js is installed as bin # # @param [String] Path to r.js bin # @return [String] the cli invokement string def rjs_bin(path) begin `#{path} -v` rescue Errno::ENOENT false else "#{path}" end end end end