暫無描述

uglify.js 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * grunt-contrib-uglify
  3. * https://gruntjs.com/
  4. *
  5. * Copyright (c) 2014 "Cowboy" Ben Alman, contributors
  6. * Licensed under the MIT license.
  7. */
  8. 'use strict';
  9. // External libs.
  10. var path = require('path');
  11. var fs = require('fs');
  12. var UglifyJS = require('uglify-js');
  13. var _ = require('lodash');
  14. var uriPath = require('uri-path');
  15. exports.init = function(grunt) {
  16. var exports = {};
  17. // Minify with UglifyJS.
  18. // From https://github.com/mishoo/UglifyJS2
  19. // API docs at http://lisperator.net/uglifyjs/
  20. exports.minify = function(files, dest, options) {
  21. options = options || {};
  22. grunt.verbose.write('Minifying with UglifyJS...');
  23. var topLevel = null;
  24. var totalCode = '';
  25. var sourcesContent = {};
  26. var outputOptions = getOutputOptions(options, dest);
  27. var output = UglifyJS.OutputStream(outputOptions);
  28. // Grab and parse all source files
  29. files.forEach(function(file){
  30. var code = grunt.file.read(file);
  31. totalCode += code;
  32. // The src file name must be relative to the source map for things to work
  33. var basename = path.basename(file);
  34. var fileDir = path.dirname(file);
  35. var sourceMapDir = path.dirname(options.generatedSourceMapName);
  36. var relativePath = path.relative(sourceMapDir, fileDir);
  37. var pathPrefix = relativePath ? (relativePath+path.sep) : '';
  38. // Convert paths to use forward slashes for sourcemap use in the browser
  39. file = uriPath(pathPrefix + basename);
  40. sourcesContent[file] = code;
  41. topLevel = UglifyJS.parse(code, {
  42. filename: file,
  43. toplevel: topLevel,
  44. expression: options.expression
  45. });
  46. });
  47. // Wrap code in a common js wrapper.
  48. if (options.wrap) {
  49. topLevel = topLevel.wrap_commonjs(options.wrap, options.exportAll);
  50. }
  51. // Wrap code in closure with configurable arguments/parameters list.
  52. if (options.enclose) {
  53. var argParamList = _.map(options.enclose, function(val, key) {
  54. return key + ':' + val;
  55. });
  56. topLevel = topLevel.wrap_enclose(argParamList);
  57. }
  58. // Need to call this before we mangle or compress,
  59. // and call after any compression or ast altering
  60. if (options.expression === false) {
  61. topLevel.figure_out_scope();
  62. }
  63. if (options.compress !== false) {
  64. if (options.compress.warnings !== true) {
  65. options.compress.warnings = false;
  66. }
  67. var compressor = UglifyJS.Compressor(options.compress);
  68. topLevel = topLevel.transform(compressor);
  69. // Need to figure out scope again after source being altered
  70. topLevel.figure_out_scope();
  71. }
  72. if (options.mangle !== false) {
  73. // disabled due to:
  74. // 1) preserve stable name mangling
  75. // 2) it increases gzipped file size, see https://github.com/mishoo/UglifyJS2#mangler-options
  76. // // compute_char_frequency optimizes names for compression
  77. // topLevel.compute_char_frequency(options.mangle);
  78. // Requires previous call to figure_out_scope
  79. // and should always be called after compressor transform
  80. topLevel.mangle_names(options.mangle);
  81. }
  82. if (options.sourceMap && options.sourceMapIncludeSources) {
  83. for (var file in sourcesContent) {
  84. if (sourcesContent.hasOwnProperty(file)) {
  85. outputOptions.source_map.get().setSourceContent(file, sourcesContent[file]);
  86. }
  87. }
  88. }
  89. // Print the ast to OutputStream
  90. topLevel.print(output);
  91. var min = output.get();
  92. // Add the source map reference to the end of the file
  93. if (options.sourceMap) {
  94. // Set all paths to forward slashes for use in the browser
  95. min += "\n//# sourceMappingURL=" + uriPath(options.destToSourceMap);
  96. }
  97. var result = {
  98. max: totalCode,
  99. min: min,
  100. sourceMap: outputOptions.source_map
  101. };
  102. grunt.verbose.ok();
  103. return result;
  104. };
  105. var getOutputOptions = function(options, dest) {
  106. var outputOptions = {
  107. beautify: false,
  108. source_map: null
  109. };
  110. if (options.preserveComments) {
  111. if (options.preserveComments === 'all' || options.preserveComments === true) {
  112. // preserve all the comments we can
  113. outputOptions.comments = true;
  114. } else if (options.preserveComments === 'some') {
  115. // preserve comments with directives or that start with a bang (!)
  116. outputOptions.comments = /^!|@preserve|@license|@cc_on/i;
  117. } else if (_.isFunction(options.preserveComments)) {
  118. // support custom functions passed in
  119. outputOptions.comments = options.preserveComments;
  120. }
  121. }
  122. if (options.banner && options.sourceMap) {
  123. outputOptions.preamble = options.banner;
  124. }
  125. if (options.beautify) {
  126. if (_.isObject(options.beautify)) {
  127. // beautify options sent as an object are merged
  128. // with outputOptions and passed to the OutputStream
  129. _.assign(outputOptions, options.beautify);
  130. } else {
  131. outputOptions.beautify = true;
  132. }
  133. }
  134. if (options.sourceMap) {
  135. var destBasename = path.basename(dest);
  136. var destPath = path.dirname(dest);
  137. var sourceMapIn;
  138. if (options.sourceMapIn) {
  139. sourceMapIn = grunt.file.readJSON(options.sourceMapIn);
  140. }
  141. outputOptions.source_map = UglifyJS.SourceMap({
  142. file: destBasename,
  143. orig: sourceMapIn
  144. });
  145. if (options.sourceMapIncludeSources && sourceMapIn && sourceMapIn.sourcesContent) {
  146. sourceMapIn.sourcesContent.forEach(function(content, idx) {
  147. outputOptions.source_map.get().setSourceContent(sourceMapIn.sources[idx], content);
  148. });
  149. }
  150. }
  151. if (options.indentLevel !== undefined) {
  152. outputOptions.indent_level = options.indentLevel;
  153. }
  154. if (options.maxLineLen !== undefined) {
  155. outputOptions.max_line_len = options.maxLineLen;
  156. }
  157. if (options.ASCIIOnly !== undefined) {
  158. outputOptions.ascii_only = options.ASCIIOnly;
  159. }
  160. return outputOptions;
  161. };
  162. return exports;
  163. };