coffeescript-router 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/.gitignore +3 -0
  2. data/Cakefile +122 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/LICENSE.txt +19 -0
  6. data/README.md +18 -0
  7. data/Rakefile +2 -0
  8. data/SpecRunner.html +25 -0
  9. data/coffeescript-router.gemspec +19 -0
  10. data/lib/CoffeeScript.png +0 -0
  11. data/lib/coffeescript-router/version.rb +5 -0
  12. data/lib/jasmine-1.0.2/MIT.LICENSE +20 -0
  13. data/lib/jasmine-1.0.2/jasmine-html.js +188 -0
  14. data/lib/jasmine-1.0.2/jasmine.css +166 -0
  15. data/lib/jasmine-1.0.2/jasmine.js +2421 -0
  16. data/node_modules/.bin/cake +7 -0
  17. data/node_modules/.bin/coffee +7 -0
  18. data/node_modules/.bin/uglifyjs +212 -0
  19. data/node_modules/coffee-script/.npmignore +11 -0
  20. data/node_modules/coffee-script/LICENSE +22 -0
  21. data/node_modules/coffee-script/README +48 -0
  22. data/node_modules/coffee-script/Rakefile +78 -0
  23. data/node_modules/coffee-script/bin/cake +7 -0
  24. data/node_modules/coffee-script/bin/coffee +7 -0
  25. data/node_modules/coffee-script/lib/browser.js +75 -0
  26. data/node_modules/coffee-script/lib/cake.js +76 -0
  27. data/node_modules/coffee-script/lib/coffee-script.js +107 -0
  28. data/node_modules/coffee-script/lib/command.js +274 -0
  29. data/node_modules/coffee-script/lib/grammar.js +591 -0
  30. data/node_modules/coffee-script/lib/helpers.js +66 -0
  31. data/node_modules/coffee-script/lib/index.js +8 -0
  32. data/node_modules/coffee-script/lib/lexer.js +650 -0
  33. data/node_modules/coffee-script/lib/nodes.js +2230 -0
  34. data/node_modules/coffee-script/lib/optparse.js +111 -0
  35. data/node_modules/coffee-script/lib/parser.js +676 -0
  36. data/node_modules/coffee-script/lib/repl.js +114 -0
  37. data/node_modules/coffee-script/lib/rewriter.js +359 -0
  38. data/node_modules/coffee-script/lib/scope.js +120 -0
  39. data/node_modules/coffee-script/package.json +27 -0
  40. data/node_modules/growl/History.md +16 -0
  41. data/node_modules/growl/Readme.md +74 -0
  42. data/node_modules/growl/lib/growl.js +82 -0
  43. data/node_modules/growl/package.json +6 -0
  44. data/node_modules/growl/test.js +17 -0
  45. data/node_modules/uglify-js/.gitignore +4 -0
  46. data/node_modules/uglify-js/README.html +825 -0
  47. data/node_modules/uglify-js/README.org +431 -0
  48. data/node_modules/uglify-js/bin/uglifyjs +212 -0
  49. data/node_modules/uglify-js/docstyle.css +75 -0
  50. data/node_modules/uglify-js/lib/parse-js.js +1319 -0
  51. data/node_modules/uglify-js/lib/process.js +1614 -0
  52. data/node_modules/uglify-js/lib/squeeze-more.js +22 -0
  53. data/node_modules/uglify-js/package.json +22 -0
  54. data/node_modules/uglify-js/test/beautify.js +28 -0
  55. data/node_modules/uglify-js/test/testparser.js +402 -0
  56. data/node_modules/uglify-js/test/unit/compress/expected/array1.js +1 -0
  57. data/node_modules/uglify-js/test/unit/compress/expected/array2.js +1 -0
  58. data/node_modules/uglify-js/test/unit/compress/expected/array3.js +1 -0
  59. data/node_modules/uglify-js/test/unit/compress/expected/array4.js +1 -0
  60. data/node_modules/uglify-js/test/unit/compress/expected/assignment.js +1 -0
  61. data/node_modules/uglify-js/test/unit/compress/expected/concatstring.js +1 -0
  62. data/node_modules/uglify-js/test/unit/compress/expected/const.js +1 -0
  63. data/node_modules/uglify-js/test/unit/compress/expected/empty-blocks.js +1 -0
  64. data/node_modules/uglify-js/test/unit/compress/expected/forstatement.js +1 -0
  65. data/node_modules/uglify-js/test/unit/compress/expected/if.js +1 -0
  66. data/node_modules/uglify-js/test/unit/compress/expected/ifreturn.js +1 -0
  67. data/node_modules/uglify-js/test/unit/compress/expected/issue10.js +1 -0
  68. data/node_modules/uglify-js/test/unit/compress/expected/issue11.js +1 -0
  69. data/node_modules/uglify-js/test/unit/compress/expected/issue13.js +1 -0
  70. data/node_modules/uglify-js/test/unit/compress/expected/issue14.js +1 -0
  71. data/node_modules/uglify-js/test/unit/compress/expected/issue16.js +1 -0
  72. data/node_modules/uglify-js/test/unit/compress/expected/issue17.js +1 -0
  73. data/node_modules/uglify-js/test/unit/compress/expected/issue20.js +1 -0
  74. data/node_modules/uglify-js/test/unit/compress/expected/issue21.js +1 -0
  75. data/node_modules/uglify-js/test/unit/compress/expected/issue25.js +1 -0
  76. data/node_modules/uglify-js/test/unit/compress/expected/issue27.js +1 -0
  77. data/node_modules/uglify-js/test/unit/compress/expected/issue28.js +1 -0
  78. data/node_modules/uglify-js/test/unit/compress/expected/issue29.js +1 -0
  79. data/node_modules/uglify-js/test/unit/compress/expected/issue30.js +1 -0
  80. data/node_modules/uglify-js/test/unit/compress/expected/issue34.js +1 -0
  81. data/node_modules/uglify-js/test/unit/compress/expected/issue4.js +1 -0
  82. data/node_modules/uglify-js/test/unit/compress/expected/issue48.js +1 -0
  83. data/node_modules/uglify-js/test/unit/compress/expected/issue50.js +1 -0
  84. data/node_modules/uglify-js/test/unit/compress/expected/issue53.js +1 -0
  85. data/node_modules/uglify-js/test/unit/compress/expected/issue54.1.js +1 -0
  86. data/node_modules/uglify-js/test/unit/compress/expected/issue68.js +1 -0
  87. data/node_modules/uglify-js/test/unit/compress/expected/issue69.js +1 -0
  88. data/node_modules/uglify-js/test/unit/compress/expected/issue9.js +1 -0
  89. data/node_modules/uglify-js/test/unit/compress/expected/strict-equals.js +1 -0
  90. data/node_modules/uglify-js/test/unit/compress/expected/var.js +1 -0
  91. data/node_modules/uglify-js/test/unit/compress/test/array1.js +3 -0
  92. data/node_modules/uglify-js/test/unit/compress/test/array2.js +4 -0
  93. data/node_modules/uglify-js/test/unit/compress/test/array3.js +4 -0
  94. data/node_modules/uglify-js/test/unit/compress/test/array4.js +6 -0
  95. data/node_modules/uglify-js/test/unit/compress/test/assignment.js +20 -0
  96. data/node_modules/uglify-js/test/unit/compress/test/concatstring.js +3 -0
  97. data/node_modules/uglify-js/test/unit/compress/test/const.js +5 -0
  98. data/node_modules/uglify-js/test/unit/compress/test/empty-blocks.js +4 -0
  99. data/node_modules/uglify-js/test/unit/compress/test/forstatement.js +10 -0
  100. data/node_modules/uglify-js/test/unit/compress/test/if.js +6 -0
  101. data/node_modules/uglify-js/test/unit/compress/test/ifreturn.js +9 -0
  102. data/node_modules/uglify-js/test/unit/compress/test/issue10.js +1 -0
  103. data/node_modules/uglify-js/test/unit/compress/test/issue11.js +3 -0
  104. data/node_modules/uglify-js/test/unit/compress/test/issue13.js +1 -0
  105. data/node_modules/uglify-js/test/unit/compress/test/issue14.js +1 -0
  106. data/node_modules/uglify-js/test/unit/compress/test/issue16.js +1 -0
  107. data/node_modules/uglify-js/test/unit/compress/test/issue17.js +4 -0
  108. data/node_modules/uglify-js/test/unit/compress/test/issue20.js +1 -0
  109. data/node_modules/uglify-js/test/unit/compress/test/issue21.js +6 -0
  110. data/node_modules/uglify-js/test/unit/compress/test/issue25.js +7 -0
  111. data/node_modules/uglify-js/test/unit/compress/test/issue27.js +1 -0
  112. data/node_modules/uglify-js/test/unit/compress/test/issue28.js +3 -0
  113. data/node_modules/uglify-js/test/unit/compress/test/issue29.js +1 -0
  114. data/node_modules/uglify-js/test/unit/compress/test/issue30.js +3 -0
  115. data/node_modules/uglify-js/test/unit/compress/test/issue34.js +3 -0
  116. data/node_modules/uglify-js/test/unit/compress/test/issue4.js +3 -0
  117. data/node_modules/uglify-js/test/unit/compress/test/issue48.js +1 -0
  118. data/node_modules/uglify-js/test/unit/compress/test/issue50.js +9 -0
  119. data/node_modules/uglify-js/test/unit/compress/test/issue53.js +1 -0
  120. data/node_modules/uglify-js/test/unit/compress/test/issue54.1.js +3 -0
  121. data/node_modules/uglify-js/test/unit/compress/test/issue68.js +5 -0
  122. data/node_modules/uglify-js/test/unit/compress/test/issue69.js +1 -0
  123. data/node_modules/uglify-js/test/unit/compress/test/issue9.js +4 -0
  124. data/node_modules/uglify-js/test/unit/compress/test/strict-equals.js +3 -0
  125. data/node_modules/uglify-js/test/unit/compress/test/var.js +3 -0
  126. data/node_modules/uglify-js/test/unit/scripts.js +46 -0
  127. data/node_modules/uglify-js/uglify-js.js +2 -0
  128. data/src/coffeescript/coffeescript-router.js.coffee +31 -0
  129. data/test/coffeescript/routerSpec.coffee +47 -0
  130. data/vendor/assets/javascripts/coffeescript-router.min.js +1 -0
  131. metadata +187 -0
