lib/uglifier.rb in uglifier-3.2.0 vs lib/uglifier.rb in uglifier-4.0.0
- old
+ new
@@ -6,11 +6,11 @@
require "uglifier/version"
# A wrapper around the UglifyJS interface
class Uglifier
# Error class for compilation errors.
- Error = ExecJS::Error
+ class Error < StandardError; end
# UglifyJS source path
SourcePath = File.expand_path("../uglify.js", __FILE__)
# UglifyJS with Harmony source path
HarmonySourcePath = File.expand_path("../uglify-harmony.js", __FILE__)
@@ -36,31 +36,31 @@
:semicolons => true, # Separate statements with semicolons
:preserve_line => false, # Preserve line numbers in outputs
:beautify => false, # Beautify output
:indent_level => 4, # Indent level in spaces
:indent_start => 0, # Starting indent level
- :space_colon => false, # Insert space before colons (only with beautifier)
:width => 80, # Specify line width when beautifier is used (only with beautifier)
:preamble => nil, # Preamble for the generated JS file. Can be used to insert any code or comment.
- :wrap_iife => false # Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option.
+ :wrap_iife => false, # Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option.
+ :shebang => true, # Preserve shebang (#!) in preamble (shell scripts)
+ :quote_style => 0, # Quote style, possible values :auto (default), :single, :double, :original
+ :keep_quoted_props => false # Keep quotes property names
},
:mangle => {
:eval => false, # Mangle names when eval of when is used in scope
- :except => ["$super"], # Argument names to be excluded from mangling
- :sort => false, # Assign shorter names to most frequently used variables. Often results in bigger output after gzip.
- :toplevel => false, # Mangle names declared in the toplevel scope
+ :reserved => ["$super"], # Argument names to be excluded from mangling
:properties => false, # Mangle property names
- :keep_fnames => false # Do not modify function names
+ :toplevel => false, # Mangle names declared in the toplevel scope
}, # Mangle variable and function names, set to false to skip mangling
- :mangle_properties => false, # Mangle property names
:compress => {
:sequences => true, # Allow statements to be joined by commas
:properties => true, # Rewrite property access using the dot notation
:dead_code => true, # Remove unreachable code
:drop_debugger => true, # Remove debugger; statements
:unsafe => false, # Apply "unsafe" transformations
:unsafe_comps => false, # Reverse < and <= to > and >= to allow improved compression. This might be unsafe when an at least one of two operands is an object with computed values due the use of methods like get, or valueOf. This could cause change in execution order after operands in the comparison are switching. Compression only works if both comparisons and unsafe_comps are both set to true.
+ :unsafe_math => false, # Optimize numerical expressions like 2 * x * 3 into 6 * x, which may give imprecise floating point results.
:unsafe_proto => false, # Optimize expressions like Array.prototype.slice.call(a) into [].slice.call(a)
:conditionals => true, # Optimize for if-s and conditional expressions
:comparisons => true, # Apply binary node optimizations for comparisons
:evaluate => true, # Attempt to evaluate constant expressions
:booleans => true, # Various optimizations to boolean contexts
@@ -72,34 +72,46 @@
:hoist_vars => false, # Hoist var declarations
:if_return => true, # Optimizations for if/return and if/continue
:join_vars => true, # Join consecutive var statements
:cascade => true, # Cascade sequences
:collapse_vars => true, # Collapse single-use var and const definitions when possible.
+ :reduce_funcs => false, # Inline single-use functions as function expressions. Depends on reduce_vars.
:reduce_vars => false, # Collapse variables assigned with and used as constant values.
:negate_iife => true, # Negate immediately invoked function expressions to avoid extra parens
:pure_getters => false, # Assume that object property access does not have any side-effects
:pure_funcs => nil, # List of functions without side-effects. Can safely discard function calls when the result value is not used
:drop_console => false, # Drop calls to console.* functions
- :angular => false, # Process @ngInject annotations
:keep_fargs => false, # Preserve unused function arguments
:keep_fnames => false, # Do not drop names in function definitions
- :passes => 1 # Number of times to run compress. Raising the number of passes will increase compress time, but can produce slightly smaller code.
+ :passes => 1, # Number of times to run compress. Raising the number of passes will increase compress time, but can produce slightly smaller code.
+ :keep_infinity => false, # Prevent compression of Infinity to 1/0
+ :side_effects => true, # Pass false to disable potentially dropping functions marked as "pure" using pure comment annotation. See UglifyJS documentation for details.
+ :switches => true, # de-duplicate and remove unreachable switch branches
}, # Apply transformations to code, set to false to skip
+ :parse => {
+ :bare_returns => false, # Allow top-level return statements.
+ :expression => false, # Parse a single expression, rather than a program (for parsing JSON).
+ :html5_comments => true, # Ignore HTML5 comments in input
+ :shebang => true, # support #!command as the first line
+ :strict => false
+ },
:define => {}, # Define values for symbol replacement
- :enclose => false, # Enclose in output function wrapper, define replacements as key-value pairs
:keep_fnames => false, # Generate code safe for the poor souls relying on Function.prototype.name at run-time. Sets both compress and mangle keep_fanems to true.
- :screw_ie8 => false, # Don't bother to generate safe code for IE8
+ :toplevel => false,
+ :ie8 => true, # Generate safe code for IE8
:source_map => false, # Generate source map
:harmony => false # Enable ES6/Harmony mode (experimental). Disabling mangling and compressing is recommended with Harmony mode.
}
- LEGACY_OPTIONS = [:comments, :squeeze, :copyright, :mangle]
+ EXTRA_OPTIONS = [:comments, :mangle_properties]
MANGLE_PROPERTIES_DEFAULTS = {
+ :debug => false, # Add debug prefix and suffix to mangled properties
:regex => nil, # A regular expression to filter property names to be mangled
- :ignore_quoted => false, # Only mangle unquoted property names
- :debug => false # Mangle names with the original name still present
+ :keep_quoted => false, # Keep quoted property names
+ :reserved => [], # List of properties that should not be mangled
+ :builtins => false, # Mangle properties that overlap with standard JS globals
}
SOURCE_MAP_DEFAULTS = {
:map_url => false, # Url for source mapping to be appended in minified source
:url => false, # Url for original source to be appended in minified source
@@ -132,16 +144,16 @@
# Initialize new context for Uglifier with given options
#
# @param options [Hash] optional overrides to +Uglifier::DEFAULTS+
def initialize(options = {})
- (options.keys - DEFAULTS.keys - LEGACY_OPTIONS)[0..1].each do |missing|
+ (options.keys - DEFAULTS.keys - EXTRA_OPTIONS)[0..1].each do |missing|
raise ArgumentError, "Invalid option: #{missing}"
end
@options = options
- source = @options[:harmony] ? source_with(HarmonySourcePath) : source_with(SourcePath)
+ source = harmony? ? source_with(HarmonySourcePath) : source_with(SourcePath)
@context = ExecJS.compile(source)
end
# Minifies JavaScript code
#
@@ -167,10 +179,24 @@
run_uglifyjs(source, true)
end
private
+ 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
+
+ if @options[:source_map][:url]
+ suffix += "\n//# sourceURL=" + @options[:source_map][:url]
+ end
+ suffix
+ end
+
def source_with(path)
[ES5FallbackPath, SplitFallbackPath, SourceMapPath, path,
UglifyJSWrapperPath].map do |file|
File.open(file, "r:UTF-8", &:read)
end.join("\n")
@@ -183,20 +209,42 @@
options = {
:source => source,
:output => output_options,
:compress => compressor_options,
:mangle => mangle_options,
- :mangle_properties => mangle_properties_options,
- :parse_options => parse_options,
- :source_map_options => source_map_options(input_map),
- :generate_map => generate_map,
- :enclose => enclose_options
+ :parse => parse_options,
+ :sourceMap => source_map_options(input_map),
+ :ie8 => ie8?
}
- @context.call("uglifier", options)
+ parse_result(@context.call("uglifier", options), generate_map)
end
+ def harmony?
+ @options[:harmony]
+ end
+
+ 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
+
+ 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
+
def read_source(source)
if source.respond_to?(:read)
source.read
else
source.to_s
@@ -208,35 +256,43 @@
DEFAULTS[:mangle],
:keep_fnames => keep_fnames?(:mangle)
)
conditional_option(
- @options.fetch(:mangle, DEFAULTS[:mangle]),
+ @options[:mangle],
defaults,
- :keep_fnames => keep_fnames?(:mangle)
+ :properties => mangle_properties_options
)
end
def mangle_properties_options
- mangle_options = @options.fetch(:mangle_properties, DEFAULTS[:mangle_properties])
- options = conditional_option(mangle_options, MANGLE_PROPERTIES_DEFAULTS)
+ 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
def compressor_options
defaults = conditional_option(
DEFAULTS[:compress],
- :global_defs => @options[:define] || {},
- :screw_ie8 => screw_ie8?
+ :global_defs => @options[:define] || {}
)
conditional_option(
- @options[:compress] || @options[:squeeze],
+ @options[:compress],
defaults,
{ :keep_fnames => keep_fnames?(:compress) }.merge(negate_iife_block)
)
end
@@ -262,35 +318,45 @@
else
false
end
end
+ 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
+
def comment_setting
if @options.has_key?(:output) && @options[:output].has_key?(:comments)
@options[:output][:comments]
elsif @options.has_key?(:comments)
@options[:comments]
- elsif @options[:copyright] == false
- :none
else
DEFAULTS[:output][:comments]
end
end
def output_options
DEFAULTS[:output].merge(@options[:output] || {}).merge(
:comments => comment_options,
- :screw_ie8 => screw_ie8?
- ).reject { |key, _| key == :ie_proof }
+ :quote_style => quote_style
+ )
end
- def screw_ie8?
- if (@options[:output] || {}).has_key?(:ie_proof)
- !@options[:output][:ie_proof]
- else
- @options.fetch(:screw_ie8, DEFAULTS[:screw_ie8])
- end
+ def ie8?
+ @options.fetch(:ie8, DEFAULTS[:ie8])
end
def keep_fnames?(type)
if @options[:keep_fnames] || DEFAULTS[:keep_fnames]
true
@@ -302,19 +368,25 @@
def source_map_options(input_map)
options = conditional_option(@options[:source_map], SOURCE_MAP_DEFAULTS) || SOURCE_MAP_DEFAULTS
{
- :file => options[:output_filename],
+ :input => options[:filename],
+ :filename => options[:output_filename],
:root => options.fetch(:root) { input_map ? input_map["sourceRoot"] : nil },
- :orig => input_map,
- :map_url => options[:map_url],
+ :content => input_map,
+ #:map_url => options[:map_url],
:url => options[:url],
- :sources_content => options[:sources_content]
+ :includeSources => options[:sources_content]
}
end
def parse_options
+ conditional_option(@options[:parse], DEFAULTS[:parse])
+ .merge(parse_source_map_options)
+ end
+
+ def parse_source_map_options
if @options[:source_map].respond_to?(:[])
{ :filename => @options[:source_map][:filename] }
else
{}
end