class Closure::Compiler::Util

Closure Script extends compiler.jar by transforming the arguments in novel ways. The most obvious augmentation is to support –ns for compiling namespaces. We can also expand paths to a new base, work with modules, and much more. These all will directly modify args. @private This is a high value target for refactoring in minor version updates.

Public Class Methods

arg_values(args, options) click to toggle source

Extracts the values for a options in the arguments. Use Array#last to emulate compiler.jar for single options. Will collect from an array of options. @param [Array<String>] args @param [String|Array<String>] options One or more options. @return [Array<String>]

# File lib/closure/compiler.rb, line 248
def self.arg_values(args, options)
  options = [options].flatten unless options.kind_of? Array
  values = []
  args_index = 0
  while args_index < args.length
    if options.include? args[args_index]
      values << args[args_index+1]
    end
    args_index = args_index + 2
  end
  values
end
augment(args, sources, env={}) click to toggle source

Main function to convert –ns arguments into –js arguments. Returns module info when modules are processed. @param [Array<String>] args @return [Array<Hash>] mods

# File lib/closure/compiler.rb, line 231
def self.augment(args, sources, env={})
  mods = extract_modules args
  if mods
    module_augment args, sources, mods, env
  else
    namespace_augment args, sources, [], env
  end
  mods
end
expand_paths(args, base) click to toggle source

Expands all filesystem argument values to a specified folder. @param [Array<String>] args @return [Array<String>] args

# File lib/closure/compiler.rb, line 169
def self.expand_paths(args, base)
  file_options = INPUT_OPTIONS + OUTPUT_OPTIONS
  args_index = 0
  while args_index < args.length
    option, value = args[args_index, 2]
    value = File.expand_path value, base
    if file_options.include? option
      args[args_index+1] = value
    end
    args_index = args_index + 2
  end
  args
end
module_info(mods, var = 'MODULE_INFO') click to toggle source

The javascript snippet for module info @param [Array<Hash>] mods

# File lib/closure/compiler.rb, line 193
def self.module_info(mods, var = 'MODULE_INFO')
  js = "var #{var} = {"
  js += mods.map do |mod|
    reqs = mod[:requires].map{ |r| r.dump }
    "#{mod[:name].dump}: [#{reqs.join ', '}]"
  end.join ', '
  js += "};\n"
end
module_path(path, var = 'MODULE_PATH') click to toggle source

The javascript snippet for module info @param [Array<Hash>] mods

# File lib/closure/compiler.rb, line 186
def self.module_path(path, var = 'MODULE_PATH')
  js = "var #{var} = #{path.dump};\n"
end
module_uris_compiled(mods, sources, prefix, var = 'MODULE_URIS') click to toggle source

The javascript snippet for compiled module file locations @param [Array<Hash>] mods

# File lib/closure/compiler.rb, line 217
def self.module_uris_compiled(mods, sources, prefix, var = 'MODULE_URIS')
  js = "var #{var} = {\n"
  js += mods.map do |mod|
    file = sources.src_for prefix + mod[:name] + '.js'
    "#{mod[:name].dump}: [#{file.dump}]"
  end.join ",\n"
  js += "\n};\n"
end
module_uris_raw(mods, sources, var = 'MODULE_URIS') click to toggle source

The javascript snippet for raw module file locations @param [Array<Hash>] mods

# File lib/closure/compiler.rb, line 205
def self.module_uris_raw(mods, sources, var = 'MODULE_URIS')
  js = "var #{var} = {\n"
  js += mods.map do |mod|
    files = mod[:files].map{ |r| (sources.src_for r).dump }
    "#{mod[:name].dump}: [\n#{files.join ",\n"}]"
  end.join ",\n"
  js += "\n};\n"
end

Private Class Methods

extract_modules(args) click to toggle source

@param [Array<String>] args @return [Array<Hash>] mods

