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
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 132 def self.compile(source, options = {}) new(options).compile(source) end
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 141 def self.compile_with_map(source, options = {}) new(options).compile_with_map(source) end
Initialize new context for Uglifier
with given options
@param options [Hash] optional overrides to Uglifier::DEFAULTS
# File lib/uglifier.rb, line 148 def initialize(options = {}) (options.keys - DEFAULTS.keys - EXTRA_OPTIONS)[0..1].each do |missing| raise ArgumentError, "Invalid option: #{missing}" end @options = options end
Public Instance Methods
Minifies JavaScript code
@param source [IO, String] valid JS source code. @return [String] minified code.
# File lib/uglifier.rb, line 159 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
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 175 def compile_with_map(source) run_uglifyjs(source, true) end
Private Instance Methods
# File lib/uglifier.rb, line 362 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
# File lib/uglifier.rb, line 393 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
# File lib/uglifier.rb, line 340 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
# File lib/uglifier.rb, line 476 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
# File lib/uglifier.rb, line 181 def context @context ||= begin source = harmony? ? source_with(HarmonySourcePath) : source_with(SourcePath) ExecJS.compile(source) end end
# File lib/uglifier.rb, line 269 def context_lines_message(source, line_number, column) return if line_number.nil? line_index = line_number - 1 lines = source.split("\n") first_line = [line_index - error_context_lines, 0].max last_line = [line_number + error_context_lines, lines.size].min options = error_context_format_options(first_line, last_line, line_index, column) context_lines = lines[first_line...last_line] "--\n#{format_lines(context_lines, options).join("\n")}\n==" end
# File lib/uglifier.rb, line 456 def enclose_options if @options[:enclose] @options[:enclose].map do |pair| pair.first + ':' + pair.last end else false end end
# File lib/uglifier.rb, line 466 def encode_regexp(regexp) modifiers = if regexp.casefold? "i" else "" end [regexp.source, modifiers] end
# File lib/uglifier.rb, line 241 def error_context_format_options(low, high, line_index, column) line_width = high.to_s.size { :line_index => line_index, :base_index => low, :line_width => line_width, :line_format => "\e[36m%#{line_width + 1}d\e[0m ", # cyan :col => column } end
# File lib/uglifier.rb, line 237 def error_context_lines @options.fetch(:error_context_lines, DEFAULTS[:error_context_lines]).to_i end
# File lib/uglifier.rb, line 283 def error_message(result, options) err = result['error'] harmony_msg = harmony? ? '' : harmony_error_message(err['message'].to_s) src_ctx = context_lines_message(options[:source], err['line'], err['col']) "#{err['message']}#{harmony_msg}\n#{src_ctx}" end
# File lib/uglifier.rb, line 498 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
# File lib/uglifier.rb, line 252 def format_error_line(line, options) # light red indicator = ' => '.rjust(options[:line_width] + 2) colored_line = "#{line[0...options[:col]]}\e[91m#{line[options[:col]..-1]}" "\e[91m#{indicator}\e[0m#{colored_line}\e[0m" end
# File lib/uglifier.rb, line 259 def format_lines(lines, options) lines.map.with_index do |line, index| if options[:base_index] + index == options[:line_index] format_error_line(line, options) else "#{options[:line_format] % (options[:base_index] + index + 1)}#{line}" end end end
# File lib/uglifier.rb, line 224 def harmony? @options[:harmony] end
# File lib/uglifier.rb, line 228 def harmony_error_message(message) if message.start_with?("Unexpected token") ". To use ES6 syntax, harmony mode must be enabled with " \ "Uglifier.new(:harmony => true)." else "" end end
# File lib/uglifier.rb, line 416 def ie8? @options.fetch(:ie8, DEFAULTS[:ie8]) end
# File lib/uglifier.rb, line 508 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
# File lib/uglifier.rb, line 420 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
# File lib/uglifier.rb, line 308 def mangle_options defaults = conditional_option( DEFAULTS[:mangle], :keep_fnames => keep_fnames?(:mangle) ) conditional_option( @options[:mangle], defaults, :properties => mangle_properties_options ) end
# File lib/uglifier.rb, line 321 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
# File lib/uglifier.rb, line 408 def migrate_braces(options) if harmony? options else options.merge(:braces => options[:bracketize]).delete_if { |key| key == :bracketize } end end
Prevent negate_iife when wrap_iife is true
# File lib/uglifier.rb, line 354 def negate_iife_block if output_options[:wrap_iife] { :negate_iife => false } else {} end end
# File lib/uglifier.rb, line 403 def output_options migrate_braces(DEFAULTS[:output].merge(@options[:output] || {})) .merge(:comments => comment_options, :quote_style => quote_style) end
# File lib/uglifier.rb, line 443 def parse_options conditional_option(@options[:parse], DEFAULTS[:parse]) .merge(parse_source_map_options) end
# File lib/uglifier.rb, line 290 def parse_result(result, generate_map, options) raise Error, error_message(result, options) if result.has_key?('error') if generate_map [result['code'] + source_map_comments, result['map']] else result['code'] + source_map_comments end end
# File lib/uglifier.rb, line 448 def parse_source_map_options if @options[:source_map].respond_to?(:[]) { :filename => @options[:source_map][:filename] } else {} end end
# File lib/uglifier.rb, line 377 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
# File lib/uglifier.rb, line 300 def read_source(source) if source.respond_to?(:read) source.read else source.to_s end end
Run UglifyJS for given source code
# File lib/uglifier.rb, line 208 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, options) end
# File lib/uglifier.rb, line 486 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
# File lib/uglifier.rb, line 188 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
# File lib/uglifier.rb, line 429 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
# File lib/uglifier.rb, line 200 def source_with(path) [ES5FallbackPath, SplitFallbackPath, SourceMapPath, path, UglifyJSWrapperPath].map do |file| File.open(file, "r:UTF-8", &:read) end.join("\n") end