uglify.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * grunt-contrib-uglify
  3. * http://gruntjs.com/
  4. *
  5. * Copyright (c) 2016 "Cowboy" Ben Alman, contributors
  6. * Licensed under the MIT license.
  7. */
  8. 'use strict';
  9. var path = require('path');
  10. var chalk = require('chalk');
  11. var maxmin = require('maxmin');
  12. var uriPath = require('uri-path');
  13. var err;
  14. // Return the relative path from file1 => file2
  15. function relativePath(file1, file2) {
  16. var file1Dirname = path.dirname(file1);
  17. var file2Dirname = path.dirname(file2);
  18. if (file1Dirname !== file2Dirname) {
  19. return path.relative(file1Dirname, file2Dirname);
  20. }
  21. return '';
  22. }
  23. // Converts \r\n to \n
  24. function normalizeLf(string) {
  25. return string.replace(/\r\n/g, '\n');
  26. }
  27. module.exports = function(grunt) {
  28. // Internal lib.
  29. var uglify = require('./lib/uglify').init(grunt);
  30. var getAvailableFiles = function (filesArray) {
  31. return filesArray.filter(function (filepath) {
  32. if (!grunt.file.exists(filepath)) {
  33. grunt.log.warn('Source file ' + chalk.cyan(filepath) + ' not found');
  34. return false;
  35. }
  36. return true;
  37. });
  38. };
  39. grunt.registerMultiTask('uglify', 'Minify files with UglifyJS.', function() {
  40. // Merge task-specific and/or target-specific options with these defaults.
  41. var options = this.options({
  42. banner: '',
  43. footer: '',
  44. compress: {},
  45. mangle: {},
  46. beautify: false,
  47. report: 'min',
  48. ie8: false
  49. });
  50. var footer = normalizeLf(options.footer);
  51. var mapNameGenerator, mapInNameGenerator;
  52. var created = {
  53. maps: 0,
  54. files: 0
  55. };
  56. var size = {
  57. before: 0,
  58. after: 0
  59. };
  60. var generateSourceMapURL = options.sourceMap && !options.sourceMap.url;
  61. var generateSourceMapFilename = options.sourceMap && !options.sourceMap.filename;
  62. // function to get the name of the sourceMap
  63. if (typeof options.sourceMapName === 'function') {
  64. mapNameGenerator = options.sourceMapName;
  65. }
  66. // Iterate over all src-dest file pairs.
  67. this.files.forEach(function (f) {
  68. var availableFiles = getAvailableFiles(f.src);
  69. if (availableFiles.length === 0) {
  70. grunt.log.warn('Destination ' + chalk.cyan(f.dest) + ' not written because src files were empty.');
  71. return;
  72. }
  73. // function to get the name of the sourceMapIn file
  74. if (typeof options.sourceMapIn === 'function') {
  75. if (availableFiles.length !== 1) {
  76. grunt.fail.warn('Cannot generate `sourceMapIn` for multiple source files.');
  77. }
  78. mapInNameGenerator = options.sourceMapIn;
  79. }
  80. // dynamically create destination sourcemap name
  81. if (mapNameGenerator) {
  82. try {
  83. options.generatedSourceMapName = mapNameGenerator(f.dest);
  84. } catch (e) {
  85. err = new Error('SourceMap failed.');
  86. err.origError = e;
  87. grunt.fail.warn(err);
  88. }
  89. // If no name is passed append .map to the filename
  90. } else if (!options.sourceMapName) {
  91. options.generatedSourceMapName = f.dest + '.map';
  92. } else {
  93. options.generatedSourceMapName = options.sourceMapName;
  94. }
  95. // Dynamically create incoming sourcemap names
  96. if (mapInNameGenerator) {
  97. try {
  98. options.sourceMapIn = mapInNameGenerator(availableFiles[0]);
  99. } catch (e) {
  100. err = new Error('SourceMapInName failed.');
  101. err.origError = e;
  102. grunt.fail.warn(err);
  103. }
  104. }
  105. if (options.sourceMap) {
  106. if (typeof options.sourceMap !== 'object') {
  107. options.sourceMap = {};
  108. }
  109. if (options.sourceMapIn) {
  110. options.sourceMap.content = grunt.file.read(options.sourceMapIn);
  111. }
  112. // Calculate the path from the dest file to the sourcemap for sourceMap.url
  113. if (generateSourceMapURL) {
  114. if (generateSourceMapFilename) {
  115. options.sourceMap.filename = path.basename(f.dest);
  116. }
  117. var destToSourceMapPath = relativePath(f.dest, options.generatedSourceMapName);
  118. var sourceMapBasename = path.basename(options.generatedSourceMapName);
  119. options.sourceMap.url = uriPath(path.join(destToSourceMapPath, sourceMapBasename));
  120. }
  121. }
  122. // Minify files, warn and fail on error.
  123. var result;
  124. try {
  125. result = uglify.minify(availableFiles, f.dest, options);
  126. } catch (e) {
  127. console.log(e);
  128. err = new Error('Uglification failed.');
  129. if (e.message) {
  130. err.message += '\n' + e.message + '. \n';
  131. if (e.line) {
  132. err.message += 'Line ' + e.line + ' in ' + availableFiles + '\n';
  133. }
  134. }
  135. err.origError = e;
  136. grunt.log.warn('Uglifying source ' + chalk.cyan(availableFiles) + ' failed.');
  137. grunt.fail.warn(err);
  138. }
  139. // Concat minified source + footer
  140. var output = result.min + footer;
  141. var unCompiledJSString = availableFiles.map(function (file) {
  142. return grunt.file.read(file);
  143. }).join('');
  144. // Write the destination file.
  145. grunt.file.write(f.dest, output);
  146. size.before += unCompiledJSString.length;
  147. // Write source map
  148. if (options.sourceMap) {
  149. grunt.file.write(options.generatedSourceMapName, result.sourceMap);
  150. grunt.verbose.writeln('File ' + chalk.cyan(options.generatedSourceMapName) + ' created (source map).');
  151. created.maps++;
  152. }
  153. var outputSize = maxmin(result.max, output, options.report === 'gzip');
  154. grunt.verbose.writeln('File ' + chalk.cyan(f.dest) + ' created: ' + chalk.dim(outputSize));
  155. created.files++;
  156. size.after += output.length;
  157. });
  158. if (created.maps > 0) {
  159. grunt.log.ok(created.maps + ' source' + grunt.util.pluralize(created.maps, 'map/maps') + ' created.');
  160. }
  161. if (created.files > 0) {
  162. grunt.log.ok(created.files + ' ' + grunt.util.pluralize(this.files.length, 'file/files') + ' created ' + chalk.dim(maxmin(size.before, size.after)));
  163. } else {
  164. grunt.log.warn('No files created.');
  165. }
  166. });
  167. };