# File lib/closure/compiler.rb, line 354
def self.extract_modules(args)
  found_starred = false
  found_numeric = false
  mod_args = []
  mods = []
  args_index = args.length
  while args_index > 0
    args_index = args_index - 2
    option, value = args[args_index, 2]
    if %w{--js --ns}.include? option
      mod_args.unshift args.delete_at args_index + 1
      mod_args.unshift args.delete_at args_index
    elsif option == '--module'
      if mod_args.empty?
        raise "No --js or --ns files for --module #{value}"
      end
      mod = value.split ':'
      if mod[1] == '*'
        found_starred = true
      else
        found_numeric = true
      end
      mods.unshift({
        :name => mod[0],
        :requires => (mod[2]||'').split(','),
        :args => mod_args
      })
      mod_args = []
      2.times {args.delete_at args_index}
    end
  end
  unless mod_args.empty? or mods.empty?
    raise 'Automatic --module must appear before first --js or --ns option.'
  end
  if found_starred and found_numeric
    raise 'Static and automatic --module options can not be mixed.'
  end
  if mods.empty?
    args.insert(-1, *mod_args)
    nil
  else
    mods
  end
end
module_augment(args, sources, mods, env) click to toggle source

Converts –ns arguments into –js arguments @param [Array<String>] args

# File lib/closure/compiler.rb, line 267
def self.module_augment(args, sources, mods, env)
  walk_modules sources, mods, env
  files = []
  mods.each do |mod|
    mod_args = mod[:args]
    mod_files = (mod[:files] ||= [])
    # We are adding the bubbled namespaces to the end.
    # This allows moduleManager.setLoaded to fire earlier
    # since the bubbled files are needed only for the children.
    (mod[:bubble]||[]).each do |mod_bubble|
      namespaces = sources.namespaces_for mod_bubble, env
      namespaces.each do |ns|
        mod_args << '--ns' << ns
      end
    end
    namespace_augment(mod_args, sources, files, env)
    args << '--module'
    args << "#{mod[:name]}:#{mod_args.size / 2}:#{mod[:requires].join(',')}"
    mod_args.each_slice(2) do |a,b|
      args << a << b
      mod_files << b
    end
  end
end
namespace_augment(args, sources, files, env) click to toggle source

Converts –ns arguments into –js arguments @param [Array<String>] args

# File lib/closure/compiler.rb, line 295
def self.namespace_augment(args, sources, files, env)
  files_index = files.length
  args_index = 0
  while args_index < args.length
    option, value = args[args_index, 2]
    if option == '--ns'
      sources.files_for(value, files, env)
      replacement = []
      while files_index < files.length
        replacement.push '--js'
        replacement.push files[files_index]
        files_index = files_index + 1
      end
      args[args_index, 2] = replacement
    else
      args_index = args_index + 2
    end
  end
end
walk_modules(sources, mods, env, seek=nil, mods_seen=[], files_seen=[]) click to toggle source

Insanity-inducing recursive explorer to find common files in child modules. Try every branch in the tree and bubble up common files as we see them.

# File lib/closure/compiler.rb, line 318
def self.walk_modules(sources, mods, env, seek=nil, mods_seen=[], files_seen=[])
  files = []
  mods_seen << seek
  mods.each do |mod|
    if (!seek and mod[:requires].empty?) or mod[:requires].include? seek
      next if mods_seen.include? mod[:name]
      # Find the needed files for this module's --ns args
      files_seen_dup = files_seen.dup
      mod[:args].each_slice(2) do |option, value|
        if option == '--ns'
          sources.files_for value, files_seen_dup, env
        end
      end
      mod_files = files_seen_dup[files_seen.size..-1]
      # Get the needed files for each of the modules requiring this mod
      files_sets = walk_modules sources, mods, env, mod[:name], mods_seen, files_seen_dup
      # Find the common files that will bubble up
      common_files = []
      child_files = files_sets.reduce([]){|memo, v|common_files |= memo&v; memo|v}
      mod[:bubble] = common_files
      # Clear bubbling files from children
      mods.each do |child_mod|
        if child_mod[:requires].include? mod[:name]
          child_mod[:bubble] -= common_files
        end
      end
      # Add to result array
      files << (mod_files + child_files).uniq
    end
  end
  files
end