uglifier 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of uglifier might be problematic. Click here for more details.

@@ -1,3 +1,6 @@
1
1
  [submodule "vendor/uglifyjs"]
2
2
  path = vendor/uglifyjs
3
- url = https://github.com/mishoo/UglifyJS.git
3
+ url = https://github.com/mishoo/UglifyJS2.git
4
+ [submodule "vendor/source-map"]
5
+ path = vendor/source-map
6
+ url = https://github.com/mozilla/source-map.git
@@ -1,8 +1,10 @@
1
+ language: ruby
1
2
  rvm:
2
3
  - 1.8.7
3
4
  - 1.9.2
4
5
  - 1.9.3
5
- - jruby
6
+ - 2.0.0
7
+ - jruby-18mode
6
8
  env:
7
9
  - EXECJS_RUNTIME=RubyRacer
8
10
  - EXECJS_RUNTIME=RubyRhino
@@ -14,5 +16,7 @@ matrix:
14
16
  env: EXECJS_RUNTIME=RubyRhino
15
17
  - rvm: 1.9.3
16
18
  env: EXECJS_RUNTIME=RubyRhino
17
- - rvm: jruby
19
+ - rvm: 2.0.0
20
+ env: EXECJS_RUNTIME=RubyRhino
21
+ - rvm: jruby-18mode
18
22
  env: EXECJS_RUNTIME=RubyRacer
