Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions .travis.yml

This file was deleted.

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.0.0
- Breaking: Replaced `uglify-es` with `terser` ([#66](https://github.com/gdborton/webpack-parallel-uglify-plugin/pull/66))
- Breaking: Update to webpack v5 api ([#68](https://github.com/gdborton/webpack-parallel-uglify-plugin/pull/68))
- Chore: Updated dependencies to address identified vulnerabilities. ([#65](https://github.com/gdborton/webpack-parallel-uglify-plugin/pull/65))

## 1.1.2
- Enable source maps by default. ([#61](https://github.com/gdborton/webpack-parallel-uglify-plugin/pull/61))

Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# webpack-parallel-uglify-plugin [![Build Status](https://travis-ci.org/gdborton/webpack-parallel-uglify-plugin.svg?branch=master)](https://travis-ci.org/gdborton/webpack-parallel-uglify-plugin) [![Coverage Status](https://coveralls.io/repos/github/gdborton/webpack-parallel-uglify-plugin/badge.svg?branch=master)](https://coveralls.io/github/gdborton/webpack-parallel-uglify-plugin?branch=master)
# webpack-parallel-uglify-plugin [![Build status](https://ci.appveyor.com/api/projects/status/v1xvpvx0xfumv9fh/branch/master?svg=true)](https://ci.appveyor.com/project/gdborton/webpack-parallel-uglify-plugin/branch/master) [![Coverage Status](https://coveralls.io/repos/github/gdborton/webpack-parallel-uglify-plugin/badge.svg?branch=master)](https://coveralls.io/github/gdborton/webpack-parallel-uglify-plugin?branch=master)

This plugin serves to help projects with many entry points speed up their builds. The UglifyJS plugin provided with webpack runs sequentially on each of the output files. This plugin runs uglify in parallel with one thread for each of your available cpus. This can lead to significantly reduced build times as minification is very CPU intensive.

Expand All @@ -22,16 +22,17 @@ module.exports = {
sourceMap, // Optional Boolean. This slows down the compilation. Defaults to false.
uglifyJS: {
// These pass straight through to uglify-js@3.
// Cannot be used with uglifyES.
// Defaults to {} if not neither uglifyJS or uglifyES are provided.
// You should use this option if you need to ensure es5 support. uglify-js will produce an error message
// if it comes across any es6 code that it can't parse.
// Cannot be used with terser.
// Defaults to {} if not neither uglifyJS or terser are provided.
// You should use this option if you need to ensure es5 support. uglify-js will produce an
// error message if it comes across any es6 code that it can't parse.
},
uglifyES: {
// These pass straight through to uglify-es.
terser: {
// These pass straight through to terser.
// Cannot be used with uglifyJS.
// uglify-es is a version of uglify that understands newer es6 syntax. You should use this option if the
// files that you're minifying do not need to run in older browsers/versions of node.
// terser is a fork of uglify-es, a version of uglify that supports ES6+ version of uglify
// that understands newer es6 syntax. You should use this option if the files that you're
// minifying do not need to run in older browsers/versions of node.
}
}),
],
Expand Down
26 changes: 26 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
image:
- Ubuntu

environment:
# Test against these versions of Node.js and io.js
matrix:
# node.js
- nodejs_version: "10"
- nodejs_version: "12"
- nodejs_version: "14"

# Install scripts. (runs after repo cloning)
install:
- nvm install $nodejs_version
- npm install

# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test

after_test:
- './node_modules/.bin/nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls'
42 changes: 25 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
const uglifier = require('./lib/uglifier');
const uglifier = require("./lib/uglifier");

function sourceMapError(lib) {
return `You should not pass options.${lib}.sourceMap, did you mean options.sourceMap?`;
}

function FasterUglifyPlugin(options) {
if (options.uglifyJS && options.uglifyES) {
throw new TypeError('You cannot use both uglifyJS and uglifyES for the same plugin.');
function ParallelUglifyPlugin(options) {
if (options.uglifyJS && options.terser) {
throw new TypeError(
"You cannot use both uglifyJS and terser for the same plugin."
);
}

if (options.uglifyJS && options.uglifyJS.sourceMap) {
throw new TypeError(sourceMapError('uglifyJS'));
throw new TypeError(sourceMapError("uglifyJS"));
}

if (options.uglifyES && options.uglifyES.sourceMap) {
throw new TypeError(sourceMapError('uglifyES'));
if (options.terser && options.terser.sourceMap) {
throw new TypeError(sourceMapError("terser"));
}
this.options = options;

if (!(this.options.uglifyJS || this.options.uglifyES)) {
if (!(this.options.uglifyJS || this.options.terser)) {
this.options.uglifyJS = {};
}
}

FasterUglifyPlugin.prototype.apply = function apply(compiler) {
compiler.plugin('compilation', (compilation) => {
compilation.plugin('optimize-chunk-assets', (chunks, callback) => {
uglifier.processAssets(compilation, this.options).then(() => {
callback();
});
});
ParallelUglifyPlugin.prototype.apply = function apply(compiler) {
compiler.hooks.compilation.tap("ParallelUglifyPlugin", (compilation) => {
compilation.hooks.processAssets.tapAsync(
{
name: "ParallelUglifyPlugin",
stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
},
(chunks, callback) => {
uglifier.processAssets(compilation, this.options).then(() => {
callback();
});
}
);
});

compiler.plugin('done', () => {
compiler.hooks.done.tap("ParallelUglifyPlugin", () => {
uglifier.pruneCache(this.options);
});
};

module.exports = FasterUglifyPlugin;
module.exports = ParallelUglifyPlugin;
58 changes: 33 additions & 25 deletions lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,22 @@ function minify(source, map, uglifyOptions, uglifier) {
}

const result = uglifier.minify(source, opts);
if (result.error) {
if (result.error.name === 'SyntaxError') {
const frame = codeFrame(source, result.error.line, result.error.col);
const errorMessage = `${result.error.name}: ${result.error.message}\n${frame}`;
throw new SyntaxError(errorMessage);
const prom = result.then ? result : Promise.resolve(result);
return prom.then(resolved => {
if (resolved.error) {
if (resolved.error.name === 'SyntaxError') {
const frame = codeFrame(source, resolved.error.line, resolved.error.col);
const errorMessage = `${resolved.error.name}: ${resolved.error.message}\n${frame}`;
throw new SyntaxError(errorMessage);
}

throw resolved.error;
}

throw result.error;
}

result.code = result.code.replace(new RegExp(BOGUS_SOURCEMAP_URL), '');

return result;
resolved.code = resolved.code.replace(new RegExp(BOGUS_SOURCEMAP_URL), '');

return result;
});
}

/**
Expand Down Expand Up @@ -65,21 +68,26 @@ function processMessage(msgLocation, callback) {
const cacheKey = cache.createCacheKey(source + !!map, message.options);
// We do not check the cache here because we already determined that this asset yields a cache
// miss in the parent process.
const { uglifyES } = message.options;
const { terser } = message.options;
const { uglifyJS } = message.options;
const uglifier = uglifyES ? require('uglify-es') : require('uglify-js'); // eslint-disable-line global-require, max-len
const minifiedContent = minify(source, map, uglifyES || uglifyJS, uglifier);
cache.saveToCache(cacheKey, JSON.stringify({
source: minifiedContent.code,
map: minifiedContent.map,
}), message.cacheDir);

tmpFile.update(msgLocation, JSON.stringify({
source: minifiedContent.code,
map: minifiedContent.map,
cacheKey,
}));
callback(null, msgLocation);
// eslint-disable-next-line global-require
const uglifier = terser ? require('terser') : require('uglify-js');
minify(source, map, terser || uglifyJS, uglifier)
.then(minifiedContent => {
cache.saveToCache(cacheKey, JSON.stringify({
source: minifiedContent.code,
map: minifiedContent.map,
}), message.cacheDir);

tmpFile.update(msgLocation, JSON.stringify({
source: minifiedContent.code,
map: minifiedContent.map,
cacheKey,
}));
callback(null, msgLocation);
}).catch(e => {
callback(e.message, msgLocation);
});
} catch (e) {
callback(e.message, msgLocation);
}
Expand Down
19 changes: 15 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webpack-parallel-uglify-plugin",
"version": "1.1.2",
"version": "2.0.0-alpha-3",
"description": "A webpack plugin to run uglifyjs in parallel.",
"main": "index.js",
"scripts": {
Expand All @@ -12,9 +12,20 @@
"url": "git://github.com/gdborton/webpack-parallel-uglify-plugin.git"
},
"keywords": [
"compress",
"compressor",
"min",
"minification",
"minifier",
"minify",
"optimize",
"optimizer",
"terser",
"uglify",
"uglify-es",
"uglify-js",
"webpack",
"uglifyjs",
"minimize"
"webpack-plugin"
],
"ava": {
"require": [
Expand Down Expand Up @@ -42,8 +53,8 @@
"glob": "^7.0.5",
"mkdirp": "^0.5.1",
"pify": "^3.0.0",
"terser": "^5.3.5",
"tmp": "0.0.29",
"uglify-es": "^3.3.9",
"uglify-js": "^3.6.0",
"webpack-sources": "^1.0.0",
"worker-farm": "^1.3.1"
Expand Down
34 changes: 25 additions & 9 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ test('creating a WebpackParallelUglifyPlugin instance w/ both uglify options thr
t.throws(() => {
new WebpackParallelUglifyPlugin({
uglifyJS: {},
uglifyES: {},
terser: {},
});
});
});
Expand All @@ -33,7 +33,7 @@ test('creating a WebpackParallelUglifyPlugin instance with uglify.sourceMap thro

t.throws(() => {
new WebpackParallelUglifyPlugin({
uglifyES: { sourceMap: true },
terser: { sourceMap: true },
});
});
});
Expand All @@ -43,13 +43,32 @@ test('providing no uglify options defaults to uglifyJS: {}', (t) => {
t.deepEqual(plugin.options, { uglifyJS: {} });
});

function FakeCompilation() {
this.hooks = {
processAssets: {
tapAsync: () => {

}
}
}
}

function FakeCompiler() {
const callbacks = {};
const fakeCompilation = new FakeCompilation();
this.assets = [];

this.plugin = (event, callback) => {
callbacks[event] = callback;
};
this.hooks = {
compilation: {
tap: (pluginName, callback) => {
callbacks['compilation'] = () => callback(fakeCompilation);
}
},
done: {
tap: (pluginName, callback) => {
callbacks['done'] = callback;
}
}
}

this.fireEvent = (event, ...args) => {
callbacks[event].apply(this, args);
Expand Down Expand Up @@ -82,10 +101,7 @@ test('deleting unused cache files after all asset optimizations', (t) => {
const compiler = new FakeCompiler();
uglifyPlugin.apply(compiler);
compiler.fireEvent('compilation', compiler);
compiler.fireEvent('optimize-chunk-assets', null, () => {});
compiler.fireEvent('optimize-chunk-assets', null, () => {});
t.is(fs.unlinkSync.callCount, 0, 'Cache should not be cleared by optimize-chunk-assets');

compiler.fireEvent('done');
t.deepEqual(
fs.unlinkSync.args,
Expand Down
6 changes: 3 additions & 3 deletions test/lib/uglifer.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function createFakeES6CompilationObject() {
assets: {
'someFile.js': {
source() {
return '() => {}';
return 'const test = () => { function asdf(){console.log("a")} asdf()}; test();';
},
map() {
return null;
Expand Down Expand Up @@ -231,11 +231,11 @@ test('Passing uglifyJS options throws an error when minifying es6', (t) => {
});
});

test('Passing uglifyES options does not throw an error when minifying es6', (t) => {
test('Passing terser options does not throw an error when minifying es6', (t) => {
const es6CompilationObject = createFakeES6CompilationObject();
return processAssets(es6CompilationObject, {
sourceMap: false,
uglifyES: {},
terser: {},
}).then(() => {
assertNoError(es6CompilationObject, t);
});
Expand Down
14 changes: 8 additions & 6 deletions test/lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,20 @@ test.afterEach(() => {

test.serial('minify should not return a map if called with a RawSource object', (t) => {
const { map } = rawSource.sourceAndMap();
const result = minify(codeSource, map, undefined, uglify);
t.is(result.map, undefined);
t.is(result.code, minifiedContent.code); // should produce the same minified content.
return minify(codeSource, map, undefined, uglify).then(result => {
t.is(result.map, undefined);
t.is(result.code, minifiedContent.code); // should produce the same minified content.
});
});

test.serial(
'minify should return a valid source map if called with an OriginalSource object',
(t) => {
const { map } = originalSource.sourceAndMap();
const result = minify(codeSource, map, undefined, uglify);
t.truthy(result.map);
t.is(result.code, minifiedContent.code); // should produce the same minified content.
return minify(codeSource, map, undefined, uglify).then(result => {
t.truthy(result.map);
t.is(result.code, minifiedContent.code); // should produce the same minified content.
});
},
);

Expand Down