class Uglifier

A wrapper around the UglifyJS interface

Constants

DEFAULTS

Default options for compilation

ES5FallbackPath

ES5 shims source path

EXTRA_OPTIONS
HarmonySourcePath

UglifyJS with Harmony source path

MANGLE_PROPERTIES_DEFAULTS
SOURCE_MAP_DEFAULTS
SourceMapPath

Source Map path

SourcePath

UglifyJS source path

SplitFallbackPath

String.split shim source path

UglifyJSWrapperPath

UglifyJS wrapper path

VERSION

Current version of Uglifier.

Public Class Methods

compile(source, options = {}) click to toggle source

Minifies JavaScript code using implicit context.

@param source [IO, String] valid JS source code. @param options [Hash] optional overrides to Uglifier::DEFAULTS @return [String] minified code.

# File lib/uglifier.rb, line 130
def self.compile(source, options = {})
  new(options).compile(source)
end
compile_with_map(source, options = {}) click to toggle source

Minifies JavaScript code and generates a source map using implicit context.

@param source [IO, String] valid JS source code. @param options [Hash] optional overrides to Uglifier::DEFAULTS @return [Array(String, String)] minified code and source map.

# File lib/uglifier.rb, line 139
def self.compile_with_map(source, options = {})
  new(options).compile_with_map(source)
end
new(options = {}) click to toggle source

Initialize new context for Uglifier with given options

@param options [Hash] optional overrides to Uglifier::DEFAULTS

# File lib/uglifier.rb, line 146
def initialize(options = {})
  (options.keys - DEFAULTS.keys - EXTRA_OPTIONS)[0..1].each do |missing|
    raise ArgumentError, "Invalid option: #{missing}"
  end
  @options = options

  source = harmony? ? source_with(HarmonySourcePath) : source_with(SourcePath)
  @context = ExecJS.compile(source)
end

Public Instance Methods

compile(source) click to toggle source

Minifies JavaScript code

@param source [IO, String] valid JS source code. @return [String] minified code.

# File lib/uglifier.rb, line 160
def compile(source)
  if @options[:source_map]
    compiled, source_map = run_uglifyjs(source, true)
    source_map_uri = Base64.strict_encode64(source_map)
    source_map_mime = "application/json;charset=utf-8;base64"
    compiled + "\n//# sourceMappingURL=data:#{source_map_mime},#{source_map_uri}"
  else
    run_uglifyjs(source, false)
  end
end
Also aliased as: compress
compile_with_map(source) click to toggle source

Minifies JavaScript code and generates a source map

@param source [IO, String] valid JS source code. @return [Array(String, String)] minified code and source map.

# File lib/uglifier.rb, line 176
def compile_with_map(source)
  run_uglifyjs(source, true)
end
compress(source)
Alias for: compile

Private Instance Methods

comment_options() click to toggle source
# File lib/uglifier.rb, line 304
def comment_options
  case comment_setting
  when :all, true
    true
  when :jsdoc
    "jsdoc"
  when :copyright
    encode_regexp(/(^!)|Copyright/i)
  when Regexp
    encode_regexp(comment_setting)
  else
    false
  end
end
comment_setting() click to toggle source
# File lib/uglifier.rb, line 335
def comment_setting
  if @options.has_key?(:output) && @options[:output].has_key?(:comments)
    @options[:output][:comments]
  elsif @options.has_key?(:comments)
    @options[:comments]
  else
    DEFAULTS[:output][:comments]
  end
end
compressor_options() click to toggle source
# File lib/uglifier.rb, line 282
def compressor_options
  defaults = conditional_option(
    DEFAULTS[:compress],
    :global_defs => @options[:define] || {}
  )

  conditional_option(
    @options[:compress],
    defaults,
    { :keep_fnames => keep_fnames?(:compress) }.merge(negate_iife_block)
  )
