mirror of https://github.com/webpack/webpack.git
668 lines
20 KiB
JavaScript
668 lines
20 KiB
JavaScript
/* globals describe, it, beforeEach*/
|
|
"use strict";
|
|
require("should");
|
|
const sinon = require("sinon");
|
|
const UglifyJsPlugin = require("../lib/optimize/UglifyJsPlugin");
|
|
const PluginEnvironment = require("./helpers/PluginEnvironment");
|
|
const SourceMapSource = require("webpack-sources").SourceMapSource;
|
|
const RawSource = require("webpack-sources").RawSource;
|
|
|
|
describe("UglifyJsPlugin", function() {
|
|
it("has apply function", function() {
|
|
(new UglifyJsPlugin()).apply.should.be.a.Function();
|
|
});
|
|
|
|
describe("when applied with no options", function() {
|
|
let eventBindings;
|
|
let eventBinding;
|
|
|
|
beforeEach(function() {
|
|
const pluginEnvironment = new PluginEnvironment();
|
|
const compilerEnv = pluginEnvironment.getEnvironmentStub();
|
|
compilerEnv.context = "";
|
|
|
|
const plugin = new UglifyJsPlugin();
|
|
plugin.apply(compilerEnv);
|
|
eventBindings = pluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("binds one event handler", function() {
|
|
eventBindings.length.should.be.exactly(1);
|
|
});
|
|
|
|
describe("compilation handler", function() {
|
|
beforeEach(function() {
|
|
eventBinding = eventBindings[0];
|
|
});
|
|
|
|
it("binds to compilation event", function() {
|
|
eventBinding.name.should.be.exactly("compilation");
|
|
});
|
|
|
|
describe("when called", function() {
|
|
let chunkPluginEnvironment;
|
|
let compilationEventBindings;
|
|
let compilationEventBinding;
|
|
let compilation;
|
|
let callback;
|
|
|
|
beforeEach(function() {
|
|
chunkPluginEnvironment = new PluginEnvironment();
|
|
compilation = chunkPluginEnvironment.getEnvironmentStub();
|
|
compilation.assets = {
|
|
"test.js": {
|
|
__UglifyJsPlugin: {}
|
|
},
|
|
"test1.js": "",
|
|
"test2.js": {
|
|
source: function() {
|
|
return "invalid javascript";
|
|
}
|
|
},
|
|
"test3.js": {
|
|
source: function() {
|
|
return "/** @preserve Foo Bar */ function foo(longVariableName) { longVariableName = 1; }";
|
|
}
|
|
}
|
|
};
|
|
compilation.errors = [];
|
|
|
|
eventBinding.handler(compilation);
|
|
compilationEventBindings = chunkPluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("binds one event handler", function() {
|
|
compilationEventBindings.length.should.be.exactly(1);
|
|
});
|
|
|
|
describe("optimize-chunk-assets handler", function() {
|
|
beforeEach(function() {
|
|
compilationEventBinding = compilationEventBindings[0];
|
|
});
|
|
|
|
it("binds to optimize-chunk-assets event", function() {
|
|
compilationEventBinding.name.should.be.exactly("optimize-chunk-assets");
|
|
});
|
|
|
|
it("only calls callback once", function() {
|
|
callback = sinon.spy();
|
|
compilationEventBinding.handler([""], callback);
|
|
callback.callCount.should.be.exactly(1);
|
|
});
|
|
|
|
it("default only parses filenames ending with .js", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test", "test.js"]
|
|
}], function() {
|
|
Object.keys(compilation.assets).length.should.be.exactly(4);
|
|
});
|
|
});
|
|
|
|
it("early returns if private property is already set", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.assets["test.js"].should.deepEqual({});
|
|
});
|
|
});
|
|
|
|
it("outputs stack trace errors for invalid asset", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test1.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(1);
|
|
compilation.errors[0].should.be.an.Error;
|
|
compilation.errors[0].message.should.have.containEql("TypeError");
|
|
});
|
|
});
|
|
|
|
it("outputs parsing errors for invalid javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test2.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(1);
|
|
compilation.errors[0].should.be.an.Error;
|
|
compilation.errors[0].message.should.have.containEql("Unexpected token");
|
|
compilation.errors[0].message.should.have.containEql("[test2.js:1,8]");
|
|
});
|
|
});
|
|
|
|
it("outputs no errors for valid javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test3.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(0);
|
|
});
|
|
});
|
|
|
|
it("outputs RawSource for valid javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test3.js"]
|
|
}], function() {
|
|
compilation.assets["test3.js"].should.be.instanceof(RawSource);
|
|
});
|
|
});
|
|
|
|
it("outputs mangled javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test3.js"]
|
|
}], function() {
|
|
compilation.assets["test3.js"]._value.should.not.containEql("longVariableName");
|
|
});
|
|
});
|
|
|
|
it("compresses and does not output beautified javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test3.js"]
|
|
}], function() {
|
|
compilation.assets["test3.js"]._value.should.not.containEql("\n");
|
|
});
|
|
});
|
|
|
|
it("preserves comments", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test3.js"]
|
|
}], function() {
|
|
compilation.assets["test3.js"]._value.should.containEql("/**");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when applied with invalid options", function() {
|
|
it("outputs uglify errors", function() {
|
|
const pluginEnvironment = new PluginEnvironment();
|
|
const compilerEnv = pluginEnvironment.getEnvironmentStub();
|
|
compilerEnv.context = "";
|
|
|
|
const plugin = new UglifyJsPlugin({
|
|
output: {
|
|
"invalid-option": true
|
|
}
|
|
});
|
|
plugin.apply(compilerEnv);
|
|
const eventBinding = pluginEnvironment.getEventBindings()[0];
|
|
|
|
const chunkPluginEnvironment = new PluginEnvironment();
|
|
const compilation = chunkPluginEnvironment.getEnvironmentStub();
|
|
compilation.assets = {
|
|
"test.js": {
|
|
source: function() {
|
|
return "var foo = 1;";
|
|
}
|
|
}
|
|
};
|
|
compilation.errors = [];
|
|
|
|
eventBinding.handler(compilation);
|
|
const compilationEventBinding = chunkPluginEnvironment.getEventBindings()[0];
|
|
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(1);
|
|
compilation.errors[0].message.should.have.containEql("supported option");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when applied with all options", function() {
|
|
let eventBindings;
|
|
let eventBinding;
|
|
|
|
beforeEach(function() {
|
|
const pluginEnvironment = new PluginEnvironment();
|
|
const compilerEnv = pluginEnvironment.getEnvironmentStub();
|
|
compilerEnv.context = "";
|
|
|
|
const plugin = new UglifyJsPlugin({
|
|
sourceMap: true,
|
|
compress: {
|
|
warnings: true,
|
|
},
|
|
mangle: false,
|
|
beautify: true,
|
|
comments: false,
|
|
extractComments: {
|
|
condition: 'should be extracted',
|
|
filename: function(file) {
|
|
return file.replace(/(\.\w+)$/, '.license$1');
|
|
},
|
|
banner: function(licenseFile) {
|
|
return 'License information can be found in ' + licenseFile;
|
|
}
|
|
}
|
|
});
|
|
plugin.apply(compilerEnv);
|
|
eventBindings = pluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("binds one event handler", function() {
|
|
eventBindings.length.should.be.exactly(1);
|
|
});
|
|
|
|
describe("compilation handler", function() {
|
|
beforeEach(function() {
|
|
eventBinding = eventBindings[0];
|
|
});
|
|
|
|
it("binds to compilation event", function() {
|
|
eventBinding.name.should.be.exactly("compilation");
|
|
});
|
|
|
|
describe("when called", function() {
|
|
let chunkPluginEnvironment;
|
|
let compilationEventBindings;
|
|
let compilationEventBinding;
|
|
let compilation;
|
|
|
|
beforeEach(function() {
|
|
chunkPluginEnvironment = new PluginEnvironment();
|
|
compilation = chunkPluginEnvironment.getEnvironmentStub();
|
|
compilation.assets = {
|
|
"test.js": {
|
|
source: function() {
|
|
return "/** @preserve Foo Bar */ function foo(longVariableName) { longVariableName = 1; }";
|
|
},
|
|
map: function() {
|
|
return {
|
|
version: 3,
|
|
sources: ["test.js"],
|
|
names: ["foo", "longVariableName"],
|
|
mappings: "AAAA,QAASA,KAAIC,kBACTA,iBAAmB"
|
|
};
|
|
}
|
|
},
|
|
"test1.js": {
|
|
source: function() {
|
|
return "invalid javascript";
|
|
},
|
|
map: function() {
|
|
return {
|
|
version: 3,
|
|
sources: ["test1.js"],
|
|
names: [""],
|
|
mappings: "AAAA"
|
|
};
|
|
}
|
|
},
|
|
"test2.js": {
|
|
source: function() {
|
|
return "function foo(x) { if (x) { return bar(); not_called1(); } }";
|
|
},
|
|
map: function() {
|
|
return {
|
|
version: 3,
|
|
sources: ["test1.js"],
|
|
names: ["foo", "x", "bar", "not_called1"],
|
|
mappings: "AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC"
|
|
};
|
|
}
|
|
},
|
|
"test3.js": {
|
|
sourceAndMap: function() {
|
|
return {
|
|
source: "/** @preserve Foo Bar */ function foo(longVariableName) { longVariableName = 1; }",
|
|
map: {
|
|
version: 3,
|
|
sources: ["test.js"],
|
|
names: ["foo", "longVariableName"],
|
|
mappings: "AAAA,QAASA,KAAIC,kBACTA,iBAAmB"
|
|
}
|
|
};
|
|
},
|
|
},
|
|
"test4.js": {
|
|
source: function() {
|
|
return "/*! this comment should be extracted */ function foo(longVariableName) { /* this will not be extracted */ longVariableName = 1; } // another comment that should be extracted to a separate file\n function foo2(bar) { return bar; }";
|
|
},
|
|
map: function() {
|
|
return {
|
|
version: 3,
|
|
sources: ["test.js"],
|
|
names: ["foo", "longVariableName"],
|
|
mappings: "AAAA,QAASA,KAAIC,kBACTA,iBAAmB"
|
|
};
|
|
}
|
|
},
|
|
};
|
|
compilation.errors = [];
|
|
compilation.warnings = [];
|
|
|
|
eventBinding.handler(compilation);
|
|
compilationEventBindings = chunkPluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("binds two event handler", function() {
|
|
compilationEventBindings.length.should.be.exactly(2);
|
|
});
|
|
|
|
describe("build-module handler", function() {
|
|
beforeEach(function() {
|
|
compilationEventBinding = compilationEventBindings[0];
|
|
});
|
|
|
|
it("binds to build-module event", function() {
|
|
compilationEventBinding.name.should.be.exactly("build-module");
|
|
});
|
|
|
|
it("sets the useSourceMap flag", function() {
|
|
const obj = {};
|
|
compilationEventBinding.handler(obj);
|
|
obj.useSourceMap.should.be.equal(true);
|
|
});
|
|
});
|
|
|
|
describe("optimize-chunk-assets handler", function() {
|
|
beforeEach(function() {
|
|
compilationEventBinding = compilationEventBindings[1];
|
|
});
|
|
|
|
it("binds to optimize-chunk-assets event", function() {
|
|
compilationEventBinding.name.should.be.exactly("optimize-chunk-assets");
|
|
});
|
|
|
|
it("outputs no errors for valid javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(0);
|
|
});
|
|
});
|
|
|
|
it("outputs SourceMapSource for valid javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.assets["test.js"].should.be.instanceof(SourceMapSource);
|
|
});
|
|
});
|
|
|
|
it("does not output mangled javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.assets["test.js"]._value.should.containEql("longVariableName");
|
|
});
|
|
});
|
|
|
|
it("outputs beautified javascript", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.assets["test.js"]._value.should.containEql("\n");
|
|
});
|
|
});
|
|
|
|
it("does not preserve comments", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js"]
|
|
}], function() {
|
|
compilation.assets["test.js"]._value.should.not.containEql("/**");
|
|
});
|
|
});
|
|
|
|
it("outputs parsing errors", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test1.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(1);
|
|
compilation.errors[0].should.be.an.Error;
|
|
compilation.errors[0].message.should.containEql("[test1.js:1,0][test1.js:1,8]");
|
|
});
|
|
});
|
|
|
|
it("outputs warnings for unreachable code", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test2.js"]
|
|
}], function() {
|
|
compilation.warnings.length.should.be.exactly(1);
|
|
compilation.warnings[0].should.be.an.Error;
|
|
compilation.warnings[0].message.should.containEql("Dropping unreachable code");
|
|
});
|
|
});
|
|
|
|
it("works with sourceAndMap assets as well", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test3.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(0);
|
|
compilation.assets["test3.js"].should.be.instanceof(SourceMapSource);
|
|
});
|
|
});
|
|
|
|
describe("with warningsFilter set", function() {
|
|
let compilationEventBindings, compilation;
|
|
|
|
describe("and the filter returns true", function() {
|
|
beforeEach(function() {
|
|
const pluginEnvironment = new PluginEnvironment();
|
|
const compilerEnv = pluginEnvironment.getEnvironmentStub();
|
|
compilerEnv.context = "";
|
|
|
|
const plugin = new UglifyJsPlugin({
|
|
warningsFilter: function() {
|
|
return true;
|
|
},
|
|
sourceMap: true,
|
|
compress: {
|
|
warnings: true,
|
|
},
|
|
mangle: false,
|
|
beautify: true,
|
|
comments: false
|
|
});
|
|
plugin.apply(compilerEnv);
|
|
const eventBindings = pluginEnvironment.getEventBindings();
|
|
|
|
const chunkPluginEnvironment = new PluginEnvironment();
|
|
compilation = chunkPluginEnvironment.getEnvironmentStub();
|
|
compilation.assets = {
|
|
"test2.js": {
|
|
source: function() {
|
|
return "function foo(x) { if (x) { return bar(); not_called1(); } }";
|
|
},
|
|
map: function() {
|
|
return {
|
|
version: 3,
|
|
sources: ["test1.js"],
|
|
names: ["foo", "x", "bar", "not_called1"],
|
|
mappings: "AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC"
|
|
};
|
|
}
|
|
},
|
|
};
|
|
compilation.errors = [];
|
|
compilation.warnings = [];
|
|
|
|
eventBindings[0].handler(compilation);
|
|
compilationEventBindings = chunkPluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("should get all warnings", function() {
|
|
compilationEventBindings[1].handler([{
|
|
files: ["test2.js"]
|
|
}], function() {
|
|
compilation.warnings.length.should.be.exactly(1);
|
|
compilation.warnings[0].should.be.an.Error;
|
|
compilation.warnings[0].message.should.containEql("Dropping unreachable code");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("and the filter returns false", function() {
|
|
beforeEach(function() {
|
|
const pluginEnvironment = new PluginEnvironment();
|
|
const compilerEnv = pluginEnvironment.getEnvironmentStub();
|
|
compilerEnv.context = "";
|
|
|
|
const plugin = new UglifyJsPlugin({
|
|
warningsFilter: function() {
|
|
return false;
|
|
},
|
|
sourceMap: true,
|
|
compress: {
|
|
warnings: true,
|
|
},
|
|
mangle: false,
|
|
beautify: true,
|
|
comments: false
|
|
});
|
|
plugin.apply(compilerEnv);
|
|
const eventBindings = pluginEnvironment.getEventBindings();
|
|
|
|
const chunkPluginEnvironment = new PluginEnvironment();
|
|
compilation = chunkPluginEnvironment.getEnvironmentStub();
|
|
compilation.assets = {
|
|
"test2.js": {
|
|
source: function() {
|
|
return "function foo(x) { if (x) { return bar(); not_called1(); } }";
|
|
},
|
|
map: function() {
|
|
return {
|
|
version: 3,
|
|
sources: ["test1.js"],
|
|
names: ["foo", "x", "bar", "not_called1"],
|
|
mappings: "AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC"
|
|
};
|
|
}
|
|
},
|
|
};
|
|
compilation.errors = [];
|
|
compilation.warnings = [];
|
|
|
|
eventBindings[0].handler(compilation);
|
|
compilationEventBindings = chunkPluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("should get no warnings", function() {
|
|
compilationEventBindings[1].handler([{
|
|
files: ["test2.js"]
|
|
}], function() {
|
|
compilation.warnings.length.should.be.exactly(0);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it("extracts license information to separate file", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test4.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(0);
|
|
compilation.assets["test4.license.js"]._value.should.containEql("/*! this comment should be extracted */");
|
|
compilation.assets["test4.license.js"]._value.should.containEql("// another comment that should be extracted to a separate file");
|
|
compilation.assets["test4.license.js"]._value.should.not.containEql("/* this will not be extracted */");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when applied with extract option set to a single file", function() {
|
|
let eventBindings;
|
|
let eventBinding;
|
|
|
|
beforeEach(function() {
|
|
const pluginEnvironment = new PluginEnvironment();
|
|
const compilerEnv = pluginEnvironment.getEnvironmentStub();
|
|
compilerEnv.context = "";
|
|
|
|
const plugin = new UglifyJsPlugin({
|
|
comments: "all",
|
|
extractComments: {
|
|
condition: /.*/,
|
|
filename: "extracted-comments.js"
|
|
}
|
|
});
|
|
plugin.apply(compilerEnv);
|
|
eventBindings = pluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("binds one event handler", function() {
|
|
eventBindings.length.should.be.exactly(1);
|
|
});
|
|
|
|
describe("compilation handler", function() {
|
|
beforeEach(function() {
|
|
eventBinding = eventBindings[0];
|
|
});
|
|
|
|
it("binds to compilation event", function() {
|
|
eventBinding.name.should.be.exactly("compilation");
|
|
});
|
|
|
|
describe("when called", function() {
|
|
let chunkPluginEnvironment;
|
|
let compilationEventBindings;
|
|
let compilationEventBinding;
|
|
let compilation;
|
|
|
|
beforeEach(function() {
|
|
chunkPluginEnvironment = new PluginEnvironment();
|
|
compilation = chunkPluginEnvironment.getEnvironmentStub();
|
|
compilation.assets = {
|
|
"test.js": {
|
|
source: function() {
|
|
return "/* This is a comment from test.js */ function foo(bar) { return bar; }";
|
|
}
|
|
},
|
|
"test2.js": {
|
|
source: function() {
|
|
return "// This is a comment from test2.js\nfunction foo2(bar) { return bar; }";
|
|
}
|
|
},
|
|
"test3.js": {
|
|
source: function() {
|
|
return "/* This is a comment from test3.js */ function foo3(bar) { return bar; }\n// This is another comment from test3.js\nfunction foobar3(baz) { return baz; }";
|
|
}
|
|
},
|
|
};
|
|
compilation.errors = [];
|
|
compilation.warnings = [];
|
|
|
|
eventBinding.handler(compilation);
|
|
compilationEventBindings = chunkPluginEnvironment.getEventBindings();
|
|
});
|
|
|
|
it("binds one event handler", function() {
|
|
compilationEventBindings.length.should.be.exactly(1);
|
|
});
|
|
|
|
describe("optimize-chunk-assets handler", function() {
|
|
beforeEach(function() {
|
|
compilationEventBinding = compilationEventBindings[0];
|
|
});
|
|
|
|
it("preserves comments", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js", "test2.js", "test3.js"]
|
|
}], function() {
|
|
compilation.assets["test.js"].source().should.containEql("/*");
|
|
compilation.assets["test2.js"].source().should.containEql("//");
|
|
compilation.assets["test3.js"].source().should.containEql("/*");
|
|
compilation.assets["test3.js"].source().should.containEql("//");
|
|
});
|
|
});
|
|
|
|
it("extracts comments to specified file", function() {
|
|
compilationEventBinding.handler([{
|
|
files: ["test.js", "test2.js", "test3.js"]
|
|
}], function() {
|
|
compilation.errors.length.should.be.exactly(0);
|
|
compilation.assets["extracted-comments.js"].source().should.containEql("/* This is a comment from test.js */");
|
|
compilation.assets["extracted-comments.js"].source().should.containEql("// This is a comment from test2.js");
|
|
compilation.assets["extracted-comments.js"].source().should.containEql("/* This is a comment from test3.js */");
|
|
compilation.assets["extracted-comments.js"].source().should.containEql("// This is another comment from test3.js");
|
|
compilation.assets["extracted-comments.js"].source().should.not.containEql("function");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|