uglifier 2.7.1 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
1
  class Uglifier
2
2
  # Current version of Uglifier.
3
- VERSION = "2.7.1"
3
+ VERSION = "3.0.1"
4
4
  end
data/lib/uglifier.js ADDED
@@ -0,0 +1,114 @@
1
+ // Set source-map.js sourceMap to uglify.js MOZ_SourceMap
2
+ MOZ_SourceMap = sourceMap;
3
+
4
+ function comments(option) {
5
+ if (Object.prototype.toString.call(option) === '[object Array]') {
6
+ return new RegExp(option[0], option[1]);
7
+ } else if (option == "jsdoc") {
8
+ return function(node, comment) {
9
+ if (comment.type == "comment2") {
10
+ return /@preserve|@license|@cc_on/i.test(comment.value);
11
+ } else {
12
+ return false;
13
+ }
14
+ };
15
+ } else {
16
+ return option;
17
+ }
18
+ }
19
+
20
+ function readNameCache(key) {
21
+ return UglifyJS.readNameCache(null, key);
22
+ }
23
+
24
+ function writeNameCache(key, cache) {
25
+ return UglifyJS.writeNameCache(null, key, cache);
26
+ }
27
+
28
+ function regexOption(options) {
29
+ if (typeof options === 'object' && options.regex) {
30
+ return new RegExp(options.regex[0], options.regex[1]);
31
+ } else {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ function parse(source, options) {
37
+ var ast = UglifyJS.parse(source, options.parse_options);
38
+ ast.figure_out_scope();
39
+
40
+ if (options.compress) {
41
+ var compressor = UglifyJS.Compressor(options.compress);
42
+ ast = ast.transform(compressor);
43
+ ast.figure_out_scope();
44
+ }
45
+
46
+ if (options.mangle) {
47
+ ast.compute_char_frequency();
48
+ ast.mangle_names(options.mangle);
49
+ }
50
+
51
+ if (options.mangle_properties) {
52
+ var regex = regexOption(options.mangle_properties);
53
+ UglifyJS.mangle_properties(ast, {
54
+ reserved: [],
55
+ only_cache: false,
56
+ regex: regex
57
+ });
58
+ }
59
+
60
+ if (options.enclose) {
61
+ ast = ast.wrap_enclose(options.enclose);
62
+ }
63
+ return ast;
64
+ }
65
+
66
+ function copySourcesContent(sourceMap, options) {
67
+ sourceMap.get().setSourceContent(options.parse_options.filename, options.source);
68
+
69
+ var original = options.source_map_options.orig;
70
+
71
+ if (original && original.sources && original.sourcesContent) {
72
+ for(var i = 0; i < original.sources.length; i++) {
73
+ sourceMap.get().setSourceContent(original.sources[i], original.sourcesContent[i]);
74
+ }
75
+ }
76
+ }
77
+
78
+ function uglifier(options) {
79
+ var source = options.source;
80
+ var ast = parse(source, options);
81
+ var source_map;
82
+
83
+ var gen_code_options = options.output;
84
+ gen_code_options.comments = comments(options.output.comments);
85
+
86
+ if (options.generate_map) {
87
+ source_map = UglifyJS.SourceMap(options.source_map_options);
88
+ gen_code_options.source_map = source_map;
89
+
90
+ if (options.source_map_options.sources_content) {
91
+ copySourcesContent(source_map, options);
92
+ }
93
+ }
94
+
95
+ var stream = UglifyJS.OutputStream(gen_code_options);
96
+ ast.print(stream);
97
+
98
+ if (options.source_map_options.map_url) {
99
+ stream += "\n//# sourceMappingURL=" + options.source_map_options.map_url;
100
+ }
101
+
102
+ if (options.source_map_options.url) {
103
+ stream += "\n//# sourceURL=" + options.source_map_options.url;
104
+ }
105
+
106
+ if (options.generate_map) {
107
+ if (options.source_map_options.sources_content) {
108
+ source_map.get().setSourceContent(options.parse_options.filename, options.source);
109
+ }
110
+ return [stream.toString(), source_map.toString()];
111
+ } else {
112
+ return stream.toString();
113
+ }
114
+ }
data/lib/uglifier.rb CHANGED
@@ -1,85 +1,25 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require "execjs"
4
3
  require "json"
4
+ require "base64"
5
+ require "execjs"
5
6
  require "uglifier/version"
6
7
 
7
8
  # A wrapper around the UglifyJS interface
8
9
  class Uglifier
9
10
  # Error class for compilation errors.
10
11
  Error = ExecJS::Error
11
- # JavaScript code to call UglifyJS
12
- JS = <<-JS
13
- (function(options) {
14
- function comments(option) {
15
- if (Object.prototype.toString.call(option) === '[object Array]') {
16
- return new RegExp(option[0], option[1]);
17
- } else if (option == "jsdoc") {
18
- return function(node, comment) {
19
- if (comment.type == "comment2") {
20
- return /@preserve|@license|@cc_on/i.test(comment.value);
21
- } else {
22
- return false;
23
- }
24
- }
25
- } else {
26
- return option;
27
- }
28
- }
29
-
30
- var source = options.source;
31
- var ast = UglifyJS.parse(source, options.parse_options);
32
- ast.figure_out_scope();
33
-
34
- if (options.compress) {
35
- var compressor = UglifyJS.Compressor(options.compress);
36
- ast = ast.transform(compressor);
37
- ast.figure_out_scope();
38
- }
39
-
40
- if (options.mangle) {
41
- ast.compute_char_frequency();
42
- ast.mangle_names(options.mangle);
43
- }
44
-
45
- if (options.enclose) {
46
- ast = ast.wrap_enclose(options.enclose);
47
- }
48
-
49
- var gen_code_options = options.output;
50
- gen_code_options.comments = comments(options.output.comments);
51
-
52
- if (options.generate_map) {
53
- var source_map = UglifyJS.SourceMap(options.source_map_options);
54
- gen_code_options.source_map = source_map;
55
- }
56
-
57
- var stream = UglifyJS.OutputStream(gen_code_options);
58
-
59
- ast.print(stream);
60
-
61
- if (options.source_map_options.map_url) {
62
- stream += "\\n//# sourceMappingURL=" + options.source_map_options.map_url;
63
- }
64
-
65
- if (options.source_map_options.url) {
66
- stream += "\\n//# sourceURL=" + options.source_map_options.url;
67
- }
68
-
69
- if (options.generate_map) {
70
- return [stream.toString(), source_map.toString()];
71
- } else {
72
- return stream.toString();
73
- }
74
- })
75
- JS
76
12
 
77
13
  # UglifyJS source path
78
14
  SourcePath = File.expand_path("../uglify.js", __FILE__)
15
+ # Source Map path
16
+ SourceMapPath = File.expand_path("../source-map.js", __FILE__)
79
17
  # ES5 shims source path
80
18
  ES5FallbackPath = File.expand_path("../es5.js", __FILE__)
81
19
  # String.split shim source path
82
20
  SplitFallbackPath = File.expand_path("../split.js", __FILE__)
21
+ # UglifyJS wrapper path
22
+ UglifyJSWrapperPath = File.expand_path("../uglifier.js", __FILE__)
83
23
 
84
24
  # Default options for compilation
85
25
  DEFAULTS = {
@@ -104,8 +44,10 @@ class Uglifier
104
44
  :eval => false, # Mangle names when eval of when is used in scope
105
45
  :except => ["$super"], # Argument names to be excluded from mangling
106
46
  :sort => false, # Assign shorter names to most frequently used variables. Often results in bigger output after gzip.
107
- :toplevel => false # Mangle names declared in the toplevel scope
47
+ :toplevel => false, # Mangle names declared in the toplevel scope
48
+ :properties => false # Mangle property names
108
49
  }, # Mangle variable and function names, set to false to skip mangling
50
+ :mangle_properties => false, # Mangle property names
109
51
  :compress => {
110
52
  :sequences => true, # Allow statements to be joined by commas
111
53
  :properties => true, # Rewrite property access using the dot notation
@@ -123,23 +65,37 @@ class Uglifier
123
65
  :if_return => true, # Optimizations for if/return and if/continue
124
66
  :join_vars => true, # Join consecutive var statements
125
67
  :cascade => true, # Cascade sequences
68
+ :collapse_vars => false, # Collapse single-use var and const definitions when possible.
126
69
  :negate_iife => true, # Negate immediately invoked function expressions to avoid extra parens
127
70
  :pure_getters => false, # Assume that object property access does not have any side-effects
128
71
  :pure_funcs => nil, # List of functions without side-effects. Can safely discard function calls when the result value is not used
129
72
  :drop_console => false, # Drop calls to console.* functions
130
73
  :angular => false, # Process @ngInject annotations
131
- :keep_fargs => false # Preserve unused function arguments
74
+ :keep_fargs => false, # Preserve unused function arguments
75
+ :keep_fnames => false # Preserve function names
132
76
  }, # Apply transformations to code, set to false to skip
133
77
  :define => {}, # Define values for symbol replacement
134
78
  :enclose => false, # Enclose in output function wrapper, define replacements as key-value pairs
135
- :source_filename => nil, # The filename of the input file
136
- :source_root => nil, # The URL of the directory which contains :source_filename
137
- :output_filename => nil, # The filename or URL where the minified output can be found
138
- :input_source_map => nil, # The contents of the source map describing the input
139
79
  :screw_ie8 => false, # Don't bother to generate safe code for IE8
140
- :source_map_url => false, # Url for source mapping to be appended in minified source
141
- :source_url => false # Url for original source to be appended in minified source
80
+ :source_map => false # Generate source map
81
+ }
82
+
83
+ LEGACY_OPTIONS = [:comments, :squeeze, :copyright, :mangle]
84
+
85
+ MANGLE_PROPERTIES_DEFAULTS = {
86
+ :regex => nil # A regular expression to filter property names to be mangled
142
87
  }
88
+
89
+ SOURCE_MAP_DEFAULTS = {
90
+ :map_url => false, # Url for source mapping to be appended in minified source
91
+ :url => false, # Url for original source to be appended in minified source
92
+ :sources_content => false, # Include original source content in map
93
+ :filename => nil, # The filename of the input file
94
+ :root => nil, # The URL of the directory which contains :filename
95
+ :output_filename => nil, # The filename or URL where the minified output can be found
96
+ :input_source_map => nil # The contents of the source map describing the input
97
+ }
98
+
143
99
  # rubocop:enable LineLength
144
100
 
145
101
  # Minifies JavaScript code using implicit context.
@@ -164,7 +120,7 @@ class Uglifier
164
120
  #
165
121
  # @param options [Hash] optional overrides to +Uglifier::DEFAULTS+
166
122
  def initialize(options = {})
167
- (options.keys - DEFAULTS.keys - [:comments, :squeeze, :copyright])[0..1].each do |missing|
123
+ (options.keys - DEFAULTS.keys - LEGACY_OPTIONS)[0..1].each do |missing|
168
124
  raise ArgumentError, "Invalid option: #{missing}"
169
125
  end
170
126
  @options = options
@@ -176,7 +132,14 @@ class Uglifier
176
132
  # @param source [IO, String] valid JS source code.
177
133
  # @return [String] minified code.
178
134
  def compile(source)
179
- run_uglifyjs(source, false)
135
+ if @options[:source_map]
136
+ compiled, source_map = run_uglifyjs(source, true)
137
+ source_map_uri = Base64.strict_encode64(source_map)
138
+ source_map_mime = "application/json;charset=utf-8;base64"
139
+ compiled + "\n//# sourceMappingURL=data:#{source_map_mime},#{source_map_uri}"
140
+ else
141
+ run_uglifyjs(source, false)
142
+ end
180
143
  end
181
144
  alias_method :compress, :compile
182
145
 
@@ -191,25 +154,29 @@ class Uglifier
191
154
  private
192
155
 
193
156
  def uglifyjs_source
194
- [ES5FallbackPath, SplitFallbackPath, SourcePath].map do |file|
195
- File.open(file, "r:UTF-8") { |f| f.read }
157
+ [ES5FallbackPath, SplitFallbackPath, SourceMapPath, SourcePath,
158
+ UglifyJSWrapperPath].map do |file|
159
+ File.open(file, "r:UTF-8", &:read)
196
160
  end.join("\n")
197
161
  end
198
162
 
199
163
  # Run UglifyJS for given source code
200
- def run_uglifyjs(source, generate_map)
164
+ def run_uglifyjs(input, generate_map)
165
+ source = read_source(input)
166
+ input_map = input_source_map(source, generate_map)
201
167
  options = {
202
- :source => read_source(source),
168
+ :source => source,
203
169
  :output => output_options,
204
170
  :compress => compressor_options,
205
171
  :mangle => mangle_options,
172
+ :mangle_properties => mangle_properties_options,
206
173
  :parse_options => parse_options,
207
- :source_map_options => source_map_options,
174
+ :source_map_options => source_map_options(input_map),
208
175
  :generate_map => generate_map,
209
176
  :enclose => enclose_options
210
177
  }
211
178
 
212
- @context.call(Uglifier::JS, options)
179
+ @context.call("uglifier", options)
213
180
  end
214
181
 
215
182
  def read_source(source)
@@ -221,14 +188,25 @@ class Uglifier
221
188
  end
222
189
 
223
190
  def mangle_options
224
- conditional_option(@options[:mangle], DEFAULTS[:mangle])
191
+ mangle_options = @options.fetch(:mangle, @options[:mangle])
192
+ conditional_option(mangle_options, DEFAULTS[:mangle])
193
+ end
194
+
195
+ def mangle_properties_options
196
+ mangle_options = @options.fetch(:mangle_properties, DEFAULTS[:mangle_properties])
197
+ options = conditional_option(mangle_options, MANGLE_PROPERTIES_DEFAULTS)
198
+ if options && options[:regex]
199
+ options.merge(:regex => encode_regexp(options[:regex]))
200
+ else
201
+ options
202
+ end
225
203
  end
226
204
 
227
205
  def compressor_options
228
206
  defaults = conditional_option(
229
207
  DEFAULTS[:compress],
230
208
  :global_defs => @options[:define] || {},
231
- :screw_ie8 => @options[:screw_ie8] || DEFAULTS[:screw_ie8]
209
+ :screw_ie8 => screw_ie8?
232
210
  )
233
211
  conditional_option(@options[:compress] || @options[:squeeze], defaults)
234
212
  end
@@ -269,24 +247,31 @@ class Uglifier
269
247
 
270
248
  def screw_ie8?
271
249
  if (@options[:output] || {}).has_key?(:ie_proof)
272
- false
250
+ !@options[:output][:ie_proof]
273
251
  else
274
- @options[:screw_ie8] || DEFAULTS[:screw_ie8]
252
+ @options.fetch(:screw_ie8, DEFAULTS[:screw_ie8])
275
253
  end
276
254
  end
277
255
 
278
- def source_map_options
256
+ def source_map_options(input_map)
257
+ options = conditional_option(@options[:source_map], SOURCE_MAP_DEFAULTS)
258
+
279
259
  {
280
- :file => @options[:output_filename],
281
- :root => @options[:source_root],
282
- :orig => @options[:input_source_map],
283
- :map_url => @options[:source_map_url],
284
- :url => @options[:source_url]
260
+ :file => options[:output_filename],
261
+ :root => options.fetch(:root) { input_map ? input_map["sourceRoot"] : nil },
262
+ :orig => input_map,
263
+ :map_url => options[:map_url],
264
+ :url => options[:url],
265
+ :sources_content => options[:sources_content]
285
266
  }
286
267
  end
287
268
 
288
269
  def parse_options
289
- { :filename => @options[:source_filename] }
270
+ if @options[:source_map].respond_to?(:[])
271
+ { :filename => @options[:source_map][:filename] }
272
+ else
273
+ {}
274
+ end
290
275
  end
291
276
 
292
277
  def enclose_options
@@ -318,4 +303,38 @@ class Uglifier
318
303
  false
319
304
  end
320
305
  end
306
+
307
+ def sanitize_map_root(map)
308
+ if map.nil?
309
+ nil
310
+ elsif map.is_a? String
311
+ sanitize_map_root(JSON.load(map))
312
+ elsif map["sourceRoot"] == ""
313
+ map.merge("sourceRoot" => nil)
314
+ else
315
+ map
316
+ end
317
+ end
318
+
319
+ def extract_source_mapping_url(source)
320
+ comment_start = %r{(?://|/\*\s*)}
321
+ comment_end = %r{\s*(?:\r?\n?\*/|$)?}
322
+ source_mapping_regex = /#{comment_start}[@#]\ssourceMappingURL=\s*(\S*?)#{comment_end}/
323
+ rest = /\s#{comment_start}[@#]\s[a-zA-Z]+=\s*(?:\S*?)#{comment_end}/
324
+ regex = /#{source_mapping_regex}(?:#{rest})*\Z/m
325
+ match = regex.match(source)
326
+ match && match[1]
327
+ end
328
+
329
+ def input_source_map(source, generate_map)
330
+ return nil unless generate_map
331
+ sanitize_map_root(@options.fetch(:source_map, {}).fetch(:input_source_map) do
332
+ url = extract_source_mapping_url(source)
333
+ if url && url.start_with?("data:")
334
+ Base64.strict_decode64(url.split(",", 2)[-1])
335
+ end
336
+ end)
337
+ rescue ArgumentError, JSON::ParserError
338
+ nil
339
+ end
321
340
  end