├── .gitignore ├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── test ├── fixtures │ ├── expected │ │ ├── compress_mangle.js │ │ ├── compress_mangle_except.js │ │ ├── compress.js │ │ ├── sourcemaps_multiple2.js │ │ ├── compress_mangle_banner.js │ │ ├── sourcemap_basic.js │ │ ├── compress_mangle_beautify.js │ │ ├── sourcemap_customName.js │ │ ├── sourcemaps_multiple2_fnName.js │ │ ├── sourcemaps_multiple1.js │ │ ├── sourcemap_functionName.js │ │ ├── sourcemap_customDir.js │ │ ├── sourcemaps_multiple1_fnName.js │ │ ├── wrap.js │ │ ├── sourcemaps_multiple2.map │ │ ├── multifile.js │ │ ├── sourcemaps_multiple2_fnName.js.fn.map │ │ ├── comments.js │ │ ├── enclose.js │ │ ├── sourcemap_basic.map │ │ ├── source_map_custom_name │ │ ├── sourcemaps_multiple1.map │ │ ├── sourcemap_functionName.js.fn.map │ │ ├── sourcemaps_multiple1_fnName.js.fn.map │ │ ├── deep │ │ │ └── directory │ │ │ │ └── location │ │ │ │ └── source_map.js.map │ │ ├── exportAll.js │ │ ├── sourcemap_sources.map │ │ ├── sourcemapin.map │ │ └── sourcemapin.js │ └── src │ │ ├── simple.js │ │ ├── comments.js │ │ ├── simple2.coffee │ │ ├── simple2.map │ │ └── simple2.js └── uglify_test.js ├── docs ├── uglify-overview.md ├── uglify-options.md └── uglify-examples.md ├── .jshintrc ├── LICENSE-MIT ├── package.json ├── CHANGELOG ├── tasks ├── uglify.js └── lib │ └── uglify.js ├── Gruntfile.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | tmp 4 | .idea 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.8" 4 | - "0.10" 5 | before_script: 6 | - npm install -g grunt-cli -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | "Cowboy" Ben Alman (http://benalman.com) 2 | Tyler Kellen (http://goingslowly.com) 3 | Jarrod Overson (http://jarrodoverson.com) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please see the [Contributing to grunt](http://gruntjs.com/contributing) guide for information on contributing to this project. 2 | -------------------------------------------------------------------------------- /test/fixtures/expected/compress_mangle.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); -------------------------------------------------------------------------------- /docs/uglify-overview.md: -------------------------------------------------------------------------------- 1 | Task targets, files and options may be specified according to the grunt [Configuring tasks](http://gruntjs.com/configuring-tasks) guide. -------------------------------------------------------------------------------- /test/fixtures/expected/compress_mangle_except.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(argumentC,a){return longNameA+longNameB+argumentC+a}var longNameA=1,longNameB=2,result=longFunctionC(3,4); -------------------------------------------------------------------------------- /test/fixtures/expected/compress.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(argumentC,argumentD){return longNameA+longNameB+argumentC+argumentD}var longNameA=1,longNameB=2,result=longFunctionC(3,4); -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple2.js: -------------------------------------------------------------------------------- 1 | function foo(){return 42}function bar(){return 2*foo()}function baz(){return bar()*bar()} 2 | //# sourceMappingURL=sourcemaps_multiple2.map -------------------------------------------------------------------------------- /test/fixtures/expected/compress_mangle_banner.js: -------------------------------------------------------------------------------- 1 | // banner without sourcemap 2 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_basic.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); 2 | //# sourceMappingURL=sourcemap_basic.map -------------------------------------------------------------------------------- /test/fixtures/expected/compress_mangle_beautify.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a, b) { 2 | return longNameA + longNameB + a + b; 3 | } 4 | 5 | var longNameA = 1, longNameB = 2, result = longFunctionC(3, 4); -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_customName.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); 2 | //# sourceMappingURL=source_map_custom_name -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple2_fnName.js: -------------------------------------------------------------------------------- 1 | function foo(){return 42}function bar(){return 2*foo()}function baz(){return bar()*bar()} 2 | //# sourceMappingURL=sourcemaps_multiple2_fnName.js.fn.map -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple1.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); 2 | //# sourceMappingURL=sourcemaps_multiple1.map -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_functionName.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); 2 | //# sourceMappingURL=sourcemap_functionName.js.fn.map -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_customDir.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); 2 | //# sourceMappingURL=deep/directory/location/source_map.js.map -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple1_fnName.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(a,b){return longNameA+longNameB+a+b}var longNameA=1,longNameB=2,result=longFunctionC(3,4); 2 | //# sourceMappingURL=sourcemaps_multiple1_fnName.js.fn.map -------------------------------------------------------------------------------- /test/fixtures/expected/wrap.js: -------------------------------------------------------------------------------- 1 | !function(exports,global){function longFunctionC(argumentC,argumentD){return longNameA+longNameB+argumentC+argumentD}global.testExport=exports;{var longNameA=1,longNameB=2;longFunctionC(3,4)}}({},function(){return this}()); -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple2.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemaps_multiple2.js","sources":["../test/fixtures/src/comments.js"],"names":["foo","bar","baz"],"mappings":"AAGA,QAASA,OACP,MAAO,IAIT,QAASC,OACP,MAAa,GAAND,MAQT,QAASE,OACP,MAAOD,OAAMA"} -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "node": true 13 | } 14 | -------------------------------------------------------------------------------- /test/fixtures/expected/multifile.js: -------------------------------------------------------------------------------- 1 | function longFunctionC(argumentC,argumentD){return longNameA+longNameB+argumentC+argumentD}function foo(){return 42}function bar(){return 2*foo()}function baz(){return bar()*bar()}var longNameA=1,longNameB=2,result=longFunctionC(3,4); -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple2_fnName.js.fn.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemaps_multiple2_fnName.js","sources":["../test/fixtures/src/comments.js"],"names":["foo","bar","baz"],"mappings":"AAGA,QAASA,OACP,MAAO,IAIT,QAASC,OACP,MAAa,GAAND,MAQT,QAASE,OACP,MAAOD,OAAMA"} -------------------------------------------------------------------------------- /test/fixtures/expected/comments.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * I am a comment 3 | */ 4 | function foo(){return 42}// @preserve preserve 5 | // @license license 6 | function bar(){return 2*foo()}/* @preserve 7 | * multiline preserve 8 | */ 9 | /* @license 10 | * multiline license 11 | */ 12 | function baz(){return bar()*bar()} -------------------------------------------------------------------------------- /test/fixtures/expected/enclose.js: -------------------------------------------------------------------------------- 1 | (function(paramA, paramB) { 2 | var longNameA = 1; 3 | var longNameB = 2; 4 | function longFunctionC(argumentC, argumentD) { 5 | return longNameA + longNameB + argumentC + argumentD; 6 | } 7 | var result = longFunctionC(3, 4); 8 | })(window.argA, window.argB); -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_basic.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemap_basic.js","sources":["../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE"} -------------------------------------------------------------------------------- /test/fixtures/expected/source_map_custom_name: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemap_customName.js","sources":["../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE"} -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple1.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemaps_multiple1.js","sources":["../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE"} -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_functionName.js.fn.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemap_functionName.js","sources":["../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE"} -------------------------------------------------------------------------------- /test/fixtures/src/simple.js: -------------------------------------------------------------------------------- 1 | 2 | // Hello world, I'm a comment! 3 | 4 | var longNameA = 1; 5 | 6 | var longNameB = 2; 7 | 8 | function longFunctionC(argumentC,argumentD) { 9 | return longNameA + longNameB + argumentC + argumentD; 10 | } 11 | 12 | var result = longFunctionC(3,4); 13 | 14 | /*! I might be preserved, yay! */ 15 | 16 | -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemaps_multiple1_fnName.js.fn.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemaps_multiple1_fnName.js","sources":["../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE"} -------------------------------------------------------------------------------- /test/fixtures/expected/deep/directory/location/source_map.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemap_customDir.js","sources":["../../../../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE"} -------------------------------------------------------------------------------- /test/fixtures/expected/exportAll.js: -------------------------------------------------------------------------------- 1 | !function(exports,global){function longFunctionC(argumentC,argumentD){return longNameA+longNameB+argumentC+argumentD}global.testExport=exports;var longNameA=1,longNameB=2,result=longFunctionC(3,4);exports.longNameA=longNameA,exports.longNameB=longNameB,exports.longFunctionC=longFunctionC,exports.result=result}({},function(){return this}()); -------------------------------------------------------------------------------- /test/fixtures/src/comments.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * I am a comment 3 | */ 4 | function foo() { 5 | return 42; 6 | } 7 | // @preserve preserve 8 | // @license license 9 | function bar() { 10 | return foo()*2; 11 | } 12 | /* @preserve 13 | * multiline preserve 14 | */ 15 | /* @license 16 | * multiline license 17 | */ 18 | function baz() { 19 | return bar()*bar(); 20 | } 21 | // end - not preserved -------------------------------------------------------------------------------- /test/fixtures/src/simple2.coffee: -------------------------------------------------------------------------------- 1 | # Assignment: 2 | number = 42 3 | opposite = true 4 | 5 | # Conditions: 6 | number = -42 if opposite 7 | 8 | # Functions: 9 | square = (x) -> x * x 10 | 11 | # Arrays: 12 | list = [1, 2, 3, 4, 5] 13 | 14 | # Objects: 15 | math = 16 | root: Math.sqrt 17 | square: square 18 | cube: (x) -> x * square x 19 | 20 | # Splats: 21 | race = (winner, runners...) -> 22 | print winner, runners 23 | 24 | # Existence: 25 | alert "I knew it!" if elvis? 26 | 27 | # Array comprehensions: 28 | cubes = (math.cube num for num in list) -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemap_sources.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemap_sources.js","sources":["../test/fixtures/src/simple.js"],"names":["longFunctionC","argumentC","argumentD","longNameA","longNameB","result"],"mappings":"AAOA,QAASA,eAAcC,EAAUC,GAC/B,MAAOC,WAAYC,UAAYH,EAAYC,EAL7C,GAAIC,WAAY,EAEZC,UAAY,EAMZC,OAASL,cAAc,EAAE","sourcesContent":["\n// Hello world, I'm a comment!\n\nvar longNameA = 1;\n\nvar longNameB = 2;\n\nfunction longFunctionC(argumentC,argumentD) {\n return longNameA + longNameB + argumentC + argumentD;\n}\n\nvar result = longFunctionC(3,4);\n\n/*! I might be preserved, yay! */\n\n"]} -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemapin.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sourcemapin.js","sources":["../test/fixtures/src/simple2.coffee"],"names":[],"mappings":";;IAAC,YAAA,GAAA,OAAA,KAAA,KAAA,OAAA,SAAA,KAAA,MACA,QAAW,GACX,UAAW,EAGK,WAAhB,OAAS,KAGT,OAAS,SAAA,GAAA,MAAO,GAAI,GAGpB,MAAQ,EAAG,EAAG,EAAG,EAAG,GAGpB,MACG,KAAQ,KAAI,KACb,OAAQ,OACR,KAAQ,SAAA,GAAA,MAAO,GAAI,OAAO,KAG1B,KAAO,SAAA,OAAA,SAAS,MAAA,SAAA,GAAA,UAAA,UAAA,MAAA,KAAA,UAAA,MACf,MAAM,OAAQ,UAGf,mBAAsB,QAAA,MAAA,OAAtB,MAAM,cAGR,MAAA,SAAA,QAAS,IAAA,GAAT,KAAS,GAAA,EAAA,QAAyB,KAAA,OAAzB,QAAA,KAAA,GAAkB,IAAO,KAAA,IAAA,OAAA,KAAzB,KAAI,KAAM,KAAV,OAAA,SAAA,KAAA,UAAA,KAAA"} -------------------------------------------------------------------------------- /test/fixtures/expected/sourcemapin.js: -------------------------------------------------------------------------------- 1 | // Hello World 2 | 3 | void function(){var cubes,list,math,number,opposite,race,square;number=42,opposite=!0,opposite&&(number=-42),square=function(x){return x*x},list=[1,2,3,4,5],math={root:Math.sqrt,square:square,cube:function(x){return x*square(x)}},race=function(winner,runners){return runners=2<=arguments.length?[].slice.call(arguments,1):[],print(winner,runners)},"undefined"!=typeof elvis&&null!=elvis&&alert("I knew it!"),cubes=function(accum$){for(var num,i$=0,length$=list.length;length$>i$;++i$)num=list[i$],accum$.push(math.cube(num));return accum$}.call(this,[])}.call(this); 4 | //# sourceMappingURL=sourcemapin.map -------------------------------------------------------------------------------- /test/fixtures/src/simple2.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"test/fixtures/src/simple2.coffee","sources":["../test/fixtures/src/simple2.coffee"],"names":[],"mappings":"AAAC;;;EACA,MAAA,GAAW;EACX,QAAA,GAAW;EAGX,IAAgB,QAAhB,CAAA;AAAA,IAAA,MAAA,GAAS,CAAC;EAGV,MAAA,GAAS,SAAA,CAAA,CAAA,CAAA;WAAO,CAAA,CAAA,CAAA,CAAI;;EAGpB,IAAA,GAAO,CAAA;AAAA,IAAC,CAAD;AAAA,IAAI,CAAJ;AAAA,IAAO,CAAP;AAAA,IAAU,CAAV;AAAA,IAAa,CAAb;AAAA,EAAA;EAGP,IAAA,GACG,CAAA;AAAA,IAAA,IAAA,EAAQ,IAAI,KAAZ;AAAA,IACD,MAAA,EAAQ,MADP;AAAA,IAED,IAAA,EAAQ,SAAA,CAAA,CAAA,CAAA;aAAO,CAAA,CAAA,CAAA,CAAI,MAAA,CAAO,CAAP;KAFlB;AAAA,EAAA;EAKD,IAAA,GAAO,SAAA,CAAA,MAAA,EAAA,OAAA,CAAA;IAAS;WACf,KAAA,CAAM,MAAN,EAAc,OAAd;;EAGD,2BAAsB,KAAA,CAAA,EAAA,SAAA,KAAtB,CAAA;AAAA,IAAA,KAAA,CAAM,YAAN;EAGF,KAAA;;IAAS,2BAAyB,YAAzB,aAAA,CAAA,KAAA,CAAA;MAAkB,MAAO;kBAAzB,IAAI,KAAJ,CAAU,GAAV"} 2 | -------------------------------------------------------------------------------- /test/fixtures/src/simple2.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.0.0-dev 2 | void function () { 3 | var cubes, list, math, number, opposite, race, square; 4 | number = 42; 5 | opposite = true; 6 | if (opposite) 7 | number = -42; 8 | square = function (x) { 9 | return x * x; 10 | }; 11 | list = [ 12 | 1, 13 | 2, 14 | 3, 15 | 4, 16 | 5 17 | ]; 18 | math = { 19 | root: Math.sqrt, 20 | square: square, 21 | cube: function (x) { 22 | return x * square(x); 23 | } 24 | }; 25 | race = function (winner, runners) { 26 | runners = 2 <= arguments.length ? [].slice.call(arguments, 1) : []; 27 | return print(winner, runners); 28 | }; 29 | if ('undefined' !== typeof elvis && null != elvis) 30 | alert('I knew it!'); 31 | cubes = function (accum$) { 32 | var num; 33 | for (var i$ = 0, length$ = list.length; i$ < length$; ++i$) { 34 | num = list[i$]; 35 | accum$.push(math.cube(num)); 36 | } 37 | return accum$; 38 | }.call(this, []); 39 | }.call(this); 40 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 "Cowboy" Ben Alman, contributors 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-contrib-uglify", 3 | "description": "Minify files with UglifyJS.", 4 | "version": "0.3.2", 5 | "homepage": "https://github.com/gruntjs/grunt-contrib-uglify", 6 | "author": { 7 | "name": "Grunt Team", 8 | "url": "http://gruntjs.com/" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/gruntjs/grunt-contrib-uglify.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/gruntjs/grunt-contrib-uglify/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "https://github.com/gruntjs/grunt-contrib-uglify/blob/master/LICENSE-MIT" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 0.8.0" 25 | }, 26 | "scripts": { 27 | "test": "grunt test" 28 | }, 29 | "dependencies": { 30 | "uglify-js": "~2.4.0", 31 | "grunt-lib-contrib": "~0.6.1", 32 | "chalk": "~0.4.0" 33 | }, 34 | "devDependencies": { 35 | "grunt-contrib-jshint": "~0.6.3", 36 | "grunt-contrib-nodeunit": "~0.2.0", 37 | "grunt-contrib-clean": "~0.5.0", 38 | "grunt-contrib-internal": "~0.4.2", 39 | "grunt": "~0.4.0" 40 | }, 41 | "peerDependencies": { 42 | "grunt": "~0.4.0" 43 | }, 44 | "keywords": [ 45 | "gruntplugin" 46 | ], 47 | "files": [ 48 | "tasks", 49 | "LICENSE-MIT" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /test/uglify_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var grunt = require('grunt'); 4 | 5 | var tmp = 'tmp/', 6 | fixtures = 'test/fixtures/expected/'; 7 | 8 | exports.contrib_uglify = { 9 | preuglified_files: function(test) { 10 | 11 | var files = [ 12 | 'comments.js', 13 | 'compress.js', 14 | 'compress_mangle.js', 15 | 'compress_mangle_banner.js', 16 | 'compress_mangle_beautify.js', 17 | 'compress_mangle_except.js', 18 | 'enclose.js', 19 | 'multifile.js', 20 | 'wrap.js', 21 | 'exportAll.js', 22 | 'sourcemap_basic.js', 23 | 'sourcemap_basic.map', 24 | 'sourcemap_customDir.js', 25 | 'sourcemap_customName.js', 26 | 'sourcemap_functionName.js', 27 | 'sourcemap_functionName.js.fn.map', 28 | 'deep/directory/location/source_map.js.map', 29 | 'sourcemapin.js', 30 | 'sourcemapin.map', 31 | 'sourcemap_sources.map', 32 | 'sourcemaps_multiple1.js', 33 | 'sourcemaps_multiple1.map', 34 | 'sourcemaps_multiple2.js', 35 | 'sourcemaps_multiple2.map', 36 | 'sourcemaps_multiple1_fnName.js', 37 | 'sourcemaps_multiple1_fnName.js.fn.map', 38 | 'sourcemaps_multiple2_fnName.js', 39 | 'sourcemaps_multiple2_fnName.js.fn.map' 40 | ]; 41 | 42 | test.expect(files.length); 43 | 44 | files.forEach(function(file){ 45 | var actual = grunt.file.read(tmp + file); 46 | var expected = grunt.file.read(fixtures + file); 47 | test.equal(actual, expected, 'task output should equal ' + file); 48 | }); 49 | 50 | test.done(); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v0.3.2: 2 | date: 2013-01-22 3 | changes: 4 | - fix handling of `sourceMapIncludeSources` option. 5 | v0.3.1: 6 | date: 2014-01-20 7 | changes: 8 | - fix relative path issue in sourcemaps 9 | v0.3.0: 10 | date: 2014-01-16 11 | changes: 12 | - refactor sourcemap support 13 | v0.2.7: 14 | date: 2013-11-09 15 | changes: 16 | - prepending banner if sourceMap option not set, addresses #109 17 | v0.2.6: 18 | date: 2013-11-08 19 | changes: 20 | - merged 45, 53, 85 (105 by way of duping 53) 21 | - Added support for banners in uglified files with sourcemaps 22 | - Updated docs 23 | v0.2.5: 24 | date: 2013-10-28 25 | changes: 26 | - Added warning for banners when using sourcemaps 27 | v0.2.4: 28 | date: 2013-09-02 29 | changes: 30 | - updated sourcemap format via /83 31 | v0.2.3: 32 | date: 2013-06-10 33 | changes: 34 | - added footer option 35 | v0.2.2: 36 | date: 2013-05-31 37 | changes: 38 | - Reverted /56 due to /58 until [chrome/239660](https://code.google.com/p/chromium/issues/detail?id=239660&q=sourcemappingurl&colspec=ID%20Pri%20M%20Iteration%20ReleaseBlock%20Cr%20Status%20Owner%20Summary%20OS%20Modified) [firefox/870361](https://bugzilla.mozilla.org/show_bug.cgi?id=870361) drop 39 | v0.2.1: 40 | date: 2013-05-22 41 | changes: 42 | - Bumped uglify to ~2.3.5 /55 /40 43 | - Changed sourcemappingUrl syntax /56 44 | - Disabled sorting of names for consistent mangling /44 45 | - Updated docs for sourceMapRoot /47 /25 46 | v0.2.0: 47 | date: 2013-03-14 48 | changes: 49 | - No longer report gzip results by default. 50 | - Support `report` option. 51 | v0.1.2: 52 | date: 2013-01-30 53 | changes: 54 | - Added better error reporting 55 | - Support for dynamic names of multiple sourcemaps 56 | v0.1.1: 57 | date: 2013-02-15 58 | changes: 59 | - First official release for Grunt 0.4.0. 60 | v0.1.1rc6: 61 | date: 2013-01-18 62 | changes: 63 | - Updating grunt/gruntplugin dependencies to rc6. 64 | - Changing in-development grunt/gruntplugin dependency versions from tilde version ranges to specific versions. 65 | v0.1.1rc5: 66 | date: 2013-01-09 67 | changes: 68 | - Updating to work with grunt v0.4.0rc5. 69 | - Switching back to this.files api. 70 | v0.1.0: 71 | date: 2012-11-28 72 | changes: 73 | - Work in progress, not yet officially released. 74 | -------------------------------------------------------------------------------- /docs/uglify-options.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | This task primarily delegates to [UglifyJS2][], so please consider the [UglifyJS documentation][] as required reading for advanced configuration. 4 | 5 | [UglifyJS2]: https://github.com/mishoo/UglifyJS2 6 | [UglifyJS documentation]: http://lisperator.net/uglifyjs/ 7 | 8 | ## mangle 9 | Type: `Boolean` `Object` 10 | Default: `{}` 11 | 12 | Turn on or off mangling with default options. If an `Object` is specified, it is passed directly to `ast.mangle_names()` *and* `ast.compute_char_frequency()` (mimicking command line behavior). 13 | 14 | ## compress 15 | Type: `Boolean` `Object` 16 | Default: `{}` 17 | 18 | Turn on or off source compression with default options. If an `Object` is specified, it is passed as options to `UglifyJS.Compressor()`. 19 | 20 | ## beautify 21 | Type: `Boolean` `Object` 22 | Default: `false` 23 | 24 | Turns on beautification of the generated source code. An `Object` will be merged and passed with the options sent to `UglifyJS.OutputStream()` 25 | 26 | ## report 27 | Choices: `false` `'min'` `'gzip'` 28 | Default: `false` 29 | 30 | Either do not report anything, report only minification result, or report minification and gzip results. This is useful to see exactly how well Uglify is performing, but using `'gzip'` can add 5-10x runtime task execution. 31 | 32 | Example ouput using `'gzip'`: 33 | 34 | ``` 35 | Original: 198444 bytes. 36 | Minified: 101615 bytes. 37 | Gzipped: 20084 bytes. 38 | ``` 39 | 40 | ## sourceMap 41 | Type: `Boolean` 42 | Default: `false` 43 | 44 | If `true`, a source map file will be generated in the same directory as the `dest` file. By default it will have the same basename as the `dest` file, but with a `.map` extension. 45 | 46 | ## sourceMapName 47 | Type: `String` `Function` 48 | Default: `undefined` 49 | 50 | To customize the name or location of the generated source map, pass a string to indicate where to write the source map to. If a function is provided, the uglify destination is passed as the argument and the return value will be used as the file name. 51 | 52 | ## sourceMapIn 53 | Type: `String` `Function` 54 | Default: `undefined` 55 | 56 | The location of an input source map from an earlier compilation, e.g. from CoffeeScript. If a function is provided, the 57 | uglify source is passed as the argument and the return value will be used as the sourceMap name. This only makes sense 58 | when there's one source file. 59 | 60 | ## sourceMapIncludeSources 61 | Type: `Boolean` 62 | Default: `false` 63 | 64 | Pass this flag if you want to include the content of source files in the source map as sourcesContent property. 65 | 66 | #### enclose 67 | Type: `Object` 68 | Default: `undefined` 69 | 70 | Wrap all of the code in a closure with a configurable arguments/parameters list. 71 | Each key-value pair in the `enclose` object is effectively an argument-parameter pair. 72 | 73 | ## wrap 74 | Type: `String` 75 | Default: `undefined` 76 | 77 | Wrap all of the code in a closure, an easy way to make sure nothing is leaking. 78 | For variables that need to be public `exports` and `global` variables are made available. 79 | The value of wrap is the global variable exports will be available as. 80 | 81 | ## exportAll 82 | Type: `Boolean` 83 | Default: `false` 84 | 85 | When using `wrap` this will make all global functions and variables available via the export variable. 86 | 87 | ## preserveComments 88 | Type: `Boolean` `String` `Function` 89 | Default: `undefined` 90 | Options: `false` `'all'` `'some'` 91 | 92 | Turn on preservation of comments. 93 | 94 | - `false` will strip all comments 95 | - `'all'` will preserve all comments in code blocks that have not been squashed or dropped 96 | - `'some'` will preserve all comments that start with a bang (`!`) or include a closure compiler style directive (`@preserve` `@license` `@cc_on`) 97 | - `Function` specify your own comment preservation function. You will be passed the current node and the current comment and are expected to return either `true` or `false` 98 | 99 | ## banner 100 | Type: `String` 101 | Default: empty string 102 | 103 | This string will be prepended to the beginning of the minified output. It is processed using [grunt.template.process][], using the default options. 104 | 105 | ## footer 106 | Type: `String` 107 | Default: empty string 108 | 109 | This string will be append to the end of the minified output. It is processed using [grunt.template.process][], using the default options. 110 | 111 | _(Default processing options are explained in the [grunt.template.process][] documentation)_ 112 | 113 | [grunt.template.process]: http://gruntjs.com/api/grunt.template#grunt.template.process 114 | 115 | -------------------------------------------------------------------------------- /tasks/uglify.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-contrib-uglify 3 | * http://gruntjs.com/ 4 | * 5 | * Copyright (c) 2013 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | var path = require('path'); 12 | 13 | // Generate the default source map name 14 | var getSourceMapLocation = function( dest ) { 15 | 16 | var destExt = path.extname(dest); 17 | var destDirname = path.dirname(dest); 18 | var destBasename = path.basename(dest, destExt); 19 | 20 | return destDirname + path.sep + destBasename + ".map"; 21 | 22 | }; 23 | 24 | // Return the relative path from file1 => file2 25 | var relativePath = function(file1, file2) { 26 | 27 | var file1Dirname = path.dirname(file1); 28 | var file2Dirname = path.dirname(file2); 29 | if (file1Dirname !== file2Dirname) { 30 | return path.relative(file1Dirname, file2Dirname) + path.sep; 31 | } else { 32 | return ""; 33 | } 34 | 35 | }; 36 | 37 | module.exports = function(grunt) { 38 | 39 | // Internal lib. 40 | var contrib = require('grunt-lib-contrib').init(grunt); 41 | var uglify = require('./lib/uglify').init(grunt); 42 | 43 | var chalk = require('chalk'); 44 | 45 | grunt.registerMultiTask('uglify', 'Minify files with UglifyJS.', function() { 46 | // Merge task-specific and/or target-specific options with these defaults. 47 | var options = this.options({ 48 | banner: '', 49 | footer: '', 50 | compress: { 51 | warnings: false 52 | }, 53 | mangle: {}, 54 | beautify: false, 55 | report: false 56 | }); 57 | 58 | // Process banner. 59 | var banner = grunt.template.process(options.banner); 60 | var footer = grunt.template.process(options.footer); 61 | var mapNameGenerator, mapInNameGenerator; 62 | 63 | // Iterate over all src-dest file pairs. 64 | this.files.forEach(function(f) { 65 | var src = f.src.filter(function(filepath) { 66 | // Warn on and remove invalid source files (if nonull was set). 67 | if (!grunt.file.exists(filepath)) { 68 | grunt.log.warn('Source file "' + filepath + '" not found.'); 69 | return false; 70 | } else { 71 | return true; 72 | } 73 | }); 74 | 75 | if (src.length === 0) { 76 | grunt.log.warn('Destination (' + f.dest + ') not written because src files were empty.'); 77 | return; 78 | } 79 | 80 | // function to get the name of the sourceMap 81 | if (typeof options.sourceMapName === "function") { 82 | mapNameGenerator = options.sourceMapName; 83 | } 84 | 85 | // function to get the name of the sourceMapIn file 86 | if (typeof options.sourceMapIn === "function") { 87 | if (src.length !== 1) { 88 | grunt.fail.warn('Cannot generate `sourceMapIn` for multiple source files.'); 89 | } 90 | mapInNameGenerator = options.sourceMapIn; 91 | } 92 | 93 | // dynamically create destination sourcemap name 94 | if (mapNameGenerator) { 95 | try { 96 | options.generatedSourceMapName = mapNameGenerator(f.dest); 97 | } catch (e) { 98 | var err = new Error('SourceMap failed.'); 99 | err.origError = e; 100 | grunt.fail.warn(err); 101 | } 102 | } 103 | // If no name is passed, generate the default name 104 | else if ( !options.sourceMapName ) { 105 | options.generatedSourceMapName = getSourceMapLocation( f.dest ); 106 | } else { 107 | options.generatedSourceMapName = options.sourceMapName; 108 | } 109 | 110 | // Dynamically create incoming sourcemap names 111 | if (mapInNameGenerator) { 112 | try { 113 | options.sourceMapIn = mapInNameGenerator(src[0]); 114 | } catch (e) { 115 | var err = new Error('SourceMapInName failed.'); 116 | err.origError = e; 117 | grunt.fail.warn(err); 118 | } 119 | } 120 | 121 | // Calculate the path from the dest file to the sourcemap for the 122 | // sourceMappingURL reference 123 | if (options.sourceMap) { 124 | var destToSourceMapPath = relativePath(f.dest, options.generatedSourceMapName); 125 | var sourceMapBasename = path.basename(options.generatedSourceMapName); 126 | options.destToSourceMap = destToSourceMapPath + sourceMapBasename; 127 | } 128 | 129 | // Minify files, warn and fail on error. 130 | var result; 131 | try { 132 | result = uglify.minify(src, f.dest, options); 133 | } catch (e) { 134 | console.log(e); 135 | var err = new Error('Uglification failed.'); 136 | if (e.message) { 137 | err.message += '\n' + e.message + '. \n'; 138 | if (e.line) { 139 | err.message += 'Line ' + e.line + ' in ' + src + '\n'; 140 | } 141 | } 142 | err.origError = e; 143 | grunt.log.warn('Uglifying source "' + src + '" failed.'); 144 | grunt.fail.warn(err); 145 | } 146 | 147 | // Concat minified source + footer 148 | var output = result.min + footer; 149 | 150 | // Only prepend banner if uglify hasn't taken care of it as part of the preamble 151 | if (!options.sourceMap) { 152 | output = banner + output; 153 | } 154 | 155 | // Write the destination file. 156 | grunt.file.write(f.dest, output); 157 | 158 | // Write source map 159 | if (options.sourceMap) { 160 | grunt.file.write(options.generatedSourceMapName, result.sourceMap); 161 | grunt.log.writeln('File ' + chalk.cyan(options.generatedSourceMapName) + ' created (source map).'); 162 | } 163 | 164 | // Print a success message. 165 | grunt.log.writeln('File ' + chalk.cyan(f.dest) + ' created.'); 166 | 167 | // ...and report some size information. 168 | if (options.report) { 169 | contrib.minMaxInfo(output, result.max, options.report); 170 | } 171 | }); 172 | }); 173 | 174 | }; 175 | -------------------------------------------------------------------------------- /tasks/lib/uglify.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-contrib-uglify 3 | * https://gruntjs.com/ 4 | * 5 | * Copyright (c) 2013 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | var path = require('path'); 12 | 13 | // External libs. 14 | var UglifyJS = require('uglify-js'); 15 | var fs = require('fs'); 16 | 17 | exports.init = function(grunt) { 18 | var exports = {}; 19 | 20 | // Minify with UglifyJS. 21 | // From https://github.com/mishoo/UglifyJS2 22 | // API docs at http://lisperator.net/uglifyjs/ 23 | exports.minify = function(files, dest, options) { 24 | options = options || {}; 25 | 26 | grunt.verbose.write('Minifying with UglifyJS...'); 27 | 28 | var topLevel = null; 29 | var totalCode = ''; 30 | var sourcesContent = {}; 31 | 32 | var outputOptions = getOutputOptions(options, dest); 33 | var output = UglifyJS.OutputStream(outputOptions); 34 | 35 | // Grab and parse all source files 36 | files.forEach(function(file){ 37 | 38 | var code = grunt.file.read(file); 39 | totalCode += code; 40 | 41 | // The src file name must be relative to the source map for things to work 42 | var basename = path.basename(file); 43 | var fileDir = path.dirname(file); 44 | var sourceMapDir = path.dirname(options.generatedSourceMapName); 45 | var relativePath = path.relative(sourceMapDir, fileDir); 46 | var pathPrefix = relativePath ? (relativePath+path.sep) : ""; 47 | 48 | file = pathPrefix + basename; 49 | 50 | sourcesContent[file] = code; 51 | topLevel = UglifyJS.parse(code, { 52 | filename: file, 53 | toplevel: topLevel 54 | }); 55 | }); 56 | 57 | // Wrap code in a common js wrapper. 58 | if (options.wrap) { 59 | topLevel = topLevel.wrap_commonjs(options.wrap, options.exportAll); 60 | } 61 | 62 | // Wrap code in closure with configurable arguments/parameters list. 63 | if (options.enclose) { 64 | var argParamList = grunt.util._.map(options.enclose, function(val, key) { 65 | return key + ':' + val; 66 | }); 67 | 68 | topLevel = topLevel.wrap_enclose(argParamList); 69 | } 70 | 71 | // Need to call this before we mangle or compress, 72 | // and call after any compression or ast altering 73 | topLevel.figure_out_scope(); 74 | 75 | if (options.compress !== false) { 76 | if (options.compress.warnings !== true) { 77 | options.compress.warnings = false; 78 | } 79 | var compressor = UglifyJS.Compressor(options.compress); 80 | topLevel = topLevel.transform(compressor); 81 | 82 | // Need to figure out scope again after source being altered 83 | topLevel.figure_out_scope(); 84 | } 85 | 86 | if (options.mangle !== false) { 87 | // disabled due to: 88 | // 1) preserve stable name mangling 89 | // 2) it increases gzipped file size, see https://github.com/mishoo/UglifyJS2#mangler-options 90 | // // compute_char_frequency optimizes names for compression 91 | // topLevel.compute_char_frequency(options.mangle); 92 | 93 | // Requires previous call to figure_out_scope 94 | // and should always be called after compressor transform 95 | topLevel.mangle_names(options.mangle); 96 | } 97 | 98 | if (options.sourceMap && options.sourceMapIncludeSources) { 99 | for (var file in sourcesContent) { 100 | if (sourcesContent.hasOwnProperty(file)) { 101 | outputOptions.source_map.get().setSourceContent(file, sourcesContent[file]); 102 | } 103 | } 104 | } 105 | 106 | // Print the ast to OutputStream 107 | topLevel.print(output); 108 | 109 | var min = output.get(); 110 | 111 | // Add the source map reference to the end of the file 112 | if (options.sourceMap) { 113 | min += "\n//# sourceMappingURL="+options.destToSourceMap; 114 | } 115 | 116 | var result = { 117 | max: totalCode, 118 | min: min, 119 | sourceMap: outputOptions.source_map 120 | }; 121 | 122 | grunt.verbose.ok(); 123 | 124 | return result; 125 | }; 126 | 127 | var getOutputOptions = function(options, dest) { 128 | var outputOptions = { 129 | beautify: false, 130 | source_map: null 131 | }; 132 | 133 | if (options.preserveComments) { 134 | if (options.preserveComments === 'all' || options.preserveComments === true) { 135 | 136 | // preserve all the comments we can 137 | outputOptions.comments = true; 138 | } else if (options.preserveComments === 'some') { 139 | // preserve comments with directives or that start with a bang (!) 140 | outputOptions.comments = /^!|@preserve|@license|@cc_on/i; 141 | } else if (grunt.util._.isFunction(options.preserveComments)) { 142 | 143 | // support custom functions passed in 144 | outputOptions.comments = options.preserveComments; 145 | } 146 | } 147 | 148 | if (options.banner && options.sourceMap) { 149 | outputOptions.preamble = options.banner; 150 | } 151 | 152 | if (options.beautify) { 153 | if (grunt.util._.isObject(options.beautify)) { 154 | // beautify options sent as an object are merged 155 | // with outputOptions and passed to the OutputStream 156 | grunt.util._.extend(outputOptions, options.beautify); 157 | } else { 158 | outputOptions.beautify = true; 159 | } 160 | } 161 | 162 | 163 | if (options.sourceMap) { 164 | 165 | var destBasename = path.basename(dest); 166 | var destPath = path.dirname(dest); 167 | var sourceMapIn; 168 | if (options.sourceMapIn) { 169 | sourceMapIn = grunt.file.readJSON(options.sourceMapIn); 170 | } 171 | outputOptions.source_map = UglifyJS.SourceMap({ 172 | file: destBasename, 173 | orig: sourceMapIn 174 | }); 175 | 176 | } 177 | 178 | if (options.indentLevel !== undefined) { 179 | outputOptions.indent_level = options.indentLevel; 180 | } 181 | 182 | return outputOptions; 183 | }; 184 | 185 | return exports; 186 | }; 187 | -------------------------------------------------------------------------------- /docs/uglify-examples.md: -------------------------------------------------------------------------------- 1 | # Usage examples 2 | 3 | ## Basic compression 4 | 5 | This configuration will compress and mangle the input files using the default options. 6 | 7 | ```js 8 | // Project configuration. 9 | grunt.initConfig({ 10 | uglify: { 11 | my_target: { 12 | files: { 13 | 'dest/output.min.js': ['src/input1.js', 'src/input2.js'] 14 | } 15 | } 16 | } 17 | }); 18 | ``` 19 | 20 | ## No mangling 21 | 22 | Specify `mangle: false` to prevent changes to your variable and function names. 23 | 24 | ```js 25 | // Project configuration. 26 | grunt.initConfig({ 27 | uglify: { 28 | options: { 29 | mangle: false 30 | }, 31 | my_target: { 32 | files: { 33 | 'dest/output.min.js': ['src/input.js'] 34 | } 35 | } 36 | } 37 | }); 38 | ``` 39 | 40 | ## Reserved identifiers 41 | 42 | You can specify identifiers to leave untouched with an `except` array in the `mangle` options. 43 | 44 | ```js 45 | // Project configuration. 46 | grunt.initConfig({ 47 | uglify: { 48 | options: { 49 | mangle: { 50 | except: ['jQuery', 'Backbone'] 51 | } 52 | }, 53 | my_target: { 54 | files: { 55 | 'dest/output.min.js': ['src/input.js'] 56 | } 57 | } 58 | } 59 | }); 60 | ``` 61 | 62 | ## Source maps 63 | 64 | Configure basic source map output by specifying a file path for the `sourceMap` option. 65 | 66 | ```js 67 | // Project configuration. 68 | grunt.initConfig({ 69 | uglify: { 70 | my_target: { 71 | options: { 72 | sourceMap: 'path/to/source-map.js' 73 | }, 74 | files: { 75 | 'dest/output.min.js': ['src/input.js'] 76 | } 77 | } 78 | } 79 | }); 80 | ``` 81 | 82 | ## Advanced source maps 83 | 84 | You can specify the parameters to pass to `UglifyJS.SourceMap()` which will 85 | allow you to configure advanced settings. 86 | 87 | Refer to the [UglifyJS SourceMap Documentation](http://lisperator.net/uglifyjs/codegen#source-map) for more information. 88 | 89 | ```js 90 | // Project configuration. 91 | grunt.initConfig({ 92 | uglify: { 93 | my_target: { 94 | options: { 95 | sourceMap: 'path/to/source-map.js', 96 | sourceMapRoot: 'http://example.com/path/to/src/', // the location to find your original source 97 | sourceMapIn: 'example/coffeescript-sourcemap.js', // input sourcemap from a previous compilation 98 | }, 99 | files: { 100 | 'dest/output.min.js': ['src/input.js'], 101 | }, 102 | }, 103 | }, 104 | }); 105 | ``` 106 | ## Discard console.* functions 107 | 108 | Specify `drop_console: true` as part of the `compress` options to discard calls to `console.*` functions. 109 | 110 | ```js 111 | // Project configuration. 112 | grunt.initConfig({ 113 | uglify: { 114 | options: { 115 | compress: { 116 | drop_console: true 117 | } 118 | }, 119 | my_target: { 120 | files: { 121 | 'dest/output.min.js': ['src/input.js'] 122 | } 123 | } 124 | } 125 | }); 126 | ``` 127 | 128 | ## Beautify 129 | 130 | Specify `beautify: true` to beautify your code for debugging/troubleshooting purposes. 131 | Pass an object to manually configure any other output options passed directly to `UglifyJS.OutputStream()`. 132 | 133 | See [UglifyJS Codegen documentation](http://lisperator.net/uglifyjs/codegen) for more information. 134 | 135 | _Note that manual configuration will require you to explicitly set `beautify: true` if you want traditional, beautified output._ 136 | 137 | ```js 138 | // Project configuration. 139 | grunt.initConfig({ 140 | uglify: { 141 | my_target: { 142 | options: { 143 | beautify: true 144 | }, 145 | files: { 146 | 'dest/output.min.js': ['src/input.js'] 147 | } 148 | }, 149 | my_advanced_target: { 150 | options: { 151 | beautify: { 152 | width: 80, 153 | beautify: true 154 | } 155 | }, 156 | files: { 157 | 'dest/output.min.js': ['src/input.js'] 158 | } 159 | } 160 | } 161 | }); 162 | ``` 163 | 164 | ## Banner comments 165 | 166 | In this example, running `grunt uglify:my_target` will prepend a banner created by interpolating the `banner` template string with the config object. Here, those properties are the values imported from the `package.json` file (which are available via the `pkg` config property) plus today's date. 167 | 168 | _Note: you don't have to use an external JSON file. It's also valid to create the `pkg` object inline in the config. That being said, if you already have a JSON file, you might as well reference it._ 169 | 170 | ```js 171 | // Project configuration. 172 | grunt.initConfig({ 173 | pkg: grunt.file.readJSON('package.json'), 174 | uglify: { 175 | options: { 176 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + 177 | '<%= grunt.template.today("yyyy-mm-dd") %> */' 178 | }, 179 | my_target: { 180 | files: { 181 | 'dest/output.min.js': ['src/input.js'] 182 | } 183 | } 184 | } 185 | }); 186 | ``` 187 | 188 | ## Conditional compilation 189 | 190 | You can also enable UglifyJS conditional compilation. This is commonly used to remove debug code blocks for production builds. 191 | 192 | See [UglifyJS global definitions documentation](http://lisperator.net/uglifyjs/compress#global-defs) for more information. 193 | 194 | ```js 195 | // Project configuration. 196 | grunt.initConfig({ 197 | uglify: { 198 | options: { 199 | compress: { 200 | global_defs: { 201 | "DEBUG": false 202 | }, 203 | dead_code: true 204 | } 205 | }, 206 | my_target: { 207 | files: { 208 | 'dest/output.min.js': ['src/input.js'] 209 | } 210 | } 211 | } 212 | }); 213 | ``` 214 | ## Compiling all files in a folder dynamically 215 | 216 | This configuration will compress and mangle the files dynamically. 217 | 218 | ```js 219 | // Project configuration. 220 | grunt.initConfig({ 221 | uglify: { 222 | my_target: { 223 | files: [{ 224 | expand: true, 225 | cwd: 'src/js', 226 | src: '**/*.js', 227 | dest: 'dest/js' 228 | }] 229 | } 230 | } 231 | }); 232 | ``` 233 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-contrib-uglify 3 | * http://gruntjs.com/ 4 | * 5 | * Copyright (c) 2013 "Cowboy" Ben Alman, contributors 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | 13 | // Project configuration. 14 | grunt.initConfig({ 15 | jshint: { 16 | all: [ 17 | 'Gruntfile.js', 18 | 'tasks/**/*.js', 19 | '<%= nodeunit.tests %>' 20 | ], 21 | options: { 22 | jshintrc: '.jshintrc' 23 | } 24 | }, 25 | 26 | // Before generating any new files, remove any previously-created files. 27 | clean: { 28 | tests: ['tmp'] 29 | }, 30 | 31 | // Configuration to be run (and then tested). 32 | uglify: { 33 | compress: { 34 | files: { 35 | 'tmp/compress.js': ['test/fixtures/src/simple.js'] 36 | }, 37 | options: { 38 | mangle: false 39 | } 40 | }, 41 | compress_mangle: { 42 | files: { 43 | 'tmp/compress_mangle.js': ['test/fixtures/src/simple.js'] 44 | } 45 | }, 46 | compress_mangle_banner: { 47 | files: { 48 | 'tmp/compress_mangle_banner.js': ['test/fixtures/src/simple.js'] 49 | }, 50 | options : { 51 | banner : '// banner without sourcemap\n' 52 | } 53 | }, 54 | no_src: { 55 | files: { 56 | 'tmp/compress_mangle.js': [] 57 | } 58 | }, 59 | compress_mangle_except: { 60 | files: { 61 | 'tmp/compress_mangle_except.js': ['test/fixtures/src/simple.js'] 62 | }, 63 | options: { 64 | mangle: { 65 | except: ['argumentC'] 66 | } 67 | } 68 | }, 69 | compress_mangle_beautify: { 70 | files: { 71 | 'tmp/compress_mangle_beautify.js': ['test/fixtures/src/simple.js'] 72 | }, 73 | options: { 74 | beautify: true 75 | } 76 | }, 77 | enclose: { 78 | files: { 79 | 'tmp/enclose.js': ['test/fixtures/src/simple.js'] 80 | }, 81 | options: { 82 | beautify: true, 83 | compress: false, 84 | enclose: { 85 | 'window.argA': 'paramA', 86 | 'window.argB': 'paramB' 87 | }, 88 | mangle: false 89 | } 90 | }, 91 | multifile: { 92 | files: { 93 | 'tmp/multifile.js': ['test/fixtures/src/simple.js','test/fixtures/src/comments.js'] 94 | }, 95 | options: { 96 | mangle: false 97 | } 98 | }, 99 | comments: { 100 | src: 'test/fixtures/src/comments.js', 101 | dest: 'tmp/comments.js', 102 | options: { 103 | mangle: false, 104 | preserveComments: 'some' 105 | } 106 | }, 107 | wrap: { 108 | src: 'test/fixtures/src/simple.js', 109 | dest: 'tmp/wrap.js', 110 | options: { 111 | mangle: false, 112 | wrap: 'testExport' 113 | } 114 | }, 115 | exportAll: { 116 | src: 'test/fixtures/src/simple.js', 117 | dest: 'tmp/exportAll.js', 118 | options: { 119 | mangle: false, 120 | wrap: 'testExport', 121 | exportAll: true 122 | } 123 | }, 124 | sourcemap_basic: { 125 | src: 'test/fixtures/src/simple.js', 126 | dest: 'tmp/sourcemap_basic.js', 127 | options: { 128 | sourceMap: true 129 | } 130 | }, 131 | sourcemap_customName: { 132 | src: 'test/fixtures/src/simple.js', 133 | dest: 'tmp/sourcemap_customName.js', 134 | options: { 135 | sourceMap: true, 136 | sourceMapName: 'tmp/source_map_custom_name' 137 | } 138 | }, 139 | sourcemap_customDir: { 140 | src: 'test/fixtures/src/simple.js', 141 | dest: 'tmp/sourcemap_customDir.js', 142 | options: { 143 | sourceMap: true, 144 | sourceMapName: 'tmp/deep/directory/location/source_map.js.map' 145 | } 146 | }, 147 | sourcemap_functionName: { 148 | src: 'test/fixtures/src/simple.js', 149 | dest: 'tmp/sourcemap_functionName.js', 150 | options: { 151 | sourceMap: true, 152 | sourceMapName: function( dest ) { 153 | return dest + ".fn.map"; 154 | } 155 | } 156 | }, 157 | sourcemap_multiple: { 158 | files: { 159 | 'tmp/sourcemaps_multiple1.js': ['test/fixtures/src/simple.js'], 160 | 'tmp/sourcemaps_multiple2.js': ['test/fixtures/src/comments.js'] 161 | }, 162 | options: { 163 | sourceMap: true 164 | } 165 | }, 166 | sourcemap_multipleFunctionNames: { 167 | files: { 168 | 'tmp/sourcemaps_multiple1_fnName.js': ['test/fixtures/src/simple.js'], 169 | 'tmp/sourcemaps_multiple2_fnName.js': ['test/fixtures/src/comments.js'] 170 | }, 171 | options: { 172 | sourceMap: true, 173 | sourceMapName: function( dest ) { 174 | return dest+'.fn.map'; 175 | } 176 | } 177 | }, 178 | sourcemapin: { 179 | files: { 180 | 'tmp/sourcemapin.js': ['test/fixtures/src/simple2.js'] 181 | }, 182 | options: { 183 | mangle: false, 184 | banner: '// Hello World\n', 185 | sourceMap: true, 186 | sourceMapIn: function() { 187 | return 'test/fixtures/src/simple2.map'; 188 | } 189 | } 190 | }, 191 | sourcemap_sources: { 192 | files: { 193 | 'tmp/sourcemap_sources.js': ['test/fixtures/src/simple.js'] 194 | }, 195 | options: { 196 | sourceMap: true, 197 | sourceMapIncludeSources: true 198 | } 199 | }, 200 | }, 201 | 202 | // Unit tests. 203 | nodeunit: { 204 | tests: ['test/*_test.js'] 205 | } 206 | 207 | }); 208 | 209 | // task that expects its argument (another task) to fail 210 | grunt.registerTask('expectFail', function(){ 211 | var task = this.args.join(':'); 212 | 213 | var done = this.async(); 214 | 215 | function onComplete(error, result, code) { 216 | grunt.log.write("\n > " + result.stdout.split("\n").join("\n > ") + "\n"); 217 | var rv = error ? true : new Error("Task " + task + " unexpectedly passed."); 218 | done(rv); 219 | } 220 | 221 | grunt.util.spawn({ 222 | grunt : true, 223 | args : task 224 | }, onComplete); 225 | }); 226 | 227 | // Actually load this plugin's task(s). 228 | grunt.loadTasks('tasks'); 229 | 230 | // These plugins provide necessary tasks. 231 | grunt.loadNpmTasks('grunt-contrib-jshint'); 232 | grunt.loadNpmTasks('grunt-contrib-clean'); 233 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 234 | grunt.loadNpmTasks('grunt-contrib-internal'); 235 | 236 | // Whenever the "test" task is run, first clean the "tmp" dir, then run this 237 | // plugin's task(s), then test the result. 238 | grunt.registerTask('test', [ 239 | 'clean', 240 | 'uglify:compress', 241 | 'uglify:compress_mangle', 242 | 'uglify:compress_mangle_banner', 243 | 'uglify:no_src', 244 | 'uglify:compress_mangle_except', 245 | 'uglify:compress_mangle_beautify', 246 | 'uglify:multifile', 247 | 'uglify:sourcemap_sources', 248 | 'uglify:comments', 249 | 'uglify:wrap', 250 | 'uglify:exportAll', 251 | 'uglify:enclose', 252 | 'uglify:sourcemap_basic', 253 | 'uglify:sourcemap_customName', 254 | 'uglify:sourcemap_customDir', 255 | 'uglify:sourcemap_functionName', 256 | 'uglify:sourcemap_multiple', 257 | 'uglify:sourcemap_multipleFunctionNames', 258 | 'uglify:sourcemapin', 259 | 'uglify:sourcemap_sources', 260 | 'nodeunit' 261 | ]); 262 | 263 | // By default, lint and run all tests. 264 | grunt.registerTask('default', ['jshint', 'test', 'build-contrib']); 265 | 266 | }; 267 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-contrib-uglify v0.3.2 [![Build Status](https://travis-ci.org/gruntjs/grunt-contrib-uglify.png?branch=master)](https://travis-ci.org/gruntjs/grunt-contrib-uglify) 2 | 3 | > Minify files with UglifyJS. 4 | 5 | 6 | 7 | ## Getting Started 8 | This plugin requires Grunt `~0.4.0` 9 | 10 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 11 | 12 | ```shell 13 | npm install grunt-contrib-uglify --save-dev 14 | ``` 15 | 16 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 17 | 18 | ```js 19 | grunt.loadNpmTasks('grunt-contrib-uglify'); 20 | ``` 21 | 22 | 23 | 24 | 25 | ## Uglify task 26 | _Run this task with the `grunt uglify` command._ 27 | 28 | Task targets, files and options may be specified according to the grunt [Configuring tasks](http://gruntjs.com/configuring-tasks) guide. 29 | ### Options 30 | 31 | This task primarily delegates to [UglifyJS2][], so please consider the [UglifyJS documentation][] as required reading for advanced configuration. 32 | 33 | [UglifyJS2]: https://github.com/mishoo/UglifyJS2 34 | [UglifyJS documentation]: http://lisperator.net/uglifyjs/ 35 | 36 | #### mangle 37 | Type: `Boolean` `Object` 38 | Default: `{}` 39 | 40 | Turn on or off mangling with default options. If an `Object` is specified, it is passed directly to `ast.mangle_names()` *and* `ast.compute_char_frequency()` (mimicking command line behavior). 41 | 42 | #### compress 43 | Type: `Boolean` `Object` 44 | Default: `{}` 45 | 46 | Turn on or off source compression with default options. If an `Object` is specified, it is passed as options to `UglifyJS.Compressor()`. 47 | 48 | #### beautify 49 | Type: `Boolean` `Object` 50 | Default: `false` 51 | 52 | Turns on beautification of the generated source code. An `Object` will be merged and passed with the options sent to `UglifyJS.OutputStream()` 53 | 54 | #### report 55 | Choices: `false` `'min'` `'gzip'` 56 | Default: `false` 57 | 58 | Either do not report anything, report only minification result, or report minification and gzip results. This is useful to see exactly how well Uglify is performing, but using `'gzip'` can add 5-10x runtime task execution. 59 | 60 | Example ouput using `'gzip'`: 61 | 62 | ``` 63 | Original: 198444 bytes. 64 | Minified: 101615 bytes. 65 | Gzipped: 20084 bytes. 66 | ``` 67 | 68 | #### sourceMap 69 | Type: `Boolean` 70 | Default: `false` 71 | 72 | If `true`, a source map file will be generated in the same directory as the `dest` file. By default it will have the same basename as the `dest` file, but with a `.map` extension. 73 | 74 | #### sourceMapName 75 | Type: `String` `Function` 76 | Default: `undefined` 77 | 78 | To customize the name or location of the generated source map, pass a string to indicate where to write the source map to. If a function is provided, the uglify destination is passed as the argument and the return value will be used as the file name. 79 | 80 | #### sourceMapIn 81 | Type: `String` `Function` 82 | Default: `undefined` 83 | 84 | The location of an input source map from an earlier compilation, e.g. from CoffeeScript. If a function is provided, the 85 | uglify source is passed as the argument and the return value will be used as the sourceMap name. This only makes sense 86 | when there's one source file. 87 | 88 | #### sourceMapIncludeSources 89 | Type: `Boolean` 90 | Default: `false` 91 | 92 | Pass this flag if you want to include the content of source files in the source map as sourcesContent property. 93 | 94 | ###### enclose 95 | Type: `Object` 96 | Default: `undefined` 97 | 98 | Wrap all of the code in a closure with a configurable arguments/parameters list. 99 | Each key-value pair in the `enclose` object is effectively an argument-parameter pair. 100 | 101 | #### wrap 102 | Type: `String` 103 | Default: `undefined` 104 | 105 | Wrap all of the code in a closure, an easy way to make sure nothing is leaking. 106 | For variables that need to be public `exports` and `global` variables are made available. 107 | The value of wrap is the global variable exports will be available as. 108 | 109 | #### exportAll 110 | Type: `Boolean` 111 | Default: `false` 112 | 113 | When using `wrap` this will make all global functions and variables available via the export variable. 114 | 115 | #### preserveComments 116 | Type: `Boolean` `String` `Function` 117 | Default: `undefined` 118 | Options: `false` `'all'` `'some'` 119 | 120 | Turn on preservation of comments. 121 | 122 | - `false` will strip all comments 123 | - `'all'` will preserve all comments in code blocks that have not been squashed or dropped 124 | - `'some'` will preserve all comments that start with a bang (`!`) or include a closure compiler style directive (`@preserve` `@license` `@cc_on`) 125 | - `Function` specify your own comment preservation function. You will be passed the current node and the current comment and are expected to return either `true` or `false` 126 | 127 | #### banner 128 | Type: `String` 129 | Default: empty string 130 | 131 | This string will be prepended to the beginning of the minified output. It is processed using [grunt.template.process][], using the default options. 132 | 133 | #### footer 134 | Type: `String` 135 | Default: empty string 136 | 137 | This string will be append to the end of the minified output. It is processed using [grunt.template.process][], using the default options. 138 | 139 | _(Default processing options are explained in the [grunt.template.process][] documentation)_ 140 | 141 | [grunt.template.process]: http://gruntjs.com/api/grunt.template#grunt.template.process 142 | 143 | 144 | ### Usage examples 145 | 146 | #### Basic compression 147 | 148 | This configuration will compress and mangle the input files using the default options. 149 | 150 | ```js 151 | // Project configuration. 152 | grunt.initConfig({ 153 | uglify: { 154 | my_target: { 155 | files: { 156 | 'dest/output.min.js': ['src/input1.js', 'src/input2.js'] 157 | } 158 | } 159 | } 160 | }); 161 | ``` 162 | 163 | #### No mangling 164 | 165 | Specify `mangle: false` to prevent changes to your variable and function names. 166 | 167 | ```js 168 | // Project configuration. 169 | grunt.initConfig({ 170 | uglify: { 171 | options: { 172 | mangle: false 173 | }, 174 | my_target: { 175 | files: { 176 | 'dest/output.min.js': ['src/input.js'] 177 | } 178 | } 179 | } 180 | }); 181 | ``` 182 | 183 | #### Reserved identifiers 184 | 185 | You can specify identifiers to leave untouched with an `except` array in the `mangle` options. 186 | 187 | ```js 188 | // Project configuration. 189 | grunt.initConfig({ 190 | uglify: { 191 | options: { 192 | mangle: { 193 | except: ['jQuery', 'Backbone'] 194 | } 195 | }, 196 | my_target: { 197 | files: { 198 | 'dest/output.min.js': ['src/input.js'] 199 | } 200 | } 201 | } 202 | }); 203 | ``` 204 | 205 | #### Source maps 206 | 207 | Configure basic source map output by specifying a file path for the `sourceMap` option. 208 | 209 | ```js 210 | // Project configuration. 211 | grunt.initConfig({ 212 | uglify: { 213 | my_target: { 214 | options: { 215 | sourceMap: 'path/to/source-map.js' 216 | }, 217 | files: { 218 | 'dest/output.min.js': ['src/input.js'] 219 | } 220 | } 221 | } 222 | }); 223 | ``` 224 | 225 | #### Advanced source maps 226 | 227 | You can specify the parameters to pass to `UglifyJS.SourceMap()` which will 228 | allow you to configure advanced settings. 229 | 230 | Refer to the [UglifyJS SourceMap Documentation](http://lisperator.net/uglifyjs/codegen#source-map) for more information. 231 | 232 | ```js 233 | // Project configuration. 234 | grunt.initConfig({ 235 | uglify: { 236 | my_target: { 237 | options: { 238 | sourceMap: 'path/to/source-map.js', 239 | sourceMapRoot: 'http://example.com/path/to/src/', // the location to find your original source 240 | sourceMapIn: 'example/coffeescript-sourcemap.js', // input sourcemap from a previous compilation 241 | }, 242 | files: { 243 | 'dest/output.min.js': ['src/input.js'], 244 | }, 245 | }, 246 | }, 247 | }); 248 | ``` 249 | #### Discard console.* functions 250 | 251 | Specify `drop_console: true` as part of the `compress` options to discard calls to `console.*` functions. 252 | 253 | ```js 254 | // Project configuration. 255 | grunt.initConfig({ 256 | uglify: { 257 | options: { 258 | compress: { 259 | drop_console: true 260 | } 261 | }, 262 | my_target: { 263 | files: { 264 | 'dest/output.min.js': ['src/input.js'] 265 | } 266 | } 267 | } 268 | }); 269 | ``` 270 | 271 | #### Beautify 272 | 273 | Specify `beautify: true` to beautify your code for debugging/troubleshooting purposes. 274 | Pass an object to manually configure any other output options passed directly to `UglifyJS.OutputStream()`. 275 | 276 | See [UglifyJS Codegen documentation](http://lisperator.net/uglifyjs/codegen) for more information. 277 | 278 | _Note that manual configuration will require you to explicitly set `beautify: true` if you want traditional, beautified output._ 279 | 280 | ```js 281 | // Project configuration. 282 | grunt.initConfig({ 283 | uglify: { 284 | my_target: { 285 | options: { 286 | beautify: true 287 | }, 288 | files: { 289 | 'dest/output.min.js': ['src/input.js'] 290 | } 291 | }, 292 | my_advanced_target: { 293 | options: { 294 | beautify: { 295 | width: 80, 296 | beautify: true 297 | } 298 | }, 299 | files: { 300 | 'dest/output.min.js': ['src/input.js'] 301 | } 302 | } 303 | } 304 | }); 305 | ``` 306 | 307 | #### Banner comments 308 | 309 | In this example, running `grunt uglify:my_target` will prepend a banner created by interpolating the `banner` template string with the config object. Here, those properties are the values imported from the `package.json` file (which are available via the `pkg` config property) plus today's date. 310 | 311 | _Note: you don't have to use an external JSON file. It's also valid to create the `pkg` object inline in the config. That being said, if you already have a JSON file, you might as well reference it._ 312 | 313 | ```js 314 | // Project configuration. 315 | grunt.initConfig({ 316 | pkg: grunt.file.readJSON('package.json'), 317 | uglify: { 318 | options: { 319 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + 320 | '<%= grunt.template.today("yyyy-mm-dd") %> */' 321 | }, 322 | my_target: { 323 | files: { 324 | 'dest/output.min.js': ['src/input.js'] 325 | } 326 | } 327 | } 328 | }); 329 | ``` 330 | 331 | #### Conditional compilation 332 | 333 | You can also enable UglifyJS conditional compilation. This is commonly used to remove debug code blocks for production builds. 334 | 335 | See [UglifyJS global definitions documentation](http://lisperator.net/uglifyjs/compress#global-defs) for more information. 336 | 337 | ```js 338 | // Project configuration. 339 | grunt.initConfig({ 340 | uglify: { 341 | options: { 342 | compress: { 343 | global_defs: { 344 | "DEBUG": false 345 | }, 346 | dead_code: true 347 | } 348 | }, 349 | my_target: { 350 | files: { 351 | 'dest/output.min.js': ['src/input.js'] 352 | } 353 | } 354 | } 355 | }); 356 | ``` 357 | #### Compiling all files in a folder dynamically 358 | 359 | This configuration will compress and mangle the files dynamically. 360 | 361 | ```js 362 | // Project configuration. 363 | grunt.initConfig({ 364 | uglify: { 365 | my_target: { 366 | files: [{ 367 | expand: true, 368 | cwd: 'src/js', 369 | src: '**/*.js', 370 | dest: 'dest/js' 371 | }] 372 | } 373 | } 374 | }); 375 | ``` 376 | 377 | 378 | ## Release History 379 | 380 | * 2013-01-22   v0.3.2   fix handling of `sourceMapIncludeSources` option. 381 | * 2014-01-20   v0.3.1   fix relative path issue in sourcemaps 382 | * 2014-01-16   v0.3.0   refactor sourcemap support 383 | * 2013-11-09   v0.2.7   prepending banner if sourceMap option not set, addresses 384 | * 2013-11-08   v0.2.6   merged 45, 53, 85 (105 by way of duping 53) Added support for banners in uglified files with sourcemaps Updated docs 385 | * 2013-10-28   v0.2.5   Added warning for banners when using sourcemaps 386 | * 2013-09-02   v0.2.4   updated sourcemap format via /83 387 | * 2013-06-10   v0.2.3   added footer option 388 | * 2013-05-31   v0.2.2   Reverted /56 due to /58 until [chrome/239660](https://code.google.com/p/chromium/issues/detail?id=239660&q=sourcemappingurl&colspec=ID%20Pri%20M%20Iteration%20ReleaseBlock%20Cr%20Status%20Owner%20Summary%20OS%20Modified) [firefox/870361](https://bugzilla.mozilla.org/show_bug.cgi?id=870361) drop 389 | * 2013-05-22   v0.2.1   Bumped uglify to ~2.3.5 /55 /40 Changed sourcemappingUrl syntax /56 Disabled sorting of names for consistent mangling /44 Updated docs for sourceMapRoot /47 /25 390 | * 2013-03-14   v0.2.0   No longer report gzip results by default. Support `report` option. 391 | * 2013-01-30   v0.1.2   Added better error reporting Support for dynamic names of multiple sourcemaps 392 | * 2013-02-15   v0.1.1   First official release for Grunt 0.4.0. 393 | * 2013-01-18   v0.1.1rc6   Updating grunt/gruntplugin dependencies to rc6. Changing in-development grunt/gruntplugin dependency versions from tilde version ranges to specific versions. 394 | * 2013-01-09   v0.1.1rc5   Updating to work with grunt v0.4.0rc5. Switching back to this.files api. 395 | * 2012-11-28   v0.1.0   Work in progress, not yet officially released. 396 | 397 | --- 398 | 399 | Task submitted by ["Cowboy" Ben Alman](http://benalman.com) 400 | 401 | *This file was generated on Thu Jan 23 2014 09:35:35.* 402 | --------------------------------------------------------------------------------