uglifier.rb 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # encoding: UTF-8
  2. require "execjs"
  3. require "multi_json"
  4. class Uglifier
  5. Error = ExecJS::Error
  6. # MultiJson.engine = :json_gem
  7. # Default options for compilation
  8. DEFAULTS = {
  9. :mangle => true, # Mangle variable and function names, use :vars to skip function mangling
  10. :toplevel => false, # Mangle top-level variable names
  11. :except => ["$super"], # Variable names to be excluded from mangling
  12. :max_line_length => 32 * 1024, # Maximum line length
  13. :squeeze => true, # Squeeze code resulting in smaller, but less-readable code
  14. :seqs => true, # Reduce consecutive statements in blocks into single statement
  15. :dead_code => true, # Remove dead code (e.g. after return)
  16. :lift_vars => false, # Lift all var declarations at the start of the scope
  17. :unsafe => false, # Optimizations known to be unsafe in some situations
  18. :copyright => true, # Show copyright message
  19. :ascii_only => false, # Encode non-ASCII characters as Unicode code points
  20. :inline_script => false, # Escape </script
  21. :quote_keys => false, # Quote keys in object literals
  22. :beautify => false, # Ouput indented code
  23. :beautify_options => {
  24. :indent_level => 4,
  25. :indent_start => 0,
  26. :space_colon => false
  27. }
  28. }
  29. SourcePath = File.expand_path("../uglify.js", __FILE__)
  30. ES5FallbackPath = File.expand_path("../es5.js", __FILE__)
  31. # Minifies JavaScript code using implicit context.
  32. #
  33. # source should be a String or IO object containing valid JavaScript.
  34. # options contain optional overrides to Uglifier::DEFAULTS
  35. #
  36. # Returns minified code as String
  37. def self.compile(source, options = {})
  38. self.new(options).compile(source)
  39. end
  40. # Initialize new context for Uglifier with given options
  41. #
  42. # options - Hash of options to override Uglifier::DEFAULTS
  43. def initialize(options = {})
  44. @options = DEFAULTS.merge(options)
  45. @context = ExecJS.compile(File.open(ES5FallbackPath, "r:UTF-8").read + File.open(SourcePath, "r:UTF-8").read)
  46. end
  47. # Minifies JavaScript code
  48. #
  49. # source should be a String or IO object containing valid JavaScript.
  50. #
  51. # Returns minified code as String
  52. def compile(source)
  53. source = source.respond_to?(:read) ? source.read : source.to_s
  54. js = []
  55. js << "var result = '';"
  56. js << "var source = #{MultiJson.dump(source)};"
  57. js << "var ast = UglifyJS.parser.parse(source);"
  58. if @options[:lift_vars]
  59. js << "ast = UglifyJS.uglify.ast_lift_variables(ast);"
  60. end
  61. if @options[:copyright]
  62. js << <<-JS
  63. var comments = UglifyJS.parser.tokenizer(source)().comments_before;
  64. for (var i = 0; i < comments.length; i++) {
  65. var c = comments[i];
  66. result += (c.type == "comment1") ? "//"+c.value+"\\n" : "/*"+c.value+"*/\\n";
  67. }
  68. JS
  69. end
  70. js << "ast = UglifyJS.uglify.ast_mangle(ast, #{MultiJson.dump(mangle_options)});"
  71. if @options[:squeeze]
  72. js << "ast = UglifyJS.uglify.ast_squeeze(ast, #{MultiJson.dump(squeeze_options)});"
  73. end
  74. if @options[:unsafe]
  75. js << "ast = UglifyJS.uglify.ast_squeeze_more(ast);"
  76. end
  77. js << "result += UglifyJS.uglify.gen_code(ast, #{MultiJson.dump(gen_code_options)});"
  78. if !@options[:beautify] && @options[:max_line_length]
  79. js << "result = UglifyJS.uglify.split_lines(result, #{@options[:max_line_length].to_i})"
  80. end
  81. js << "return result + ';';"
  82. @context.exec js.join("\n")
  83. end
  84. alias_method :compress, :compile
  85. private
  86. def mangle_options
  87. {
  88. "mangle" => @options[:mangle],
  89. "toplevel" => @options[:toplevel],
  90. "defines" => {},
  91. "except" => @options[:except],
  92. "no_functions" => @options[:mangle] == :vars
  93. }
  94. end
  95. def squeeze_options
  96. {
  97. "make_seqs" => @options[:seqs],
  98. "dead_code" => @options[:dead_code],
  99. "keep_comps" => !@options[:unsafe]
  100. }
  101. end
  102. def gen_code_options
  103. options = {
  104. :ascii_only => @options[:ascii_only],
  105. :inline_script => @options[:inline_script],
  106. :quote_keys => @options[:quote_keys]
  107. }
  108. if @options[:beautify]
  109. options.merge(:beautify => true).merge(@options[:beautify_options])
  110. else
  111. options
  112. end
  113. end
  114. end