end
conditional_option(value, defaults, overrides = {}) click to toggle source
# File lib/uglifier.rb, line 418
def conditional_option(value, defaults, overrides = {})
  if value == true || value.nil?
    defaults.merge(overrides)
  elsif value
    defaults.merge(value).merge(overrides)
  else
    false
  end
end
enclose_options() click to toggle source
# File lib/uglifier.rb, line 398
def enclose_options
  if @options[:enclose]
    @options[:enclose].map do |pair|
      pair.first + ':' + pair.last
    end
  else
    false
  end
end
encode_regexp(regexp) click to toggle source
# File lib/uglifier.rb, line 408
def encode_regexp(regexp)
  modifiers = if regexp.casefold?
                "i"
              else
                ""
              end

  [regexp.source, modifiers]
end
error_message(result) click to toggle source
# File lib/uglifier.rb, line 222
def error_message(result)
  result['error']['message'] +
    if result['error']['message'].start_with?("Unexpected token") && !harmony?
      ". To use ES6 syntax, harmony mode must be enabled with "          "Uglifier.new(:harmony => true)."
    else
      ""
    end
end
extract_source_mapping_url(source) click to toggle source
# File lib/uglifier.rb, line 440
def extract_source_mapping_url(source)
  comment_start = %r{(?://|/\*\s*)}
  comment_end = %r{\s*(?:\r?\n?\*/|$)?}
  source_mapping_regex = /#{comment_start}[@#]\ssourceMappingURL=\s*(\S*?)#{comment_end}/
  rest = /\s#{comment_start}[@#]\s[a-zA-Z]+=\s*(?:\S*?)#{comment_end}/
  regex = /#{source_mapping_regex}(?:#{rest})*\Z/m
  match = regex.match(source)
  match && match[1]
end
harmony?() click to toggle source
# File lib/uglifier.rb, line 218
def harmony?
  @options[:harmony]
end
ie8?() click to toggle source
# File lib/uglifier.rb, line 358
def ie8?
  @options.fetch(:ie8, DEFAULTS[:ie8])
end
input_source_map(source, generate_map) click to toggle source
# File lib/uglifier.rb, line 450
def input_source_map(source, generate_map)
  return nil unless generate_map
  source_map_options = @options[:source_map].is_a?(Hash) ? @options[:source_map] : {}
  sanitize_map_root(source_map_options.fetch(:input_source_map) do
    url = extract_source_mapping_url(source)
    Base64.strict_decode64(url.split(",", 2)[-1]) if url && url.start_with?("data:")
  end)
rescue ArgumentError, JSON::ParserError
  nil
end
keep_fnames?(type) click to toggle source
# File lib/uglifier.rb, line 362
def keep_fnames?(type)
  if @options[:keep_fnames] || DEFAULTS[:keep_fnames]
    true
  else
    @options[type].respond_to?(:[]) && @options[type][:keep_fnames] ||
      DEFAULTS[type].respond_to?(:[]) && DEFAULTS[type][:keep_fnames]
  end
end
mangle_options() click to toggle source
# File lib/uglifier.rb, line 250
def mangle_options
  defaults = conditional_option(
    DEFAULTS[:mangle],
    :keep_fnames => keep_fnames?(:mangle)
  )

  conditional_option(
    @options[:mangle],
    defaults,
    :properties => mangle_properties_options
  )
end
mangle_properties_options() click to toggle source
# File lib/uglifier.rb, line 263
def mangle_properties_options
  mangle_options = conditional_option(@options[:mangle], DEFAULTS[:mangle])

  mangle_properties_options =
    if @options.has_key?(:mangle_properties)
      @options[:mangle_properties]
    else
      mangle_options && mangle_options[:properties]
    end

  options = conditional_option(mangle_properties_options, MANGLE_PROPERTIES_DEFAULTS)

  if options && options[:regex]
    options.merge(:regex => encode_regexp(options[:regex]))
  else
    options
  end
end
migrate_braces(options) click to toggle source
# File lib/uglifier.rb, line 350
def migrate_braces(options)
  if harmony?
    options
  else
    options.merge(:braces => options[:bracketize]).delete_if { |key| key == :bracketize }
  end
end
negate_iife_block() click to toggle source

Prevent negate_iife when wrap_iife is true

# File lib/uglifier.rb, line 296
def negate_iife_block
  if output_options[:wrap_iife]
    { :negate_iife => false }
  else
    {}
  end
end
output_options() click to toggle source
# File lib/uglifier.rb, line 345
def output_options
  migrate_braces(DEFAULTS[:output].merge(@options[:output] || {}))
    .merge(:comments => comment_options, :quote_style => quote_style)
end
parse_options() click to toggle source
# File lib/uglifier.rb, line 385
def parse_options
  conditional_option(@options[:parse], DEFAULTS[:parse])
    .merge(parse_source_map_options)
end
parse_result(result, generate_map) click to toggle source
# File lib/uglifier.rb, line 232
def parse_result(result, generate_map)
  raise Error, error_message(result) if result.has_key?('error')

  if generate_map
    [result['code'] + source_map_comments, result['map']]
  else
    result['code'] + source_map_comments
  end
end
parse_source_map_options() click to toggle source
# File lib/uglifier.rb, line 390
def parse_source_map_options
  if @options[:source_map].respond_to?(:[])
    { :filename => @options[:source_map][:filename] }
  else
    {}
  end
end
quote_style() click to toggle source
# File lib/uglifier.rb, line 319
def quote_style
  option = conditional_option(@options[:output], DEFAULTS[:output])[:quote_style]
  case option
  when :single
    1
  when :double
    2
  when :original
    3
  when Numeric
    option
  else # auto
    0
  end
end
read_source(source) click to toggle source
# File lib/uglifier.rb, line 242
def read_source(source)
  if source.respond_to?(:read)
    source.read
  else
    source.to_s
  end
end
run_uglifyjs(input, generate_map) click to toggle source

Run UglifyJS for given source code

# File lib/uglifier.rb, line 202
def run_uglifyjs(input, generate_map)
  source = read_source(input)
  input_map = input_source_map(source, generate_map)
  options = {
    :source => source,
    :output => output_options,
    :compress => compressor_options,
    :mangle => mangle_options,
    :parse => parse_options,
    :sourceMap => source_map_options(input_map),
    :ie8 => ie8?
  }

  parse_result(@context.call("uglifier", options), generate_map)
end
sanitize_map_root(map) click to toggle source
# File lib/uglifier.rb, line 428
def sanitize_map_root(map)
  if map.nil?
    nil
  elsif map.is_a? String
    sanitize_map_root(JSON.parse(map))
  elsif map["sourceRoot"] == ""
    map.merge("sourceRoot" => nil)
  else
    map
  end
end
source_map_comments() click to toggle source
# File lib/uglifier.rb, line 182
def source_map_comments
  return '' unless @options[:source_map].respond_to?(:[])

  suffix = ''
  if @options[:source_map][:map_url]
    suffix += "\n//# sourceMappingURL=" + @options[:source_map][:map_url]
  end

  suffix += "\n//# sourceURL=" + @options[:source_map][:url] if @options[:source_map][:url]
  suffix
end
source_map_options(input_map) click to toggle source
# File lib/uglifier.rb, line 371
def source_map_options(input_map)
  options = conditional_option(@options[:source_map], SOURCE_MAP_DEFAULTS) || SOURCE_MAP_DEFAULTS

  {
    :input => options[:filename],
    :filename => options[:output_filename],
    :root => options.fetch(:root) { input_map ? input_map["sourceRoot"] : nil },
    :content => input_map,
    #:map_url => options[:map_url],
    :url => options[:url],
    :includeSources => options[:sources_content]
  }
end
source_with(path) click to toggle source
# File lib/uglifier.rb, line 194
def source_with(path)
  [ES5FallbackPath, SplitFallbackPath, SourceMapPath, path,
   UglifyJSWrapperPath].map do |file|
    File.open(file, "r:UTF-8", &:read)
  end.join("\n")
end