Repository: hughsk/uglifyify Branch: master Commit: 73e72d83ada8 Files: 7 Total size: 15.1 KB Directory structure: gitextract_71oo8zc1/ ├── .gitignore ├── LICENSE.md ├── README.md ├── index.js ├── package.json └── test/ ├── fixture.js └── index.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules/ ================================================ FILE: LICENSE.md ================================================ This software is released under the MIT license: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # uglifyify A [Browserify](http://browserify.org) v2 transform which minifies your code using [terser](https://github.com/fabiosantoscode/terser) (a maintained fork of uglify-es). ## Installation ``` bash npm install uglifyify ``` ## Motivation/Usage Ordinarily you'd be fine doing this: ``` bash browserify index.js | uglifyjs -c > bundle.js ``` But uglifyify is able to yield smaller output by processing files individually instead of just the entire bundle. When using uglifyify you should generally **also** use Uglify, to achieve the smallest output. Uglifyify provides an additional optimization when used with Uglify, but does not provide all of the optimization that using Uglify on its own does, so it's not a replacement. Uglifyify gives you the benefit of applying Uglify's "squeeze" transform on each file *before* it's included in the bundle, meaning you can remove dead code paths for conditional requires. Here's a contrived example: ``` javascript if (true) { module.exports = require('./browser') } else { module.exports = require('./node') } ``` `module.exports = require('./node')` will be excluded by Uglify, meaning that only `./browser` will be bundled and required. If you combine uglifyify with [envify](http://github.com/hughsk/envify), you can make this a little more accessible. Take this code: ``` javascript if (process.env.NODE_ENV === 'development') { module.exports = require('./development') } else { module.exports = require('./production') } ``` And use this to compile: ``` bash NODE_ENV=development browserify -t envify -t uglifyify index.js -o dev.js && NODE_ENV=production browserify -t envify -t uglifyify index.js -o prod.js ``` It should go without saying that you should be hesitant using environment variables in a Browserify module - this is best suited to your own applications or modules built with Browserify's `--standalone` tag. ## File Extensions Sometimes, you don't want uglifyify to minify all of your files – for example, if you're using a transform to `require` CSS or HTML, you might get an error as uglify expects JavaScript and will throw if it can't parse what it's given. This is done using the `-x` or `--exts` transform options, e.g. from the command-line: ``` bash browserify \ -t coffeeify \ -t [ uglifyify -x .js -x .coffee ] ``` The above example will only minify `.js` and `.coffee` files, ignoring the rest. ## Global Transforms You might also want to take advantage of uglifyify's pre-bundle minification to produce slightly leaner files across your entire browserify bundle. By default, transforms only alter your application code, but you can use global transforms to minify module code too. From your terminal: ``` bash browserify -g uglifyify ./index.js > bundle.js ``` Or programatically: ``` javascript var browserify = require('browserify') var fs = require('fs') var bundler = browserify(__dirname + '/index.js') bundler.transform('uglifyify', { global: true }) bundler.bundle() .pipe(fs.createWriteStream(__dirname + '/bundle.js')) ``` Note that this is fine for uglifyify as it shouldn't modify the behavior of your code unexpectedly, but transforms such as envify should almost always stay local – otherwise you'll run into unexpected side-effects within modules that weren't expecting to be modified as such. ## Ignoring Files Sometimes uglifyjs will break specific files under specific settings – it's rare, but does happen – and to work around that, you can use the `ignore` option. Given one or more glob patterns, you can filter out specific files this way: ``` bash browserify -g [ uglifyify --ignore '**/node_modules/weakmap/*' ] ./index.js ``` ``` javascript var bundler = browserify('index.js') bundler.transform('uglifyify', { global: true, ignore: [ '**/node_modules/weakmap/*' , '**/node_modules/async/*' ] }) bundler.bundle().pipe(process.stdout) ``` ## Source Maps Uglifyify supports source maps, so you can minify your code and still see the original source – this works especially well with a tool such as [exorcist](https://github.com/thlorenz/exorcist) when creating production builds. Source maps are enabled when: * You're using another transform, such as [coffeeify](https://github.com/jnordberg/coffeeify), that inlines source maps. * You've passed the `--debug` flag (or `debug` option) to your browserify bundle. Enabling `--debug` with browserify is easy: ``` bash browserify -t uglifyify --debug index.js ``` ``` javascript var bundler = browserify({ debug: true }) bundler .add('index.js') .transform('uglifyify') .bundle() .pipe(process.stdout) ``` If you'd prefer them not to be included regardless, you can opt out using the `sourcemap` option: ``` bash browserify -t [ uglifyify --no-sourcemap ] app.js ``` ``` javascript var bundler = browserify('index.js') bundler.transform('uglifyify', { sourceMap: false }) .bundle() .pipe(process.stdout) ``` ================================================ FILE: index.js ================================================ var minimatch = require('minimatch').Minimatch , convert = require('convert-source-map') , through = require('through') , path = require('path') , ujs = require('terser') , xtend = require('xtend') module.exports = uglifyify function uglifyify(file, opts) { opts = xtend(opts || {}) var debug = opts._flags && opts._flags.debug if (ignore(file, opts.ignore)) { return through() } var buffer = '' var exts = [] .concat(opts.exts || []) .concat(opts.x || []) .map(function(d) { if (d.charAt(0) === '.') return d return '.' + d }) if ( /\.json$/.test(file) || exts.length && exts.indexOf(path.extname(file)) === -1 ) { return through() } // remove exts before passing opts to uglify delete opts.global delete opts.exts delete opts.x return through(function write(chunk) { buffer += chunk }, capture(function ready() { debug = opts.sourceMap !== false && debug opts = xtend({ compress: true, mangle: true, sourceMap: { filename: file } }, opts) // map out command line options to uglify compatible ones mapArgv(opts) if (typeof opts.compress === 'object') { delete opts.compress._ } if (debug) opts.sourceMap.url = 'out.js.map' // Check if incoming source code already has source map comment. // If so, send it in to ujs.minify as the inSourceMap parameter if (debug) { opts.sourceMap.content = 'inline' } var min = ujs.minify(buffer, opts) // we should catcch the min error if it comes back and end the stream if (min.error) return this.emit('error', min.error) // Uglify leaves a source map comment pointing back to "out.js.map", // which we want to get rid of because it confuses browserify. min.code = min.code.replace(/\/\/[#@] ?sourceMappingURL=out.js.map$/, '') this.queue(min.code) if (min.map && min.map !== 'null') { var map = convert.fromJSON(min.map) map.setProperty('sources', [path.basename(file)]) this.queue('\n') this.queue(map.toComment()) } this.queue(null) })) function capture(fn) { return function() { try { fn.apply(this, arguments) } catch(err) { return this.emit('error', err) } } } } function ignore(file, list) { if (!list) return list = Array.isArray(list) ? list : [list] return list.some(function(pattern) { var match = minimatch(pattern) return match.match(file) }) } // uglify-es doesn't allow for command line options in javascript api, this // remaps it function mapArgv (opts) { if (opts._flags) { delete opts._flags } if (opts.c) { opts.compress = opts.c delete opts.c } if (opts.m) { opts.mangle = opts.m delete opts.m } if (opts.p) { opts.parse = opts.p delete opts.p } if (opts.b) { opts.beautify = opts.b delete opts.b } if (opts.o) { opts.output = opts.o delete opts.o } if (opts.d) { opts.define = opts.d delete opts.d } delete opts._ } ================================================ FILE: package.json ================================================ { "name": "uglifyify", "version": "5.0.2", "description": "A browserify transform which minifies your code using UglifyJS2", "main": "index.js", "dependencies": { "convert-source-map": "~1.1.0", "minimatch": "^3.0.2", "terser": "^3.7.5", "through": "~2.3.4", "xtend": "^4.0.1" }, "devDependencies": { "bl": "^0.9.3", "browserify": "^8.1.1", "from2": "^1.3.0", "tap-spec": "^2.1.2", "tape": "^3.2.0", "wrap-stream": "^2.0.0" }, "scripts": { "test": "node test | tap-spec" }, "repository": { "type": "git", "url": "git://github.com/hughsk/uglifyify.git" }, "keywords": [ "uglify", "minify", "compress", "compile", "browserify", "transform", "stream" ], "author": "Hugh Kennedy (http://hughskennedy.com/)", "license": "MIT", "readmeFilename": "README.md" } ================================================ FILE: test/fixture.js ================================================ var hello = 'world' if (hello !== 'world') { throw new Error } ================================================ FILE: test/index.js ================================================ const convert = require('convert-source-map') const wrap = require('wrap-stream') const browserify = require('browserify') const uglify = require('terser') const from2 = require('from2') const test = require('tape') const path = require('path') const uglifyify = require('../') const fs = require('fs') const bl = require('bl') test('uglifyify: sanity check', function(t) { var src = path.join(__dirname, 'fixture.js') var orig = fs.readFileSync(src, 'utf8') fs.createReadStream(src) .pipe(uglifyify(src)) .pipe(bl(function(err, data) { if (err) return t.ifError(err) data = String(data) t.notEqual(data.indexOf('var hello'), -1, 'var hello') t.notEqual(data.indexOf('"world"'), -1, '"world"') t.notEqual(data, orig, 'should be minified') t.end() })) }) test('uglifyify: ignores json', function(t) { var src = path.join(__dirname, 'fixture.js') var json = path.join(__dirname, 'fixture.json') var orig = fs.readFileSync(src, 'utf8') fs.createReadStream(src) .pipe(uglifyify(json)) .pipe(bl(buffered)) function buffered(err, data) { if (err) return t.ifError(err) data = String(data) t.equal(data, orig, 'should not be minified') t.end() } }) test('uglifyify: -t [ uglifyify --exts ]', function(t) { var src = path.join(__dirname, 'fixture.js') var orig = fs.readFileSync(src, 'utf8') t.plan(5) check(path.join(__dirname, 'fixture.json'), true) check(path.join(__dirname, 'fixture.obj2'), false) check(path.join(__dirname, 'fixture.mkdn'), false) check(path.join(__dirname, 'fixture.fbla'), true) check(src, true) function check(name, ignored) { fs.createReadStream(src) .pipe(uglifyify(name, { exts: ['mkdn' ], x: ['.obj2'] })) .pipe(bl(buffered)) function buffered(err, data) { if (err) return t.ifError(err) data = String(data) t.ok(ignored ? data === orig : data !== orig , path.extname(name) + ' handled as expected') } } }) test('uglifyify: passes options to uglify', function(t) { var src = path.join(__dirname, 'fixture.js') var orig = fs.readFileSync(src, 'utf8') var buf1 = null fs.createReadStream(src) .pipe(closure()) .pipe(uglifyify(src, { compress: { conditionals: false } })) .pipe(bl(buffered1)) function buffered1(err, _buf1) { if (err) return t.ifError(err) buf1 = String(_buf1) t.notEqual(buf1, orig, 'should be minified') fs.createReadStream(src) .pipe(closure()) .pipe(uglifyify(src)) .pipe(bl(buffered2)) } function buffered2(err, buf2) { if (err) return buf2 = String(buf2) t.notEqual(buf2, orig, 'should be minified') t.notEqual(buf1, buf2, 'options altered output') t.end() } }) function closure() { return wrap('(function(){', '})()') } test('uglifyify: sourcemaps', function(t) { t.plan(10) var src = path.join(__dirname, 'fixture.js') var json = path.join(__dirname, 'fixture.json') var orig = fs.readFileSync(src, 'utf8') var min = uglify.minify(orig, { sourceMap: { url: 'out.js.map' } }) var map = convert.fromJSON(min.map) map.setProperty('sources', [src]) map.setProperty('sourcesContent', [orig]) var mapped = [orig, map.toComment()].join('\n') from2([mapped]) .pipe(uglifyify(json)) .pipe(bl(doneWithMap)) from2([orig]) .pipe(uglifyify(json)) .pipe(bl(doneWithoutMap)) browserify({ entries: [src], debug: true }) .transform(uglifyify) .bundle() .pipe(bl(doneWithMap)) browserify({ entries: [src], debug: false }) .transform(uglifyify) .bundle() .pipe(bl(doneWithoutDebug)) from2([mapped]) .pipe(uglifyify(json, { _flags: { debug: false }})) .pipe(bl(doneWithMapAndNoDebug)) function doneWithMap(err, data) { if (err) return t.ifError(err) data = String(data) t.notEqual(data, orig, 'should have changed') t.equal(data.match(/\/\/[@#]/g).length, 1, 'should have sourcemap') } function doneWithoutMap(err, data) { if (err) return t.ifError(err) data = String(data) t.equal(data, orig, 'should not have changed') t.equal(data.indexOf(/\/\/[@#]/g), -1, 'should not have sourcemap') } function doneWithoutDebug(err, data) { if (err) return t.ifError(err) data = String(data) t.notEqual(data, orig, 'should have changed') t.equal(data.indexOf(/\/\/[@#]/g), -1, 'should not have sourcemap') } function doneWithMapAndNoDebug(err, data) { if (err) return t.ifError(err) data = String(data) t.notEqual(data, orig, 'should have changed') t.equal(data.match(/\/\/[@#]/g).length, 1, 'should have sourcemap') } }) test('uglifyify: transform is stable', function(t) { t.plan(1) var src = path.join(__dirname, 'fixture.js') var opts = { _flags: { debug: false } } var tr1 = fs.createReadStream(src).pipe(uglifyify(src, opts)) var tr2 = fs.createReadStream(src).pipe(uglifyify(src, opts)) tr1.pipe(bl(function(err, data) { if (err) return t.ifError(err) var data1 = String(data) tr2.pipe(bl(function(err, data) { if (err) return t.ifError(err) var data2 = String(data) t.equal(data2, data1, 'repeated runs should be the same') t.end() })) })) })