@@ -0,0 +1,17 @@
1
+ # Contributing to Uglifier
2
+
3
+ Any contributions to Uglifier are welcome, whether they are feedback, bug reports, or - even better - pull requests.
4
+
5
+ ## Reporting issues
6
+
7
+ Uglifier uses the [GitHub issue tracker](https://github.com/lautis/uglifier/issues) to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. You can indicate support for an existing issuse by voting it up. When submitting a bug report, please include a Gist that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, **MultiJSON engine** and **ExecJS runtime**. Ideally, a bug report should include a pull request with failing specs.
8
+
9
+ ## Contributing to uglifier
10
+
11
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
12
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
13
+ * Fork the project
14
+ * Start a feature/bugfix branch
15
+ * Commit and push until you are happy with your contribution
16
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
17
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  gem "execjs", ">=0.3.0"
4
4
  gem "multi_json", "~> 1.0", ">= 1.0.2"
@@ -21,5 +21,6 @@ group :development do
21
21
  gem "rspec", "~> 2.7"
22
22
  gem "bundler", "~> 1.0"
23
23
  gem "jeweler", "~> 1.8.3"
24
- gem "rdoc", "~> 3.11"
24
+ gem "rdoc", ">= 3.11"
25
+ gem "source_map"
25
26
  end
data/README.md CHANGED
@@ -20,59 +20,70 @@ Ensure that your environment has a JavaScript interpreter supported by [ExecJS](
20
20
  # Or alternatively
21
21
  Uglifier.compile(File.read("source.js"))
22
22
 
23
- When initializing UglifyJS, you can tune the behavior of UglifyJS by passing options. For example, if you want top-level variable names to be mangled:
23
+ Uglifier also supports generating source maps:
24
24
 
25
- Uglifier.new(:toplevel => true).compile(source)
25
+ uglified, source_map = Uglifier.new.compile_with_map(source)
26
+
27
+ When initializing UglifyJS, you can tune the behavior of UglifyJS by passing options. For example, if you want disable variable name mangling:
28
+
29
+ Uglifier.new(:mangle => false).compile(source)
26
30
 
27
31
  # Or
28
- Uglifier.compile(source, :toplevel => true)
32
+ Uglifier.compile(source, :mangle => false)
29
33
 
30
34
  Available options and their defaults are
31
35
 
32
36
  {
33
- :mangle => true, # Mangle variable and function names, use :variables to skip function mangling
34
- :toplevel => false, # Mangle top-level variable names
35
- :except => [], # Variable names to be excluded from mangling
36
- :max_line_length => 32 * 1024, # Maximum line length
37
- :squeeze => true, # Squeeze code resulting in smaller, but less-readable code
38
- :seqs => true, # Reduce consecutive statements in blocks into single statement
39
- :dead_code => true, # Remove dead code (e.g. after return)
40
- :lift_vars => false, # Lift all var declarations at the start of the scope
41
- :unsafe => false, # Optimizations known to be unsafe in some situations
42
- :copyright => true, # Show copyright message
43
- :ascii_only => false, # Encode non-ASCII characters as Unicode code points
44
- :inline_script => false, # Escape </script
45
- :quote_keys => false, # Quote keys in object literals
37
+ :output => {
38
+ :ascii_only => false, # Escape non-ASCII characters
39
+ :comments => :copyright, # Preserve comments (:all, :jsdoc, :copyright, :none)
40
+ :inline_script => false, # Escape occurrences of </script in strings
41
+ :quote_keys => false, # Quote keys in object literals
42
+ :max_line_len => 32 * 1024, # Maximum line length in minified code
43
+ :ie_proof => true, # Output block brackets around do-while loops
44
+ :bracketize => false, # Bracketize if, for, do, while or with statements, even if their body is a single statement
45
+ :semicolons => true, # Separate statements with semicolons
46
+ :preserve_line => false, # Preserve line numbers in outputs
47
+ :beautify => false, # Beautify output
48
+ :indent_level => 4, # Indent level in spaces
49
+ :indent_start => 0, # Starting indent level
50
+ :space_colon => false, # Insert space before colons (only with beautifier)
51
+ :width => 80 # Specify line width when beautifier is used (only with beautifier)
52
+ },
53
+ :mangle => {
54
+ :except => ["$super"] # Argument names to be excluded from mangling
55
+ }, # Mangle variable and function names, set to false to skip mangling
56
+ :compress => {
57
+ :sequences => true, # Allow statements to be joined by commas
58
+ :properties => true, # Rewrite property access using the dot notation
59
+ :dead_code => true, # Remove unreachable code
60
+ :drop_debugger => true, # Remove debugger; statements
61
+ :unsafe => false, # Apply "unsafe" transformations
62
+ :conditionals => true, # Optimize for if-s and conditional expressions
63
+ :comparisons => true, # Apply binary node optimizations for comparisons
64
+ :evaluate => true, # Attempt to evaluate constant expressions
65
+ :booleans => true, # Various optimizations to boolean contexts
66
+ :loops => true, # Optimize lops when condition can be statically determined
67
+ :unused => true, # Drop unreferenced functions and variables
68
+ :hoist_funs => true, # Hoist function declarations
69
+ :hoist_vars => false, # Hoist var declarations
70
+ :if_return => true, # Optimizations for if/return and if/continue
71
+ :join_vars => true, # Join consecutive var statements
72
+ :cascade => true # Cascade sequences
73
+ }, # Apply transformations to code, set to false to skip
46
74
  :define => {}, # Define values for symbol replacement
47
- :beautify => false, # Ouput indented code
48
- :beautify_options => {
49
- :indent_level => 4,
50
- :indent_start => 0,
51
- :space_colon => false
52
- }
75
+ :source_filename => nil, # The filename of the input file
76
+ :source_root => nil, # The URL of the directory which contains :source_filename
77
+ :output_filename => nil, # The filename or URL where the minified output can be found
78
+ :input_source_map => nil # The contents of the source map describing the input
53
79
  }
54
80
 
55
81
  ## Development
56
82
 
57
- Uglifier uses [stitch](https://github.com/sstephenson/stitch) to compile UglifyJs for non-node JS runtimes. If you need to update or patch UglifyJS, you can stitch UglifyJS using
58
-
59
- node build.js
60
-
61
- ## Submitting an issue
62
-
63
- Uglifier uses the [GitHub issue tracker](https://github.com/lautis/uglifier/issues) to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. You can indicate support for an existing issuse by voting it up. When submitting a bug report, please include a Gist that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, **MultiJSON engine** and **ExecJS runtime**. Ideally, a bug report should include a pull request with failing specs.
64
-
65
- ## Contributing to uglifier
66
-
67
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
68
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
69
- * Fork the project
70
- * Start a feature/bugfix branch
71
- * Commit and push until you are happy with your contribution
72
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
73
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
83
+ Uglifier bundles its javascript dependencies using git submodules. If you want to rebuild the javascript you will first need to get the latest version of the code with `git submodule update --init`. After you have the git submodules at the desired versions, run `rake js` to recreate `lib/uglify.js`.
74
84
 
85
+ See [CONTRIBUTING](https://github.com/lautis/uglifier/blob/master/CONTRIBUTING.md) for details about contributing to Uglifier.
75
86
 
76
87
  ## Copyright
77
88
 
78
- © Ville Lautanala, [Flowdock](https://flowdock.com/). Released under MIT license, see [LICENSE.txt](https://github.com/lautis/uglifier/blob/master/LICENSE.txt) for more details.
89
+ © Ville Lautanala. Released under MIT license, see [LICENSE.txt](https://github.com/lautis/uglifier/blob/master/LICENSE.txt) for more details.
data/Rakefile CHANGED
@@ -17,6 +17,7 @@ Jeweler::Tasks.new do |gem|
17
17
  gem.email = "lautis@gmail.com"
18
18
  gem.homepage = "http://github.com/lautis/uglifier"
19
19
  gem.authors = ["Ville Lautanala"]
20
+ gem.license = "MIT"
20
21
  end
21
22
  Jeweler::RubygemsDotOrgTasks.new
22
23
 
@@ -37,3 +38,25 @@ Rake::RDocTask.new do |rdoc|
37
38
  rdoc.rdoc_files.include('README*')
38
39
  rdoc.rdoc_files.include('lib/**/*.rb')
39
40
  end
41
+
42
+ desc "Rebuild lib/uglify.js"
43
+ task :js do
44
+
45
+ cd 'vendor/source-map/' do
46
+ `npm install`
47
+ `node Makefile.dryice.js`
48
+ end
49
+
50
+ cd 'vendor/uglifyjs/' do
51
+ # required to run ./uglifyjs2 --self; not bundled.
52
+ `npm install`
53
+ end
54
+
55
+ source = ""
56
+ source << "window = this;"
57
+ source << File.read("vendor/source-map/dist/source-map.js")
58
+ source << "MOZ_SourceMap = sourceMap;"
59
+ source << `./vendor/uglifyjs/bin/uglifyjs --self --comments /Copyright/`
60
+
61
+ File.write("lib/uglify.js", source)
62
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 2.0.0
@@ -5,30 +5,51 @@ require "multi_json"
5
5
 
6
6
  class Uglifier
7
7
  Error = ExecJS::Error
8
- # MultiJson.engine = :json_gem
9
8
 
10
9
  # Default options for compilation
11
10
  DEFAULTS = {
12
- :mangle => true, # Mangle variable and function names, use :vars to skip function mangling
13
- :toplevel => false, # Mangle top-level variable names
14
- :except => ["$super"], # Variable names to be excluded from mangling
15
- :max_line_length => 32 * 1024, # Maximum line length
16
- :squeeze => true, # Squeeze code resulting in smaller, but less-readable code
17
- :seqs => true, # Reduce consecutive statements in blocks into single statement
18
- :dead_code => true, # Remove dead code (e.g. after return)
19
- :lift_vars => false, # Lift all var declarations at the start of the scope
20
- :unsafe => false, # Optimizations known to be unsafe in some situations
21
- :copyright => true, # Show copyright message
22
- :ascii_only => false, # Encode non-ASCII characters as Unicode code points
23
- :inline_script => false, # Escape </script
24
- :quote_keys => false, # Quote keys in object literals
11
+ :output => {
12
+ :ascii_only => false, # Escape non-ASCII characterss
13
+ :comments => :copyright, # Preserve comments (:all, :jsdoc, :copyright, :none)
14
+ :inline_script => false, # Escape occurrences of </script in strings
15
+ :quote_keys => false, # Quote keys in object literals
16
+ :max_line_len => 32 * 1024, # Maximum line length in minified code
17
+ :ie_proof => true, # Output block brakcets around do-while loops
18
+ :bracketize => false, # Bracketize if, for, do, while or with statements, even if their body is a single statement
19
+ :semicolons => true, # Separate statements with semicolons
20
+ :preserve_line => false, # Preserve line numbers in outputs
21
+ :beautify => false, # Beautify output
22
+ :indent_level => 4, # Indent level in spaces
23
+ :indent_start => 0, # Starting indent level
24
+ :space_colon => false, # Insert space before colons (only with beautifier)
25
+ :width => 80 # Specify line width when beautifier is used (only with beautifier)
26
+ },
27
+ :mangle => {
28
+ :except => ["$super"] # Argument names to be excluded from mangling
29
+ }, # Mangle variable and function names, set to false to skip mangling
30
+ :compress => {
31
+ :sequences => true, # Allow statements to be joined by commas
32
+ :properties => true, # Rewrite property access using the dot notation
33
+ :dead_code => true, # Remove unreachable code
34
+ :drop_debugger => true, # Remove debugger; statements
35
+ :unsafe => false, # Apply "unsafe" transformations
36
+ :conditionals => true, # Optimize for if-s and conditional expressions
37
+ :comparisons => true, # Apply binary node optimizations for comparisons
38
+ :evaluate => true, # Attempt to evaluate constant expressions
39
+ :booleans => true, # Various optimizations to boolean contexts
40
+ :loops => true, # Optimize lops when condition can be statically determined
41
+ :unused => true, # Drop unreferenced functions and variables
42
+ :hoist_funs => true, # Hoist function declarations
43
+ :hoist_vars => false, # Hoist var declarations
44
+ :if_return => true, # Optimizations for if/return and if/continue
45
+ :join_vars => true, # Join consecutive var statements
46
+ :cascade => true # Cascade sequences
47
+ }, # Apply transformations to code, set to false to skip
25
48
  :define => {}, # Define values for symbol replacement
26
- :beautify => false, # Ouput indented code
27
- :beautify_options => {
28
- :indent_level => 4,
29
- :indent_start => 0,
30
- :space_colon => false
31
- }
49
+ :source_filename => nil, # The filename of the input file
50
+ :source_root => nil, # The URL of the directory which contains :source_filename
51
+ :output_filename => nil, # The filename or URL where the minified output can be found
52
+ :input_source_map => nil # The contents of the source map describing the input
32
53
  }
33
54
 
34
55
  SourcePath = File.expand_path("../uglify.js", __FILE__)
@@ -44,11 +65,24 @@ class Uglifier
44
65
  self.new(options).compile(source)
45
66
  end
46
67
 
68
+ # Minifies JavaScript code and generates a source map using implicit context.
69
+ #
70
+ # source should be a String or IO object containing valid JavaScript.
71
+ # options contain optional overrides to Uglifier::DEFAULTS
72
+ #
73
+ # Returns a pair of [minified code as String, source map as a String]
74
+ def self.compile_with_map(source, options = {})
75
+ self.new(options).compile_with_map(source)
76
+ end
77
+
47
78
  # Initialize new context for Uglifier with given options
48
79
  #
49
80
  # options - Hash of options to override Uglifier::DEFAULTS
50
81
  def initialize(options = {})
51
- @options = DEFAULTS.merge(options)
82
+ (options.keys - DEFAULTS.keys - [:comments, :squeeze])[0..1].each do |missing|
83
+ raise ArgumentError.new("Invalid option: #{missing}")
84
+ end
85
+ @options = options
52
86
  @context = ExecJS.compile(File.open(ES5FallbackPath, "r:UTF-8").read + File.open(SourcePath, "r:UTF-8").read)
53
87
  end
54
88
 
@@ -58,96 +92,137 @@ class Uglifier
58
92
  #
59
93
  # Returns minified code as String
60
94
  def compile(source)
61
- source = source.respond_to?(:read) ? source.read : source.to_s
95
+ really_compile(source, false)
96
+ end
97
+ alias_method :compress, :compile
62
98
 
63
- js = []
64
- js << "var result = '';"
65
- js << "var source = #{json_encode(source)};"
66
- js << "var ast = UglifyJS.parser.parse(source);"
99
+ # Minifies JavaScript code and generates a source map
100
+ #
101
+ # source should be a String or IO object containing valid JavaScript.
102
+ #
103
+ # Returns a pair of [minified code as String, source map as a String]
104
+ def compile_with_map(source)
105
+ really_compile(source, true)
106
+ end
67
107
 
68
- if @options[:lift_vars]
69
- js << "ast = UglifyJS.uglify.ast_lift_variables(ast);"
70
- end
108
+ private
109
+
110
+ # Minifies JavaScript code
111
+ #
112
+ # source should be a String or IO object containing valid JavaScript.
113
+ def really_compile(source, generate_map)
114
+ source = source.respond_to?(:read) ? source.read : source.to_s
71
115
 
72
- if @options[:copyright]
73
- js << <<-JS
74
- var comments = UglifyJS.parser.tokenizer(source)().comments_before;
75
- for (var i = 0; i < comments.length; i++) {
76
- var c = comments[i];
77
- result += (c.type == "comment1") ? "//"+c.value+"\\n" : "/*"+c.value+"*/\\n";
116
+ js = <<-JS
117
+ function comments(option) {
118
+ if (Object.prototype.toString.call(option) === '[object Array]') {
119
+ return new RegExp(option[0], option[1]);
120
+ } else if (option == "jsdoc") {
121
+ return function(node, comment) {
122
+ if (comment.type == "comment2") {
123
+ return /@preserve|@license|@cc_on/i.test(comment.value);
124
+ } else {
125
+ return false;
126
+ }
127
+ }
128
+ } else {
129
+ return option;
130
+ }
78
131
  }
79
- JS
80
- end
81
132
 
82
- js << "ast = UglifyJS.uglify.ast_mangle(ast, #{json_encode(mangle_options)});"
133
+ var options = %s;
134
+ var source = options.source;
135
+ var ast = UglifyJS.parse(source, options.parse_options);
136
+ ast.figure_out_scope();
83
137
 
84
- if @options[:squeeze]
85
- js << "ast = UglifyJS.uglify.ast_squeeze(ast, #{json_encode(squeeze_options)});"
86
- end
138
+ if (options.compress) {
139
+ var compressor = UglifyJS.Compressor(options.compress);
140
+ ast = ast.transform(compressor);
141
+ ast.figure_out_scope();
142
+ }
87
143
 
88
- if @options[:unsafe]
89
- js << "ast = UglifyJS.uglify.ast_squeeze_more(ast);"
90
- end
144
+ if (options.mangle) {
145
+ ast.compute_char_frequency();
146
+ ast.mangle_names(options.mangle);
147
+ }
91
148
 
92
- js << "result += UglifyJS.uglify.gen_code(ast, #{json_encode(gen_code_options)});"
149
+ var gen_code_options = options.output;
150
+ gen_code_options.comments = comments(options.output.comments);
93
151
 
94
- if !@options[:beautify] && @options[:max_line_length]
95
- js << "result = UglifyJS.uglify.split_lines(result, #{@options[:max_line_length].to_i})"
96
- end
152
+ if (options.generate_map) {
153
+ var source_map = UglifyJS.SourceMap(options.source_map_options);
154
+ gen_code_options.source_map = source_map;
155
+ }
97
156
 
98
- js << "return result + ';';"
157
+ var stream = UglifyJS.OutputStream(gen_code_options);
99
158
 
100
- @context.exec js.join("\n")
101
- end
102
- alias_method :compress, :compile
159
+ ast.print(stream);
160
+ if (options.generate_map) {
161
+ return [stream.toString(), source_map.toString()];
162
+ } else {
163
+ return stream.toString();
164
+ }
165
+ JS
103
166
 
104
- private
167
+ @context.exec(js % json_encode(
168
+ :source => source,
169
+ :output => output_options,
170
+ :compress => compressor_options,
171
+ :mangle => mangle_options,
172
+ :parse_options => parse_options,
173
+ :source_map_options => source_map_options,
174
+ :generate_map => (!!generate_map)
175
+ ))
176
+ end
105
177
 
106
178
  def mangle_options
107
- {
108
- "mangle" => @options[:mangle],
109
- "toplevel" => @options[:toplevel],
110
- "defines" => defines,
111
- "except" => @options[:except],
112
- "no_functions" => @options[:mangle] == :vars
113
- }
179
+ conditional_option(@options[:mangle], DEFAULTS[:mangle])
114
180
  end
115
181
 
116
- def squeeze_options
117
- {
118
- "make_seqs" => @options[:seqs],
119
- "dead_code" => @options[:dead_code],
120
- "keep_comps" => !@options[:unsafe]
121
- }
182
+ def compressor_options
183
+ defaults = conditional_option(DEFAULTS[:compress],
184
+ :global_defs => @options[:define] || {})
185
+ conditional_option(@options[:compress] || @options[:squeeze], defaults)
122
186
  end
123
187
 
124
- def defines
125
- Hash[(@options[:define] || {}).map do |k, v|
126
- token = if v.is_a? Numeric
127
- ['num', v]
128
- elsif [true, false].include?(v)
129
- ['name', v.to_s]
130
- elsif v == nil
131
- ['name', 'null']
132
- else
133
- ['string', v.to_s]
134
- end
135
- [k, token]
136
- end]
188
+ def comment_options
189
+ val = if @options.has_key?(:output) && @options[:output].has_key?(:comments)
190
+ @options[:output][:comments]
191
+ elsif @options.has_key?(:comments)
192
+ @options[:comments]
193
+ else
194
+ DEFAULTS[:output][:comments]
195
+ end
196
+
197
+ case val
198
+ when :all, true
199
+ true
200
+ when :jsdoc
201
+ "jsdoc"
202
+ when :copyright
203
+ encode_regexp(/Copyright/i)
204
+ when Regexp
205
+ encode_regexp(val)
206
+ else
207
+ false
208
+ end
137
209
  end
138
210
 
139
- def gen_code_options
140
- options = {
141
- :ascii_only => @options[:ascii_only],
142
- :inline_script => @options[:inline_script],
143
- :quote_keys => @options[:quote_keys]
211
+ def output_options
212
+ DEFAULTS[:output].merge(@options[:output] || {}).merge(
213
+ :comments => comment_options)
214
+ end
215
+
216
+ def source_map_options
217
+ {
218
+ :file => @options[:output_filename],
219
+ :root => @options[:source_root],
220
+ :orig => @options[:input_source_map]
144
221
  }
222
+ end
145
223
 
146
- if @options[:beautify]
147
- options.merge(:beautify => true).merge(@options[:beautify_options])
148
- else
149
- options
150
- end
224
+ def parse_options
225
+ {:filename => @options[:source_filename]}
151
226
  end
152
227
 
153
228
  # MultiJson API detection
@@ -160,4 +235,24 @@ class Uglifier
160
235
  MultiJson.encode(obj)
161
236
  end
162
237
  end
238
+
239
+ def encode_regexp(regexp)
240
+ modifiers = if regexp.casefold?
241
+ "i"
242
+ else
243
+ ""
244
+ end
245
+
246
+ [regexp.source, modifiers]
247
+ end
248
+
249
+ def conditional_option(value, defaults)
250
+ if value == true || value == nil
251
+ defaults
252
+ elsif value
253
+ defaults.merge(value)
254
+ else
255
+ false
256
+ end
257
+ end
163
258
  end