├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── appveyor.yml ├── composer.js ├── docs └── why-use-pipeline │ ├── README.md │ ├── pipe-error.png │ └── pump-error.png ├── index.js ├── lib ├── create-error.js ├── gulp-uglify-error.js ├── log.js └── minify.js ├── package.json └── test ├── composer.js ├── create-error.js ├── err.js ├── minify.js ├── null.js ├── sourcemap.js └── streams.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | components 4 | coverage 5 | .nyc_output 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - '0.10' 5 | - '0.12' 6 | - '4' 7 | - '6' 8 | - '8' 9 | - '10' 10 | matrix: 11 | include: 12 | - node_js: 10 13 | script: npm run lint 14 | script: 15 | - node --throw-deprecation ./node_modules/.bin/tape test/*.js 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | As of version 2.0.0, the CHANGELOG is maintained on [GitHub Releases](https://github.com/terinjokes/gulp-uglify/releases). 2 | 3 | # Change Log 4 | 5 | 6 | ## [1.5.4](https://github.com/terinjokes/gulp-uglify/compare/v1.5.3...v1.5.4) (2016-06-22) 7 | 8 | ## 1.5.3 9 | 10 | - Updated UglifyJS to 2.6.2 11 | 12 | ## 1.5.2 13 | 14 | - Updated UglfiyJS to 2.6.1 15 | 16 | ## 1.5.0 17 | 18 | - Update UglifyJS to 2.6.0. 19 | - CI and dependencies chores. 20 | - Attempt to resolve issue #109 where "ghost" files would appear in generated sourcemaps. 21 | 22 | ## 1.4.2 23 | 24 | - Updated UglifyJS to 2.5.0. 25 | - CI and dependencies chores. 26 | 27 | ## 1.4.1 28 | 29 | - Detect if options is a non-Object and log a warning. 30 | 31 | Older versions of Node.js did not allow Strings to be passed to `Object.keys` leading to errors and confusion to users following certain tutorials. 32 | 33 | ## 1.4.0 34 | 35 | - Deprecated the `preserveComments` option of "some". 36 | - Added the `preserveComments` option of "license" that uses [`uglify-save-license`](https://github.com/shinnn/uglify-save-license). 37 | 38 | ## 1.3.0 39 | 40 | - Updated UglifyJS to 2.4.24. 41 | - Streams3 support via through2 dependency update. 42 | 43 | ## 1.2.0 44 | 45 | - Update dependencies, including UglifyJS to 2.4.19. 46 | 47 | ## 1.1.0 48 | 49 | - Fix sources path in source maps (thanks @floridoo) 50 | - Update UglifyJS to 2.4.16 (thanks @tschaub) 51 | 52 | ## 1.0.0 53 | 54 | - Handle cases where UglifyJS uses e.msg instead of e.message for error codes. Fixes #51. 55 | - Supplement UglifyJS’s source map merging with vinyl-sourcemap-apply to correct issues where `sources` and `sourcesContent` were different. Fixes #43. 56 | - Refactor option parsing and defaults, and calls to uglify-js, to reduce complexity of the main function. 57 | - Added tests for the previously forgotten `preserveComments` option. 58 | - Updated UglifyJS to 2.4.15. 59 | - Changed dependencies to explicit ranges to avoid `node-semver` issues. 60 | 61 | ## 0.3.2 62 | 63 | - Removed the PluginError factory wrapper 64 | - Removed test that was failing due to gulp-util issue. 65 | - Tests should end the streams they are writing to. 66 | - Update dependencies. Fixes #44. Fixes #42. 67 | 68 | ## 0.3.1 69 | 70 | - Fixed homepage URL in npm metadata 71 | - Removes UglifyJS-inserted sourceMappingURL comment [Fixes #39] 72 | - Don’t pass input source map to UglifyJS if there are no mappings 73 | - Added installation instructions 74 | 75 | ## 0.3.0 76 | 77 | - Removed support for old style source maps 78 | - Added support for gulp-sourcemap 79 | - Updated tape development dependency 80 | - Dropped support for Node 0.9 81 | - UglifyJS errors are no longer swallowed 82 | 83 | ## 0.2.1 84 | 85 | - Correct source map output 86 | - Remove `gulp` dependency by using `vinyl` in testing 87 | - Passthrough null files correctly 88 | - Report error if attempting to use a stream-backed file 89 | 90 | ## 0.2.0 91 | 92 | - Dropped support for Node versions less than 0.9 93 | - Switched to using Streams2 94 | - Add support for generating source maps 95 | - Add option for preserving comments 96 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2017 Terin Stock 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-uglify [![][travis-shield-img]][travis-shield][![][appveyor-shield-img]][appveyor-shield][![][npm-dl-shield-img]][npm-shield][![][npm-v-shield-img]][npm-shield][![][coveralls-shield-img]][coveralls-shield] 2 | 3 | > Minify JavaScript with UglifyJS3. 4 | 5 | ## Installation 6 | 7 | Install package with NPM and add it to your development dependencies: 8 | 9 | `npm install --save-dev gulp-uglify` 10 | 11 | ## Usage 12 | 13 | ```javascript 14 | var gulp = require('gulp'); 15 | var uglify = require('gulp-uglify'); 16 | var pipeline = require('readable-stream').pipeline; 17 | 18 | gulp.task('compress', function () { 19 | return pipeline( 20 | gulp.src('lib/*.js'), 21 | uglify(), 22 | gulp.dest('dist') 23 | ); 24 | }); 25 | ``` 26 | 27 | To help properly handle error conditions with Node streams, this project 28 | recommends the use of 29 | [`pipeline`](https://nodejs.org/docs/latest/api/stream.html#stream_stream_pipeline_streams_callback), 30 | from [`readable-stream`](https://github.com/nodejs/readable-stream). 31 | 32 | ## Options 33 | 34 | Most of the [minify options](https://github.com/mishoo/UglifyJS2#minify-options) from 35 | the UglifyJS API are supported. There are a few exceptions: 36 | 37 | 1. The `sourceMap` option must not be set, as it will be automatically configured 38 | based on your Gulp configuration. See the documentation for [Gulp sourcemaps][gulp-sm]. 39 | 40 | [gulp-sm]: https://github.com/gulp-sourcemaps/gulp-sourcemaps#usage 41 | 42 | ## Errors 43 | 44 | `gulp-uglify` emits an 'error' event if it is unable to minify a specific file. 45 | The GulpUglifyError constructor is exported by this plugin for `instanceof` checks. 46 | It contains the following properties: 47 | 48 | - `fileName`: The full file path for the file being minified. 49 | - `cause`: The original UglifyJS error, if available. 50 | 51 | Most UglifyJS error messages have the following properties: 52 | 53 | - `message` (or `msg`) 54 | - `filename` 55 | - `line` 56 | 57 | To see useful error messages, see [Why Use Pipeline?](docs/why-use-pipeline/README.md#why-use-pipeline). 58 | 59 | ## Using a Different UglifyJS 60 | 61 | By default, `gulp-uglify` uses the version of UglifyJS installed as a dependency. 62 | It's possible to configure the use of a different version using the "composer" entry point. 63 | 64 | ```javascript 65 | var uglifyjs = require('uglify-js'); // can be a git checkout 66 | // or another module (such as `uglify-es` for ES6 support) 67 | var composer = require('gulp-uglify/composer'); 68 | var pump = require('pump'); 69 | 70 | var minify = composer(uglifyjs, console); 71 | 72 | gulp.task('compress', function (cb) { 73 | // the same options as described above 74 | var options = {}; 75 | 76 | pump([ 77 | gulp.src('lib/*.js'), 78 | minify(options), 79 | gulp.dest('dist') 80 | ], 81 | cb 82 | ); 83 | }); 84 | ``` 85 | 86 | [travis-shield-img]: https://img.shields.io/travis/terinjokes/gulp-uglify/master.svg?label=Travis%20CI&style=flat-square 87 | [travis-shield]: https://travis-ci.org/terinjokes/gulp-uglify 88 | [appveyor-shield-img]: https://img.shields.io/appveyor/ci/terinjokes/gulp-uglify/master.svg?label=AppVeyor&style=flat-square 89 | [appveyor-shield]: https://ci.appveyor.com/project/terinjokes/gulp-uglify 90 | [npm-dl-shield-img]: https://img.shields.io/npm/dm/gulp-uglify.svg?style=flat-square 91 | [npm-shield]: https://yarnpkg.com/en/package/gulp-uglify 92 | [npm-v-shield-img]: https://img.shields.io/npm/v/gulp-uglify.svg?style=flat-square 93 | [coveralls-shield-img]: https://img.shields.io/coveralls/terinjokes/gulp-uglify/master.svg?style=flat-square 94 | [coveralls-shield]: https://coveralls.io/github/terinjokes/gulp-uglify 95 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "0.10" 4 | - nodejs_version: "0.12" 5 | - nodejs_version: "4" 6 | 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - npm install 10 | 11 | test_script: 12 | - node --version 13 | - npm --version 14 | - npm test 15 | 16 | build: off 17 | 18 | version: "{build}" 19 | -------------------------------------------------------------------------------- /composer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var through = require('through2'); 3 | var minify = require('./lib/minify'); 4 | 5 | module.exports = function(uglify, logger) { 6 | return function(opts) { 7 | var minifier = minify(uglify, logger)(opts); 8 | return through.obj(function(file, encoding, callback) { 9 | var newFile = null; 10 | var err = null; 11 | try { 12 | newFile = minifier(file); 13 | } catch (e) { 14 | err = e; 15 | } 16 | callback(err, newFile); 17 | }); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /docs/why-use-pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Why Use Pipeline? 2 | 3 | When using `pipe` from the Node.js streams, errors are not propagated forward 4 | through the piped streams, and source streams aren’t closed if a destination 5 | stream closed. The [`pipeline`][pipeline] method of the Streams API normalizes 6 | these problems, and properly propagates errors of substreams to the pipeline. 7 | 8 | ## A common gulpfile example 9 | 10 | A common pattern in gulp files is to simply return a Node.js stream, and expect 11 | the gulp tool to handle errors. 12 | 13 | ```javascript 14 | // example of a common gulpfile 15 | var gulp = require('gulp'); 16 | var uglify = require('gulp-uglify'); 17 | 18 | gulp.task('compress', function () { 19 | // returns a Node.js stream, but no handling of error messages 20 | return gulp.src('lib/*.js') 21 | .pipe(uglify()) 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | ``` 25 | 26 | ![pipe error](pipe-error.png) 27 | 28 | There’s an error in one of the JavaScript files, but that error message is the 29 | opposite of helpful. You want to know what file and line contains the error. So 30 | what is this mess? 31 | 32 | When there’s an error in a stream, the Node.js stream fire the 'error' event, 33 | but if there’s no handler for this event, it instead goes to the defined 34 | [uncaught exception][uncaughtException] handler. The default behavior of the 35 | uncaught exception handler is documented: 36 | 37 | > By default, Node.js handles such exceptions by printing the stack trace to 38 | > stderr and exiting. 39 | 40 | ## Handling the Errors 41 | 42 | Since allowing the errors to make it to the uncaught exception handler isn’t 43 | useful, we should handle the exceptions properly. Let’s give that a quick shot. 44 | 45 | ```javascript 46 | var gulp = require('gulp'); 47 | var uglify = require('gulp-uglify'); 48 | 49 | gulp.task('compress', function () { 50 | return gulp.src('lib/*.js') 51 | .pipe(uglify()) 52 | .pipe(gulp.dest('dist')) 53 | .on('error', function(err) { 54 | console.error('Error in compress task', err.toString()); 55 | }); 56 | }); 57 | ``` 58 | 59 | Unfortunately, Node.js stream’s `pipe` function doesn’t forward errors through 60 | the chain, so this error handler only handles the errors given by 61 | `gulp.dest`. Instead we need to handle errors for each stream. 62 | 63 | ```javascript 64 | var gulp = require('gulp'); 65 | var uglify = require('gulp-uglify'); 66 | 67 | gulp.task('compress', function () { 68 | function createErrorHandler(name) { 69 | return function (err) { 70 | console.error('Error from ' + name + ' in compress task', err.toString()); 71 | }; 72 | } 73 | 74 | return gulp.src('lib/*.js') 75 | .on('error', createErrorHandler('gulp.src')) 76 | .pipe(uglify()) 77 | .on('error', createErrorHandler('uglify')) 78 | .pipe(gulp.dest('dist')) 79 | .on('error', createErrorHandler('gulp.dest')); 80 | }); 81 | ``` 82 | 83 | This is a lot of complexity to add in each of your gulp tasks, and it’s easy to 84 | forget to do it. In addition, it’s still not perfect, as it doesn’t properly 85 | signal to gulp’s task system that the task has failed. We can fix this, and we 86 | can handle the other pesky issues with error propagations with streams, but it’s 87 | even more work! 88 | 89 | ## Using pipelines 90 | 91 | The [`pipeline`][pipeline] method is a cheat code of sorts. It’s a wrapper 92 | around the `pipe` functionality that handles these cases for you, so you can 93 | stop hacking on your gulpfiles, and get back to hacking new features into your 94 | app. 95 | 96 | ```javascript 97 | var gulp = require('gulp'); 98 | var uglify = require('gulp-uglify'); 99 | var pipeline = require('readable-stream').pipeline; 100 | 101 | gulp.task('compress', function () { 102 | return pipeline( 103 | gulp.src('lib/*.js'), 104 | uglify(), 105 | gulp.dest('dist') 106 | ); 107 | }); 108 | ``` 109 | 110 | The pipeline method accepts variable number of streams, which it internally 111 | pipes together. It is careful to propagate errors and destroy streams properly. 112 | The gulp task system waits for the returned pipeline stream to end, just like 113 | before, but can now handle errors from the substreams properly. 114 | 115 | ![pump error](pump-error.png) 116 | 117 | Now it’s very clear what plugin the error was from, what the error actually was, 118 | and from what file and line number. 119 | 120 | [pipeline]: https://nodejs.org/api/stream.html#stream_stream_pipeline_streams_callback 121 | [uncaughtException]: https://nodejs.org/api/process.html#process_event_uncaughtexception 122 | -------------------------------------------------------------------------------- /docs/why-use-pipeline/pipe-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terinjokes/gulp-uglify/7b9a7adc6f097a26a984c6598f93e15e286b18e3/docs/why-use-pipeline/pipe-error.png -------------------------------------------------------------------------------- /docs/why-use-pipeline/pump-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terinjokes/gulp-uglify/7b9a7adc6f097a26a984c6598f93e15e286b18e3/docs/why-use-pipeline/pump-error.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var uglify = require('uglify-js'); 3 | var compose = require('./composer'); 4 | var GulpUglifyError = require('./lib/gulp-uglify-error'); 5 | var logger = require('./lib/log'); 6 | 7 | module.exports = function(opts) { 8 | return compose( 9 | uglify, 10 | logger 11 | )(opts); 12 | }; 13 | 14 | module.exports.GulpUglifyError = GulpUglifyError; 15 | -------------------------------------------------------------------------------- /lib/create-error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var GulpUglifyError = require('./gulp-uglify-error'); 3 | 4 | function createError(file, msg, cause) { 5 | var perr = new GulpUglifyError(msg, cause); 6 | perr.plugin = 'gulp-uglify'; 7 | perr.fileName = file.path; 8 | perr.showStack = false; 9 | return perr; 10 | } 11 | 12 | module.exports = createError; 13 | -------------------------------------------------------------------------------- /lib/gulp-uglify-error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var makeErrorCause = require('make-error-cause'); 3 | 4 | var gulpUglifyError = makeErrorCause('GulpUglifyError'); 5 | gulpUglifyError.prototype.toString = function() { 6 | var cause = this.cause || {}; 7 | 8 | return ( 9 | makeErrorCause.BaseError.prototype.toString.call(this) + 10 | (this.fileName ? '\nFile: ' + this.fileName : '') + 11 | (cause.line ? '\nLine: ' + cause.line : '') + 12 | (cause.col ? '\nCol: ' + cause.col : '') 13 | ); 14 | }; 15 | 16 | module.exports = gulpUglifyError; 17 | -------------------------------------------------------------------------------- /lib/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var hasLog = require('has-gulplog'); 3 | var each = require('array-each'); 4 | 5 | var levels = ['debug', 'info', 'warn', 'error']; 6 | 7 | each(levels, function(level) { 8 | module.exports[level] = function() { 9 | if (hasLog()) { 10 | var log = require('gulplog'); 11 | 12 | log[level].apply(log, arguments); 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /lib/minify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Buffer = require('safe-buffer').Buffer; 3 | var applySourceMap = require('vinyl-sourcemaps-apply'); 4 | var isObject = require('isobject'); 5 | var extend = require('extend-shallow'); 6 | var createError = require('./create-error'); 7 | 8 | module.exports = function(uglify, log) { 9 | function setup(opts) { 10 | if (opts && !isObject(opts)) { 11 | log.warn('gulp-uglify expects an object, non-object provided'); 12 | opts = {}; 13 | } 14 | 15 | return extend( 16 | {}, 17 | { 18 | output: {} 19 | }, 20 | opts 21 | ); 22 | } 23 | 24 | return function(opts) { 25 | return function(file) { 26 | var options = setup(opts || {}); 27 | var hasSourceMaps = Boolean(file.sourceMap); 28 | 29 | if (file.isNull()) { 30 | return file; 31 | } 32 | 33 | if (file.isStream()) { 34 | throw createError(file, 'Streaming not supported', null); 35 | } 36 | 37 | if (hasSourceMaps) { 38 | options.sourceMap = { 39 | filename: file.sourceMap.file, 40 | includeSources: true 41 | }; 42 | 43 | // UglifyJS generates broken source maps if the input source map 44 | // does not contain mappings. 45 | if (file.sourceMap.mappings) { 46 | options.sourceMap.content = file.sourceMap; 47 | } 48 | } 49 | 50 | var fileMap = {}; 51 | fileMap[file.relative] = String(file.contents); 52 | 53 | var mangled = uglify.minify(fileMap, options); 54 | 55 | if (!mangled || mangled.error) { 56 | throw createError( 57 | file, 58 | 'unable to minify JavaScript', 59 | mangled && mangled.error 60 | ); 61 | } 62 | 63 | if (mangled.warnings) { 64 | mangled.warnings.forEach(function(warning) { 65 | log.warn('gulp-uglify [%s]: %s', file.relative, warning); 66 | }); 67 | } 68 | 69 | file.contents = Buffer.from(mangled.code); 70 | 71 | if (hasSourceMaps) { 72 | var sourceMap = JSON.parse(mangled.map); 73 | applySourceMap(file, sourceMap); 74 | } 75 | 76 | return file; 77 | }; 78 | }; 79 | }; 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-uglify", 3 | "description": "Minify files with UglifyJS.", 4 | "version": "3.0.2", 5 | "author": "Terin Stock ", 6 | "bugs": "https://github.com/terinjokes/gulp-uglify/issues", 7 | "dependencies": { 8 | "array-each": "^1.0.1", 9 | "extend-shallow": "^3.0.2", 10 | "gulplog": "^1.0.0", 11 | "has-gulplog": "^0.1.0", 12 | "isobject": "^3.0.1", 13 | "make-error-cause": "^1.1.1", 14 | "safe-buffer": "^5.1.2", 15 | "through2": "^2.0.0", 16 | "uglify-js": "^3.0.5", 17 | "vinyl-sourcemaps-apply": "^0.2.0" 18 | }, 19 | "devDependencies": { 20 | "eslint": "^3.18.0", 21 | "eslint-config-prettier": "^2.1.0", 22 | "eslint-config-xo": "^0.18.1", 23 | "eslint-plugin-no-use-extend-native": "^0.3.12", 24 | "eslint-plugin-prettier": "^2.0.1", 25 | "eslint-plugin-unicorn": "^2.1.0", 26 | "power-assert": "^1.4.1", 27 | "prettier": "^1.1.0", 28 | "source-list-map": "^1.1.2", 29 | "tape": "^4.9.1", 30 | "tape-catch": "^1.0.6", 31 | "testdouble": "^2.1.2", 32 | "vinyl": "^2.0.0" 33 | }, 34 | "homepage": "https://github.com/terinjokes/gulp-uglify/", 35 | "keywords": [ 36 | "gulpplugin" 37 | ], 38 | "license": "MIT", 39 | "main": "index.js", 40 | "repository": "terinjokes/gulp-uglify", 41 | "eslintConfig": { 42 | "env": { 43 | "node": true 44 | }, 45 | "extends": [ 46 | "xo", 47 | "prettier" 48 | ], 49 | "plugins": [ 50 | "unicorn", 51 | "no-use-extend-native", 52 | "prettier" 53 | ], 54 | "rules": { 55 | "prettier/prettier": [ 56 | "error", 57 | { 58 | "printWidth": 80, 59 | "tabWidth": 2, 60 | "singleQuote": true, 61 | "trailingComma": "none", 62 | "bracketSpacing": false 63 | } 64 | ] 65 | } 66 | }, 67 | "files": [ 68 | "index.js", 69 | "composer.js", 70 | "lib/" 71 | ], 72 | "scripts": { 73 | "lint": "eslint *.js lib test", 74 | "test": "tape test/*.js" 75 | }, 76 | "greenkeeper": { 77 | "ignore": [ 78 | "gulp-sourcemaps" 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/composer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var Buffer = require('safe-buffer').Buffer; 5 | var Vinyl = require('vinyl'); 6 | var td = require('testdouble'); 7 | var through = require('through2'); 8 | var composer = require('../composer'); 9 | var GulpUglifyError = require('../lib/gulp-uglify-error'); 10 | 11 | test('composer should forward errors', function(t) { 12 | var badJsFile = new Vinyl({ 13 | cwd: '/', 14 | base: '/test/', 15 | path: '/test/file.js', 16 | contents: Buffer.from('invalid js') 17 | }); 18 | 19 | var uglify = td.object(['minify']); 20 | var logger = td.object(['warn']); 21 | var composed = composer(uglify, logger)({}); 22 | 23 | assert.throws(function() { 24 | composed.write(badJsFile); 25 | }, GulpUglifyError); 26 | 27 | td.reset(); 28 | t.end(); 29 | }); 30 | 31 | test("compose doesn't invoke callback twice", function(t) { 32 | var expectedErr = new Error(); 33 | var jsFile = new Vinyl({ 34 | cwd: '/', 35 | base: '/test/', 36 | path: '/test/file.js', 37 | contents: Buffer.from('var x = 123') 38 | }); 39 | 40 | var thrower = through.obj(function() { 41 | throw expectedErr; 42 | }); 43 | 44 | var uglify = td.object(['minify']); 45 | var logger = td.object(['warn']); 46 | td.when( 47 | uglify.minify( 48 | {'file.js': 'var x = 123'}, 49 | { 50 | output: {} 51 | } 52 | ) 53 | ).thenReturn({ 54 | code: '', 55 | map: {} 56 | }); 57 | 58 | var composed = composer(uglify, logger)({}); 59 | composed.pipe(thrower); 60 | 61 | assert.throws( 62 | function() { 63 | composed.write(jsFile); 64 | }, 65 | function(err) { 66 | assert.strictEqual(err, expectedErr); 67 | return true; 68 | } 69 | ); 70 | 71 | td.reset(); 72 | t.end(); 73 | }); 74 | -------------------------------------------------------------------------------- /test/create-error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var Buffer = require('safe-buffer').Buffer; 5 | var Vinyl = require('vinyl'); 6 | var createError = require('../lib/create-error'); 7 | var GulpUglifyError = require('../lib/gulp-uglify-error'); 8 | 9 | test('createError has error message', function(t) { 10 | var e = createError(createTestFile(), 'error message text', null); 11 | 12 | assert.ok(e instanceof Error, 'argument should be of type Error'); 13 | assert.ok( 14 | e instanceof GulpUglifyError, 15 | 'argument should be of type GulpUglifyError' 16 | ); 17 | assert.equal(e.plugin, 'gulp-uglify', 'error is from gulp-uglify'); 18 | assert.equal(e.message, 'error message text'); 19 | assert.ok(!e.cause, 'should not contain a cause'); 20 | 21 | t.end(); 22 | }); 23 | 24 | test('createError wraps cause', function(t) { 25 | var cause = new Error('boom!'); 26 | var e = createError(createTestFile(), 'error message text', cause); 27 | 28 | assert.ok(e instanceof Error, 'argument should be of type Error'); 29 | assert.ok( 30 | e instanceof GulpUglifyError, 31 | 'argument should be of type GulpUglifyError' 32 | ); 33 | assert.equal(e.plugin, 'gulp-uglify', 'error is from gulp-uglify'); 34 | assert.ok(e.message.match(/^error message text/)); 35 | assert.equal(e.cause, cause); 36 | 37 | t.end(); 38 | }); 39 | 40 | function createTestFile() { 41 | var testOkContentsInput = 42 | '"use strict"; (function(console, first, second) { console.log(first + second) }(5, 10))'; 43 | 44 | return new Vinyl({ 45 | cwd: '/home/terin/broken-promises/', 46 | base: '/home/terin/broken-promises/test', 47 | path: '/home/terin/broken-promises/test/test2.js', 48 | contents: Buffer.from(testOkContentsInput) 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /test/err.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var Buffer = require('safe-buffer').Buffer; 5 | var Vinyl = require('vinyl'); 6 | var td = require('testdouble'); 7 | var GulpUglifyError = require('../lib/gulp-uglify-error'); 8 | var minify = require('../lib/minify'); 9 | 10 | test('errors should report files in error', function(t) { 11 | var testFile = new Vinyl({ 12 | cwd: '/home/terin/broken-promises/', 13 | base: '/home/terin/broken-promises/test', 14 | path: '/home/terin/broken-promises/test/test1.js', 15 | contents: Buffer.from('function errorFunction(error)\n{') 16 | }); 17 | var uglify = td.object(['minify']); 18 | var logger = td.object(['warn']); 19 | var expOptions = { 20 | output: {} 21 | }; 22 | var err = new Error(); 23 | err.line = 28889; 24 | 25 | td.when( 26 | uglify.minify( 27 | { 28 | 'test1.js': 'function errorFunction(error)\n{' 29 | }, 30 | expOptions 31 | ) 32 | ).thenReturn({ 33 | error: err 34 | }); 35 | 36 | var subject = minify(uglify, logger)({}); 37 | 38 | assert.throws( 39 | function() { 40 | subject(testFile); 41 | }, 42 | function(err) { 43 | assert.ok( 44 | err instanceof GulpUglifyError, 45 | 'argument should be of type GulpUglifyError' 46 | ); 47 | assert.equal(err.plugin, 'gulp-uglify', 'error is from gulp-uglify'); 48 | assert.equal( 49 | err.fileName, 50 | testFile.path, 51 | 'error reports correct file name' 52 | ); 53 | assert.equal(err.cause.line, 28889, 'error reports correct line number'); 54 | assert.ok(err.stack, 'error has a stack'); 55 | assert.ok(!err.showStack, 'error is configured to not print the stack'); 56 | assert.ok(err instanceof Error, 'argument should be of type Error'); 57 | 58 | return true; 59 | } 60 | ); 61 | 62 | td.verify(logger.warn(), {times: 0, ignoreExtraArgs: true}); 63 | td.reset(); 64 | t.end(); 65 | }); 66 | 67 | test("errors shouldn't blow up", function(t) { 68 | var testFile = new Vinyl({ 69 | cwd: '/home/terin/broken-promises/', 70 | base: '/home/terin/broken-promises/test', 71 | path: '/home/terin/broken-promises/test/test1.js', 72 | contents: Buffer.from('{}') 73 | }); 74 | var uglify = td.object(['minify']); 75 | var logger = td.object(['warn']); 76 | var expOptions = { 77 | output: { 78 | exportAll: true 79 | } 80 | }; 81 | var err = new Error('`exportAll` is not a supported option'); 82 | 83 | td.when( 84 | uglify.minify( 85 | { 86 | 'test1.js': '{}' 87 | }, 88 | expOptions 89 | ) 90 | ).thenReturn({ 91 | error: err 92 | }); 93 | 94 | var subject = minify(uglify, logger)({ 95 | output: { 96 | exportAll: true 97 | } 98 | }); 99 | 100 | assert.throws( 101 | function() { 102 | subject(testFile); 103 | }, 104 | function(err) { 105 | assert.ok(err instanceof Error, 'argument should be of type Error'); 106 | assert.ok( 107 | err instanceof GulpUglifyError, 108 | 'argument should be of type GulpUglifyError' 109 | ); 110 | assert.equal(err.cause.message, '`exportAll` is not a supported option'); 111 | assert.equal(err.plugin, 'gulp-uglify', 'error is from gulp-uglify'); 112 | assert.equal( 113 | err.fileName, 114 | testFile.path, 115 | 'error reports correct file name' 116 | ); 117 | assert.ok(!err.showStack, 'error is configured to not print the stack'); 118 | 119 | return true; 120 | } 121 | ); 122 | 123 | td.verify(logger.warn(), {times: 0, ignoreExtraArgs: true}); 124 | td.reset(); 125 | t.end(); 126 | }); 127 | -------------------------------------------------------------------------------- /test/minify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var Vinyl = require('vinyl'); 5 | var Buffer = require('safe-buffer').Buffer; 6 | var td = require('testdouble'); 7 | var minify = require('../lib/minify'); 8 | 9 | test('minify should work', function(t) { 10 | var testContentsInput = 11 | '"use strict"; (function(console, first, second) { console.log(first + second) }(5, 10))'; 12 | var testFile = createTestFile(testContentsInput); 13 | var uglify = td.object(['minify']); 14 | var logger = td.object(['warn']); 15 | 16 | td.when( 17 | uglify.minify( 18 | { 19 | 'test1.js': testContentsInput 20 | }, 21 | { 22 | output: {} 23 | } 24 | ) 25 | ).thenReturn({ 26 | code: 'foobar' 27 | }); 28 | 29 | var subject = minify(uglify, logger)({}); 30 | var file = subject(testFile); 31 | 32 | assert.ok(file instanceof Vinyl); 33 | assert.ok(Buffer.isBuffer(file.contents)); 34 | assert.equal(String(file.contents), 'foobar'); 35 | 36 | td.verify(logger.warn(), { 37 | times: 0, 38 | ignoreExtraArgs: true 39 | }); 40 | td.reset(); 41 | t.end(); 42 | }); 43 | 44 | test('minify should warn with string argument', function(t) { 45 | var testContentsInput = 46 | '"use strict"; (function(console, first, second) { console.log(first + second) }(5, 10))'; 47 | var testFile = createTestFile(testContentsInput); 48 | var uglify = td.object(['minify']); 49 | var logger = td.object(['warn']); 50 | 51 | td.when( 52 | uglify.minify( 53 | { 54 | 'test1.js': testContentsInput 55 | }, 56 | { 57 | output: {} 58 | } 59 | ) 60 | ).thenReturn({ 61 | code: 'foobar' 62 | }); 63 | 64 | var subject = minify(uglify, logger)('build.min.js'); 65 | var file = subject(testFile); 66 | 67 | td.verify(logger.warn('gulp-uglify expects an object, non-object provided')); 68 | 69 | assert.ok(file instanceof Vinyl); 70 | assert.ok(Buffer.isBuffer(file.contents)); 71 | assert.equal(String(file.contents), 'foobar'); 72 | 73 | td.reset(); 74 | t.end(); 75 | }); 76 | 77 | function createTestFile(input) { 78 | return new Vinyl({ 79 | cwd: '/home/terin/broken-promises/', 80 | base: '/home/terin/broken-promises/test', 81 | path: '/home/terin/broken-promises/test/test1.js', 82 | contents: Buffer.from(input) 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /test/null.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var Vinyl = require('vinyl'); 5 | var td = require('testdouble'); 6 | var minify = require('../lib/minify'); 7 | 8 | test('null Vinyl should passthrough', function(t) { 9 | var testFile = new Vinyl({ 10 | cwd: '/home/terin/broken-promises/', 11 | base: '/home/terin/broken-promises/test', 12 | path: '/home/terin/broken-promises/test/test1.js', 13 | contents: null 14 | }); 15 | var uglify = td.object(['minify']); 16 | var logger = td.object(['warn']); 17 | 18 | var subject = minify(uglify, logger)({}); 19 | 20 | var file = subject(testFile); 21 | 22 | assert.strictEqual(file, testFile); 23 | 24 | td.verify(logger.warn(), {times: 0, ignoreExtraArgs: true}); 25 | td.verify(uglify.minify(), {times: 0, ignoreExtraArgs: true}); 26 | td.reset(); 27 | t.end(); 28 | }); 29 | -------------------------------------------------------------------------------- /test/sourcemap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var Buffer = require('safe-buffer').Buffer; 5 | var Vinyl = require('vinyl'); 6 | var SourceListMap = require('source-list-map').SourceListMap; 7 | var fromStringWithSourceMap = require('source-list-map') 8 | .fromStringWithSourceMap; 9 | var td = require('testdouble'); 10 | var minify = require('../lib/minify'); 11 | 12 | test('sourcemaps should be merged', function(t) { 13 | var testContents1Input = 14 | '(function(first, second) {\n console.log(first + second);\n}(5, 10));\n'; 15 | var testFile = new Vinyl({ 16 | cwd: '/home/terin/broken-promises/', 17 | base: '/home/terin/broken-promises/test', 18 | path: '/home/terin/broken-promises/test/test1.js', 19 | contents: Buffer.from(testContents1Input) 20 | }); 21 | 22 | var originalMap = new SourceListMap(); 23 | originalMap.add(testContents1Input, 'test1.js', testContents1Input); 24 | testFile.sourceMap = originalMap.toStringWithSourceMap({ 25 | file: 'test1.js' 26 | }).map; 27 | 28 | var outMap = fromStringWithSourceMap( 29 | 'foobar', 30 | testFile.sourceMap 31 | ).toStringWithSourceMap({file: 'test1.js'}); 32 | 33 | var uglify = td.object(['minify']); 34 | var logger = td.object(['logger']); 35 | 36 | td.when( 37 | uglify.minify( 38 | { 39 | 'test1.js': testContents1Input 40 | }, 41 | { 42 | output: {}, 43 | sourceMap: { 44 | filename: 'test1.js', 45 | includeSources: true, 46 | content: testFile.sourceMap 47 | } 48 | } 49 | ) 50 | ).thenReturn({ 51 | code: 'foobar', 52 | map: JSON.stringify(outMap.map) 53 | }); 54 | 55 | var subject = minify(uglify, logger)({}); 56 | var file = subject(testFile); 57 | 58 | assert.ok(file instanceof Vinyl); 59 | assert.ok(Buffer.isBuffer(file.contents), 'file contents are a buffer'); 60 | 61 | assert.equal(String(file.contents), 'foobar'); 62 | 63 | assert.ok(file.sourceMap, 'has a source map'); 64 | assert.equal(file.sourceMap.version, 3, 'source map has expected version'); 65 | assert.ok( 66 | Array.isArray(file.sourceMap.sources), 67 | 'source map has sources array' 68 | ); 69 | assert.ok(Array.isArray(file.sourceMap.names), 'source maps has names array'); 70 | assert.ok(file.sourceMap.mappings, 'source map has mappings'); 71 | td.reset(); 72 | t.end(); 73 | }); 74 | 75 | test('sourcemaps should merge when concatted', function(t) { 76 | var inMap = new SourceListMap(); 77 | inMap.add('foo\n', 'foo.js', 'foo\n'); 78 | inMap.add('bar\n', 'bar.js', 'bar\n'); 79 | 80 | var testFile = new Vinyl({ 81 | cwd: '/home/terin/broken-promises/', 82 | base: '/home/terin/broken-promises/test', 83 | path: '/home/terin/broken-promises/test/test1.js', 84 | contents: Buffer.from(String(inMap)) 85 | }); 86 | testFile.sourceMap = inMap.toStringWithSourceMap({file: 'test1.js'}).map; 87 | 88 | var outMap = new SourceListMap(); 89 | outMap.add(' ', 'foo.js', 'foo\n'); 90 | outMap.add(' ', 'bar.js', 'bar\n'); 91 | outMap = outMap.toStringWithSourceMap({file: 'test1.js'}); 92 | 93 | var uglify = td.object(['minify']); 94 | var logger = td.object(['warn']); 95 | 96 | td.when( 97 | uglify.minify( 98 | { 99 | 'test1.js': String(inMap) 100 | }, 101 | { 102 | output: {}, 103 | sourceMap: { 104 | filename: 'test1.js', 105 | includeSources: true, 106 | content: testFile.sourceMap 107 | } 108 | } 109 | ) 110 | ).thenReturn({ 111 | code: 'send a PR changing this to the best La Croix flavor', 112 | map: JSON.stringify(outMap.map) 113 | }); 114 | 115 | var subject = minify(uglify, logger)({}); 116 | var file = subject(testFile); 117 | 118 | assert.ok(file instanceof Vinyl); 119 | assert.ok(Buffer.isBuffer(file.contents), 'file contents are a buffer'); 120 | assert.equal( 121 | String(file.contents), 122 | 'send a PR changing this to the best La Croix flavor' 123 | ); 124 | 125 | assert.ok(file.sourceMap, 'has a source map'); 126 | assert.equal(file.sourceMap.version, 3, 'source map has expected version'); 127 | assert.ok( 128 | Array.isArray(file.sourceMap.sources), 129 | 'source map has sources array' 130 | ); 131 | assert.deepEqual( 132 | file.sourceMap.sources, 133 | ['foo.js', 'bar.js'], 134 | 'sources array has the inputs' 135 | ); 136 | assert.ok(Array.isArray(file.sourceMap.names), 'source maps has names array'); 137 | assert.ok(file.sourceMap.mappings, 'source map has mappings'); 138 | td.reset(); 139 | t.end(); 140 | }); 141 | -------------------------------------------------------------------------------- /test/streams.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape-catch'); 3 | var assert = require('assert'); 4 | var GulpUglifyError = require('../lib/gulp-uglify-error'); 5 | var td = require('testdouble'); 6 | var minify = require('../lib/minify'); 7 | 8 | test('stream Vinyls should emit error', function(t) { 9 | var uglify = td.object(['minify']); 10 | var logger = td.object(['warn']); 11 | var file = td.object(['isNull', 'isStream']); 12 | 13 | var subject = minify(uglify, logger)({}); 14 | 15 | td.when(file.isNull()).thenReturn(false); 16 | td.when(file.isStream()).thenReturn(true); 17 | 18 | assert.throws(function() { 19 | subject(file); 20 | }, GulpUglifyError); 21 | td.reset(); 22 | t.end(); 23 | }); 24 | --------------------------------------------------------------------------------