@@ -0,0 +1,431 @@
1
+ #+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier
2
+ #+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier
3
+ #+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript
4
+ #+STYLE: <link rel="stylesheet" type="text/css" href="docstyle.css" />
5
+ #+AUTHOR: Mihai Bazon
6
+ #+EMAIL: mihai.bazon@gmail.com
7
+
8
+ * UglifyJS --- a JavaScript parser/compressor/beautifier
9
+
10
+ This package implements a general-purpose JavaScript
11
+ parser/compressor/beautifier toolkit. It is developed on [[http://nodejs.org/][NodeJS]], but it
12
+ should work on any JavaScript platform supporting the CommonJS module system
13
+ (and if your platform of choice doesn't support CommonJS, you can easily
14
+ implement it, or discard the =exports.*= lines from UglifyJS sources).
15
+
16
+ The tokenizer/parser generates an abstract syntax tree from JS code. You
17
+ can then traverse the AST to learn more about the code, or do various
18
+ manipulations on it. This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a
19
+ port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn
20
+ Haverbeke]].
21
+
22
+ ( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of
23
+ UglifyJS. )
24
+
25
+ The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and
26
+ manipulates the AST generated by the parser to provide the following:
27
+
28
+ - ability to re-generate JavaScript code from the AST. Optionally
29
+ indented---you can use this if you want to “beautify” a program that has
30
+ been compressed, so that you can inspect the source. But you can also run
31
+ our code generator to print out an AST without any whitespace, so you
32
+ achieve compression as well.
33
+
34
+ - shorten variable names (usually to single characters). Our mangler will
35
+ analyze the code and generate proper variable names, depending on scope
36
+ and usage, and is smart enough to deal with globals defined elsewhere, or
37
+ with =eval()= calls or =with{}= statements. In short, if =eval()= or
38
+ =with{}= are used in some scope, then all variables in that scope and any
39
+ variables in the parent scopes will remain unmangled, and any references
40
+ to such variables remain unmangled as well.
41
+
42
+ - various small optimizations that may lead to faster code but certainly
43
+ lead to smaller code. Where possible, we do the following:
44
+
45
+ - foo["bar"] ==> foo.bar
46
+
47
+ - remove block brackets ={}=
48
+
49
+ - join consecutive var declarations:
50
+ var a = 10; var b = 20; ==> var a=10,b=20;
51
+
52
+ - resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the
53
+ replacement if the result occupies less bytes; for example 1/3 would
54
+ translate to 0.333333333333, so in this case we don't replace it.
55
+
56
+ - consecutive statements in blocks are merged into a sequence; in many
57
+ cases, this leaves blocks with a single statement, so then we can remove
58
+ the block brackets.
59
+
60
+ - various optimizations for IF statements:
61
+
62
+ - if (foo) bar(); else baz(); ==> foo?bar():baz();
63
+ - if (!foo) bar(); else baz(); ==> foo?baz():bar();
64
+ - if (foo) bar(); ==> foo&&bar();
65
+ - if (!foo) bar(); ==> foo||bar();
66
+ - if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
67
+ - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
68
+
69
+ - remove some unreachable code and warn about it (code that follows a
70
+ =return=, =throw=, =break= or =continue= statement, except
71
+ function/variable declarations).
72
+
73
+ ** <<Unsafe transformations>>
74
+
75
+ UglifyJS tries its best to achieve great compression while leaving the
76
+ semantics of the code intact. In general, if your code logic is broken by
77
+ UglifyJS then it's a bug in UglifyJS and you should report it and I should
78
+ fix it. :-)
79
+
80
+ However, I opted to include the following potentially unsafe transformations
81
+ as default behavior. Discussion is welcome, if you have ideas of how to
82
+ handle this better, or any objections to these optimizations, please let me
83
+ know.
84
+
85
+ *** Calls involving the global Array constructor
86
+
87
+ The following transformations occur:
88
+
89
+ #+BEGIN_SRC js
90
+ new Array(1, 2, 3, 4) => [1,2,3,4]
91
+ Array(a, b, c) => [a,b,c]
92
+ new Array(5) => Array(5)
93
+ new Array(a) => Array(a)
94
+ #+END_SRC
95
+
96
+ These are all safe if the Array name isn't redefined. JavaScript does allow
97
+ one to globally redefine Array (and pretty much everything, in fact) but I
98
+ personally don't see why would anyone do that.
99
+
100
+ UglifyJS does handle the case where Array is redefined locally, or even
101
+ globally but with a =function= or =var= declaration. Therefore, in the
102
+ following cases UglifyJS *doesn't touch* calls or instantiations of Array:
103
+
104
+ #+BEGIN_SRC js
105
+ // case 1. globally declared variable
106
+ var Array;
107
+ new Array(1, 2, 3);
108
+ Array(a, b);
109
+
110
+ // or (can be declared later)
111
+ new Array(1, 2, 3);
112
+ var Array;
113
+
114
+ // or (can be a function)
115
+ new Array(1, 2, 3);
116
+ function Array() { ... }
117
+
118
+ // case 2. declared in a function
119
+ (function(){
120
+ a = new Array(1, 2, 3);
121
+ b = Array(5, 6);
122
+ var Array;
123
+ })();
124
+
125
+ // or
126
+ (function(Array){
127
+ return Array(5, 6, 7);
128
+ })();
129
+
130
+ // or
131
+ (function(){
132
+ return new Array(1, 2, 3, 4);
133
+ function Array() { ... }
134
+ })();
135
+
136
+ // etc.
137
+ #+END_SRC
138
+
139
+ ** Install (NPM)
140
+
141
+ UglifyJS is now available through NPM --- =npm install uglify-js= should do
142
+ the job.
143
+
144
+ ** Install latest code from GitHub
145
+
146
+ #+BEGIN_SRC sh
147
+ ## clone the repository
148
+ mkdir -p /where/you/wanna/put/it
149
+ cd /where/you/wanna/put/it
150
+ git clone git://github.com/mishoo/UglifyJS.git
151
+
152
+ ## make the module available to Node
153
+ mkdir -p ~/.node_libraries/
154
+ cd ~/.node_libraries/
155
+ ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
156
+
157
+ ## and if you want the CLI script too:
158
+ mkdir -p ~/bin
159
+ cd ~/bin
160
+ ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
161
+ # (then add ~/bin to your $PATH if it's not there already)
162
+ #+END_SRC
163
+
164
+ ** Usage
165
+
166
+ There is a command-line tool that exposes the functionality of this library
167
+ for your shell-scripting needs:
168
+
169
+ #+BEGIN_SRC sh
170
+ uglifyjs [ options... ] [ filename ]
171
+ #+END_SRC
172
+
173
+ =filename= should be the last argument and should name the file from which
174
+ to read the JavaScript code. If you don't specify it, it will read code
175
+ from STDIN.
176
+
177
+ Supported options:
178
+
179
+ - =-b= or =--beautify= --- output indented code; when passed, additional
180
+ options control the beautifier:
181
+
182
+ - =-i N= or =--indent N= --- indentation level (number of spaces)
183
+
184
+ - =-q= or =--quote-keys= --- quote keys in literal objects (by default,
185
+ only keys that cannot be identifier names will be quotes).
186
+
187
+ - =--ascii= --- pass this argument to encode non-ASCII characters as
188
+ =\uXXXX= sequences. By default UglifyJS won't bother to do it and will
189
+ output Unicode characters instead. (the output is always encoded in UTF8,
190
+ but if you pass this option you'll only get ASCII).
191
+
192
+ - =-nm= or =--no-mangle= --- don't mangle variable names
193
+
194
+ - =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various
195
+ optimizations that result in smaller, less readable code).
196
+
197
+ - =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too
198
+ (by default we don't do this).
199
+
200
+ - =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass
201
+ =--no-squeeze=) it will reduce consecutive statements in blocks into a
202
+ sequence. For example, "a = 10; b = 20; foo();" will be written as
203
+ "a=10,b=20,foo();". In various occasions, this allows us to discard the
204
+ block brackets (since the block becomes a single statement). This is ON
205
+ by default because it seems safe and saves a few hundred bytes on some
206
+ libs that I tested it on, but pass =--no-seqs= to disable it.
207
+
208
+ - =--no-dead-code= --- by default, UglifyJS will remove code that is
209
+ obviously unreachable (code that follows a =return=, =throw=, =break= or
210
+ =continue= statement and is not a function/variable declaration). Pass
211
+ this option to disable this optimization.
212
+
213
+ - =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial
214
+ comment tokens in the generated code (assumed to be copyright information
215
+ etc.). If you pass this it will discard it.
216
+
217
+ - =-o filename= or =--output filename= --- put the result in =filename=. If
218
+ this isn't given, the result goes to standard output (or see next one).
219
+
220
+ - =--overwrite= --- if the code is read from a file (not from STDIN) and you
221
+ pass =--overwrite= then the output will be written in the same file.
222
+
223
+ - =--ast= --- pass this if you want to get the Abstract Syntax Tree instead
224
+ of JavaScript as output. Useful for debugging or learning more about the
225
+ internals.
226
+
227
+ - =-v= or =--verbose= --- output some notes on STDERR (for now just how long
228
+ each operation takes).
229
+
230
+ - =--extra= --- enable additional optimizations that have not yet been
231
+ extensively tested. These might, or might not, break your code. If you
232
+ find a bug using this option, please report a test case.
233
+
234
+ - =--unsafe= --- enable other additional optimizations that are known to be
235
+ unsafe in some contrived situations, but could still be generally useful.
236
+ For now only this:
237
+
238
+ - foo.toString() ==> foo+""
239
+
240
+ - =--max-line-len= (default 32K characters) --- add a newline after around
241
+ 32K characters. I've seen both FF and Chrome croak when all the code was
242
+ on a single line of around 670K. Pass --max-line-len 0 to disable this
243
+ safety feature.
244
+
245
+ - =--reserved-names= --- some libraries rely on certain names to be used, as
246
+ pointed out in issue #92 and #81, so this option allow you to exclude such
247
+ names from the mangler. For example, to keep names =require= and =$super=
248
+ intact you'd specify --reserved-names "require,$super".
249
+
250
+ *** API
251
+
252
+ To use the library from JavaScript, you'd do the following (example for
253
+ NodeJS):
254
+
255
+ #+BEGIN_SRC js
256
+ var jsp = require("uglify-js").parser;
257
+ var pro = require("uglify-js").uglify;
258
+
259
+ var orig_code = "... JS code here";
260
+ var ast = jsp.parse(orig_code); // parse code and get the initial AST
261
+ ast = pro.ast_mangle(ast); // get a new AST with mangled names
262
+ ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
263
+ var final_code = pro.gen_code(ast); // compressed code here
264
+ #+END_SRC
265
+
266
+ The above performs the full compression that is possible right now. As you
267
+ can see, there are a sequence of steps which you can apply. For example if
268
+ you want compressed output but for some reason you don't want to mangle
269
+ variable names, you would simply skip the line that calls
270
+ =pro.ast_mangle(ast)=.
271
+
272
+ Some of these functions take optional arguments. Here's a description:
273
+
274
+ - =jsp.parse(code, strict_semicolons)= -- parses JS code and returns an AST.
275
+ =strict_semicolons= is optional and defaults to =false=. If you pass
276
+ =true= then the parser will throw an error when it expects a semicolon and
277
+ it doesn't find it. For most JS code you don't want that, but it's useful
278
+ if you want to strictly sanitize your code.
279
+
280
+ - =pro.ast_mangle(ast, options)= -- generates a new AST containing mangled
281
+ (compressed) variable and function names. It supports the following
282
+ options:
283
+
284
+ - =toplevel= -- mangle toplevel names (by default we don't touch them).
285
+ - =except= -- an array of names to exclude from compression.
286
+
287
+ - =pro.ast_squeeze(ast, options)= -- employs further optimizations designed
288
+ to reduce the size of the code that =gen_code= would generate from the
289
+ AST. Returns a new AST. =options= can be a hash; the supported options
290
+ are:
291
+
292
+ - =make_seqs= (default true) which will cause consecutive statements in a
293
+ block to be merged using the "sequence" (comma) operator
294
+
295
+ - =dead_code= (default true) which will remove unreachable code.
296
+
297
+ - =pro.gen_code(ast, options)= -- generates JS code from the AST. By
298
+ default it's minified, but using the =options= argument you can get nicely
299
+ formatted output. =options= is, well, optional :-) and if you pass it it
300
+ must be an object and supports the following properties (below you can see
301
+ the default values):
302
+
303
+ - =beautify: false= -- pass =true= if you want indented output
304
+ - =indent_start: 0= (only applies when =beautify= is =true=) -- initial
305
+ indentation in spaces
306
+ - =indent_level: 4= (only applies when =beautify= is =true=) --
307
+ indentation level, in spaces (pass an even number)
308
+ - =quote_keys: false= -- if you pass =true= it will quote all keys in
309
+ literal objects
310
+ - =space_colon: false= (only applies when =beautify= is =true=) -- wether
311
+ to put a space before the colon in object literals
312
+ - =ascii_only: false= -- pass =true= if you want to encode non-ASCII
313
+ characters as =\uXXXX=.
314
+
315
+ *** Beautifier shortcoming -- no more comments
316
+
317
+ The beautifier can be used as a general purpose indentation tool. It's
318
+ useful when you want to make a minified file readable. One limitation,
319
+ though, is that it discards all comments, so you don't really want to use it
320
+ to reformat your code, unless you don't have, or don't care about, comments.
321
+
322
+ In fact it's not the beautifier who discards comments --- they are dumped at
323
+ the parsing stage, when we build the initial AST. Comments don't really
324
+ make sense in the AST, and while we could add nodes for them, it would be
325
+ inconvenient because we'd have to add special rules to ignore them at all
326
+ the processing stages.
327
+
328
+ ** Compression -- how good is it?
329
+
330
+ (XXX: this is somewhat outdated. On the jQuery source code we beat Closure
331
+ by 168 bytes (560 after gzip) and by many seconds.)
332
+
333
+ There are a few popular JS minifiers nowadays -- the two most well known
334
+ being the GoogleClosure (GCL) compiler and the YUI compressor. For some
335
+ reason they are both written in Java. I didn't really hope to beat any of
336
+ them, but finally I did -- UglifyJS compresses better than the YUI
337
+ compressor, and safer than GoogleClosure.
338
+
339
+ I tested it on two big libraries. [[http://www.dynarchlib.com/][DynarchLIB]] is my own, and it's big enough
340
+ to contain probably all the JavaScript tricks known to mankind. [[http://jquery.com/][jQuery]] is
341
+ definitely the most popular JavaScript library (to some people, it's a
342
+ synonym to JavaScript itself).
343
+
344
+ I cannot swear that there are no bugs in the generated codes, but they
345
+ appear to work fine.
346
+
347
+ Compression results:
348
+
349
+ | Library | Orig. size | UglifyJS | YUI | GCL |
350
+ |------------+------------+----------+----------------+------------------------|
351
+ | DynarchLIB | 636896 | 241441 | 246452 (+5011) | 240439 (-1002) (buggy) |
352
+ | jQuery | 163855 | 72006 | 79702 (+7696) | 71858 (-148) |
353
+
354
+ UglifyJS is the fastest to run. On my laptop UglifyJS takes 1.35s for
355
+ DynarchLIB, while YUI takes 2.7s and GCL takes 6.5s.
356
+
357
+ GoogleClosure does a lot of smart ass optimizations. I had to strive really
358
+ hard to get close to it. It should be possible to even beat it, but then
359
+ again, GCL has a gazillion lines of code and runs terribly slow, so I'm not
360
+ sure it worths spending the effort to save a few bytes. Also, GCL doesn't
361
+ cope with =eval()= or =with{}= -- it just dumps a warning and proceeds to
362
+ mangle names anyway; my DynarchLIB compiled with it is buggy because of
363
+ this.
364
+
365
+ UglifyJS consists of ~1100 lines of code for the tokenizer/parser, and ~1100
366
+ lines for the compressor and code generator. That should make it very
367
+ maintainable and easily extensible, so I would say it has a good place in
368
+ this field and it's bound to become the de-facto standard JS minifier. And
369
+ I shall rule the world. :-) Use it, and *spread the word*!
370
+
371
+ ** Bugs?
372
+
373
+ Unfortunately, for the time being there is no automated test suite. But I
374
+ ran the compressor manually on non-trivial code, and then I tested that the
375
+ generated code works as expected. A few hundred times.
376
+
377
+ DynarchLIB was started in times when there was no good JS minifier.
378
+ Therefore I was quite religious about trying to write short code manually,
379
+ and as such DL contains a lot of syntactic hacks[1] such as “foo == bar ? a
380
+ = 10 : b = 20”, though the more readable version would clearly be to use
381
+ “if/else”.
382
+
383
+ Since the parser/compressor runs fine on DL and jQuery, I'm quite confident
384
+ that it's solid enough for production use. If you can identify any bugs,
385
+ I'd love to hear about them ([[http://groups.google.com/group/uglifyjs][use the Google Group]] or email me directly).
386
+
387
+ [1] I even reported a few bugs and suggested some fixes in the original
388
+ [[http://marijn.haverbeke.nl/parse-js/][parse-js]] library, and Marijn pushed fixes literally in minutes.
389
+
390
+ ** Links
391
+
392
+ - Project at GitHub: [[http://github.com/mishoo/UglifyJS][http://github.com/mishoo/UglifyJS]]
393
+ - Google Group: [[http://groups.google.com/group/uglifyjs][http://groups.google.com/group/uglifyjs]]
394
+ - Common Lisp JS parser: [[http://marijn.haverbeke.nl/parse-js/][http://marijn.haverbeke.nl/parse-js/]]
395
+ - JS-to-Lisp compiler: [[http://github.com/marijnh/js][http://github.com/marijnh/js]]
396
+ - Common Lisp JS uglifier: [[http://github.com/mishoo/cl-uglify-js][http://github.com/mishoo/cl-uglify-js]]
397
+
398
+ ** License
399
+
400
+ UglifyJS is released under the BSD license:
401
+
402
+ #+BEGIN_EXAMPLE
403
+ Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
404
+ Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
405
+
406
+ Redistribution and use in source and binary forms, with or without
407
+ modification, are permitted provided that the following conditions
408
+ are met:
409
+
410
+ * Redistributions of source code must retain the above
411
+ copyright notice, this list of conditions and the following
412
+ disclaimer.
413
+
414
+ * Redistributions in binary form must reproduce the above
415
+ copyright notice, this list of conditions and the following
416
+ disclaimer in the documentation and/or other materials
417
+ provided with the distribution.
418
+
419
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
420
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
421
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
422
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
423
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
424
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
425
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
426
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
427
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
428
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
429
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
430
+ SUCH DAMAGE.
431
+ #+END_EXAMPLE
@@ -0,0 +1,212 @@
1
+ #! /usr/bin/env node
2
+ // -*- js -*-
3
+
4
+ global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
5
+ var fs = require("fs");
6
+ var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js
7
+ jsp = uglify.parser,
8
+ pro = uglify.uglify;
9
+
10
+ var options = {
11
+ ast: false,
12
+ mangle: true,
13
+ mangle_toplevel: false,
14
+ squeeze: true,
15
+ make_seqs: true,
16
+ dead_code: true,
17
+ verbose: false,
18
+ show_copyright: true,
19
+ out_same_file: false,
20
+ max_line_length: 32 * 1024,
21
+ unsafe: false,
22
+ reserved_names: null,
23
+ codegen_options: {
24
+ ascii_only: false,
25
+ beautify: false,
26
+ indent_level: 4,
27
+ indent_start: 0,
28
+ quote_keys: false,
29
+ space_colon: false
30
+ },
31
+ output: true // stdout
32
+ };
33
+
34
+ var args = jsp.slice(process.argv, 2);
35
+ var filename;
36
+
37
+ out: while (args.length > 0) {
38
+ var v = args.shift();
39
+ switch (v) {
40
+ case "-b":
41
+ case "--beautify":
42
+ options.codegen_options.beautify = true;
43
+ break;
44
+ case "-i":
45
+ case "--indent":
46
+ options.codegen_options.indent_level = args.shift();
47
+ break;
48
+ case "-q":
49
+ case "--quote-keys":
50
+ options.codegen_options.quote_keys = true;
51
+ break;
52
+ case "-mt":
53
+ case "--mangle-toplevel":
54
+ options.mangle_toplevel = true;
55
+ break;
56
+ case "--no-mangle":
57
+ case "-nm":
58
+ options.mangle = false;
59
+ break;
60
+ case "--no-squeeze":
61
+ case "-ns":
62
+ options.squeeze = false;
63
+ break;
64
+ case "--no-seqs":
65
+ options.make_seqs = false;
66
+ break;
67
+ case "--no-dead-code":
68
+ options.dead_code = false;
69
+ break;
70
+ case "--no-copyright":
71
+ case "-nc":
72
+ options.show_copyright = false;
73
+ break;
74
+ case "-o":
75
+ case "--output":
76
+ options.output = args.shift();
77
+ break;
78
+ case "--overwrite":
79
+ options.out_same_file = true;
80
+ break;
81
+ case "-v":
82
+ case "--verbose":
83
+ options.verbose = true;
84
+ break;
85
+ case "--ast":
86
+ options.ast = true;
87
+ break;
88
+ case "--unsafe":
89
+ options.unsafe = true;
90
+ break;
91
+ case "--max-line-len":
92
+ options.max_line_length = parseInt(args.shift(), 10);
93
+ break;
94
+ case "--reserved-names":
95
+ options.reserved_names = args.shift().split(",");
96
+ break;
97
+ case "--ascii":
98
+ options.codegen_options.ascii_only = true;
99
+ break;
100
+ default:
101
+ filename = v;
102
+ break out;
103
+ }
104
+ }
105
+
106
+ if (options.verbose) {
107
+ pro.set_logger(function(msg){
108
+ sys.debug(msg);
109
+ });
110
+ }
111
+
112
+ jsp.set_logger(function(msg){
113
+ sys.debug(msg);
114
+ });
115
+
116
+ if (filename) {
117
+ fs.readFile(filename, "utf8", function(err, text){
118
+ if (err) throw err;
119
+ output(squeeze_it(text));
120
+ });
121
+ } else {
122
+ var stdin = process.openStdin();
123
+ stdin.setEncoding("utf8");
124
+ var text = "";
125
+ stdin.on("data", function(chunk){
126
+ text += chunk;
127
+ });
128
+ stdin.on("end", function() {
129
+ output(squeeze_it(text));
130
+ });
131
+ }
132
+
133
+ function output(text) {
134
+ var out;
135
+ if (options.out_same_file && filename)
136
+ options.output = filename;
137
+ if (options.output === true) {
138
+ out = process.stdout;
139
+ } else {
140
+ out = fs.createWriteStream(options.output, {
141
+ flags: "w",
142
+ encoding: "utf8",
143
+ mode: 0644
144
+ });
145
+ }
146
+ out.write(text);
147
+ if (options.output !== true) {
148
+ out.end();
149
+ }
150
+ };
151
+
152
+ // --------- main ends here.
153
+
154
+ function show_copyright(comments) {
155
+ var ret = "";
156
+ for (var i = 0; i < comments.length; ++i) {
157
+ var c = comments[i];
158
+ if (c.type == "comment1") {
159
+ ret += "//" + c.value + "\n";
160
+ } else {
161
+ ret += "/*" + c.value + "*/";
162
+ }
163
+ }
164
+ return ret;
165
+ };
166
+
167
+ function squeeze_it(code) {
168
+ var result = "";
169
+ if (options.show_copyright) {
170
+ var tok = jsp.tokenizer(code), c;
171
+ c = tok();
172
+ result += show_copyright(c.comments_before);
173
+ }
174
+ try {
175
+ var ast = time_it("parse", function(){ return jsp.parse(code); });
176
+ if (options.mangle) ast = time_it("mangle", function(){
177
+ return pro.ast_mangle(ast, {
178
+ toplevel: options.mangle_toplevel,
179
+ except: options.reserved_names
180
+ });
181
+ });
182
+ if (options.squeeze) ast = time_it("squeeze", function(){
183
+ ast = pro.ast_squeeze(ast, {
184
+ make_seqs : options.make_seqs,
185
+ dead_code : options.dead_code,
186
+ keep_comps : !options.unsafe
187
+ });
188
+ if (options.unsafe)
189
+ ast = pro.ast_squeeze_more(ast);
190
+ return ast;
191
+ });
192
+ if (options.ast)
193
+ return sys.inspect(ast, null, null);
194
+ result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
195
+ if (!options.codegen_options.beautify && options.max_line_length) {
196
+ result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
197
+ }
198
+ return result;
199
+ } catch(ex) {
200
+ sys.debug(ex.stack);
201
+ sys.debug(sys.inspect(ex));
202
+ sys.debug(JSON.stringify(ex));
203
+ }
204
+ };
205
+
206
+ function time_it(name, cont) {
207
+ if (!options.verbose)
208
+ return cont();
209
+ var t1 = new Date().getTime();
210
+ try { return cont(); }
211
+ finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
212
+ };