Added logging, changed some directory structure

This commit is contained in:
2018-01-13 21:33:40 -05:00
parent f079a5f067
commit 8e72ffb917
73656 changed files with 35284 additions and 53718 deletions

View File

@@ -0,0 +1,115 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class AggressiveMergingPlugin {
constructor(options) {
if(options !== undefined && typeof options !== "object" || Array.isArray(options)) {
throw new Error("Argument should be an options object. To use defaults, pass in nothing.\nFor more info on options, see https://webpack.js.org/plugins/");
}
this.options = options || {};
}
apply(compiler) {
const options = this.options;
const minSizeReduce = options.minSizeReduce || 1.5;
function getParentsWeight(chunk) {
return chunk.parents.map((p) => {
return p.isInitial() ? options.entryChunkMultiplicator || 10 : 1;
}).reduce((a, b) => {
return a + b;
}, 0);
}
compiler.plugin("this-compilation", (compilation) => {
compilation.plugin("optimize-chunks-advanced", (chunks) => {
let combinations = [];
chunks.forEach((a, idx) => {
if(a.isInitial()) return;
for(let i = 0; i < idx; i++) {
const b = chunks[i];
if(b.isInitial()) continue;
combinations.push({
a,
b,
improvement: undefined
});
}
});
combinations.forEach((pair) => {
const a = pair.b.size({
chunkOverhead: 0
});
const b = pair.a.size({
chunkOverhead: 0
});
const ab = pair.b.integratedSize(pair.a, {
chunkOverhead: 0
});
let newSize;
if(ab === false) {
pair.improvement = false;
return;
} else if(options.moveToParents) {
const aOnly = ab - b;
const bOnly = ab - a;
const common = a + b - ab;
newSize = common + getParentsWeight(pair.b) * aOnly + getParentsWeight(pair.a) * bOnly;
} else {
newSize = ab;
}
pair.improvement = (a + b) / newSize;
});
combinations = combinations.filter((pair) => {
return pair.improvement !== false;
});
combinations.sort((a, b) => {
return b.improvement - a.improvement;
});
const pair = combinations[0];
if(!pair) return;
if(pair.improvement < minSizeReduce) return;
if(options.moveToParents) {
const commonModules = pair.b.modules.filter((m) => {
return pair.a.modules.indexOf(m) >= 0;
});
const aOnlyModules = pair.b.modules.filter((m) => {
return commonModules.indexOf(m) < 0;
});
const bOnlyModules = pair.a.modules.filter((m) => {
return commonModules.indexOf(m) < 0;
});
aOnlyModules.forEach((m) => {
pair.b.removeModule(m);
m.removeChunk(pair.b);
pair.b.parents.forEach((c) => {
c.addModule(m);
m.addChunk(c);
});
});
bOnlyModules.forEach((m) => {
pair.a.removeModule(m);
m.removeChunk(pair.a);
pair.a.parents.forEach((c) => {
c.addModule(m);
m.addChunk(c);
});
});
}
if(pair.b.integrate(pair.a, "aggressive-merge")) {
chunks.splice(chunks.indexOf(pair.a), 1);
return true;
}
});
});
}
}
module.exports = AggressiveMergingPlugin;

View File

@@ -0,0 +1,195 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const identifierUtils = require("../util/identifier");
function moveModuleBetween(oldChunk, newChunk) {
return function(module) {
oldChunk.moveModule(module, newChunk);
};
}
function isNotAEntryModule(entryModule) {
return function(module) {
return entryModule !== module;
};
}
function copyWithReason(obj) {
const newObj = {};
Object.keys(obj).forEach((key) => {
newObj[key] = obj[key];
});
if(!newObj.reasons || newObj.reasons.indexOf("aggressive-splitted") < 0)
newObj.reasons = (newObj.reasons || []).concat("aggressive-splitted");
return newObj;
}
class AggressiveSplittingPlugin {
constructor(options) {
this.options = options || {};
if(typeof this.options.minSize !== "number") this.options.minSize = 30 * 1024;
if(typeof this.options.maxSize !== "number") this.options.maxSize = 50 * 1024;
if(typeof this.options.chunkOverhead !== "number") this.options.chunkOverhead = 0;
if(typeof this.options.entryChunkMultiplicator !== "number") this.options.entryChunkMultiplicator = 1;
}
apply(compiler) {
compiler.plugin("this-compilation", (compilation) => {
compilation.plugin("optimize-chunks-advanced", (chunks) => {
// Precompute stuff
const nameToModuleMap = new Map();
compilation.modules.forEach(m => {
const name = identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache);
nameToModuleMap.set(name, m);
});
const savedSplits = compilation.records && compilation.records.aggressiveSplits || [];
const usedSplits = compilation._aggressiveSplittingSplits ?
savedSplits.concat(compilation._aggressiveSplittingSplits) : savedSplits;
const minSize = this.options.minSize;
const maxSize = this.options.maxSize;
// 1. try to restore to recorded splitting
for(let j = 0; j < usedSplits.length; j++) {
const splitData = usedSplits[j];
const selectedModules = splitData.modules.map(name => nameToModuleMap.get(name));
// Does the modules exist at all?
if(selectedModules.every(Boolean)) {
// Find all chunks containing all modules in the split
for(let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
// Cheap check if chunk is suitable at all
if(chunk.getNumberOfModules() < splitData.modules.length)
continue;
// Check if all modules are in the chunk
if(selectedModules.every(m => chunk.containsModule(m))) {
// Is chunk identical to the split or do we need to split it?
if(chunk.getNumberOfModules() > splitData.modules.length) {
// split the chunk into two parts
const newChunk = compilation.addChunk();
selectedModules.forEach(moveModuleBetween(chunk, newChunk));
chunk.split(newChunk);
chunk.name = null;
newChunk._fromAggressiveSplitting = true;
if(j < savedSplits.length)
newChunk._fromAggressiveSplittingIndex = j;
if(splitData.id !== null && splitData.id !== undefined) {
newChunk.id = splitData.id;
}
newChunk.origins = chunk.origins.map(copyWithReason);
chunk.origins = chunk.origins.map(copyWithReason);
return true;
} else { // chunk is identical to the split
if(j < savedSplits.length)
chunk._fromAggressiveSplittingIndex = j;
chunk.name = null;
if(splitData.id !== null && splitData.id !== undefined) {
chunk.id = splitData.id;
}
}
}
}
}
}
// 2. for any other chunk which isn't splitted yet, split it
for(let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const size = chunk.size(this.options);
if(size > maxSize && chunk.getNumberOfModules() > 1) {
const newChunk = compilation.addChunk();
const modules = chunk.getModules()
.filter(isNotAEntryModule(chunk.entryModule))
.sort((a, b) => {
a = a.identifier();
b = b.identifier();
if(a > b) return 1;
if(a < b) return -1;
return 0;
});
for(let k = 0; k < modules.length; k++) {
chunk.moveModule(modules[k], newChunk);
const newSize = newChunk.size(this.options);
const chunkSize = chunk.size(this.options);
// break early if it's fine
if(chunkSize < maxSize && newSize < maxSize && newSize >= minSize && chunkSize >= minSize)
break;
if(newSize > maxSize && k === 0) {
// break if there is a single module which is bigger than maxSize
break;
}
if(newSize > maxSize || chunkSize < minSize) {
// move it back
newChunk.moveModule(modules[k], chunk);
// check if it's fine now
if(newSize < maxSize && newSize >= minSize && chunkSize >= minSize)
break;
}
}
if(newChunk.getNumberOfModules() > 0) {
chunk.split(newChunk);
chunk.name = null;
newChunk.origins = chunk.origins.map(copyWithReason);
chunk.origins = chunk.origins.map(copyWithReason);
compilation._aggressiveSplittingSplits = (compilation._aggressiveSplittingSplits || []).concat({
modules: newChunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache))
});
return true;
} else {
chunks.splice(chunks.indexOf(newChunk), 1);
}
}
}
});
compilation.plugin("record-hash", (records) => {
// 3. save to made splittings to records
const minSize = this.options.minSize;
if(!records.aggressiveSplits) records.aggressiveSplits = [];
compilation.chunks.forEach((chunk) => {
if(chunk.hasEntryModule()) return;
const size = chunk.size(this.options);
const incorrectSize = size < minSize;
const modules = chunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache));
if(typeof chunk._fromAggressiveSplittingIndex === "undefined") {
if(incorrectSize) return;
chunk.recorded = true;
records.aggressiveSplits.push({
modules: modules,
hash: chunk.hash,
id: chunk.id
});
} else {
const splitData = records.aggressiveSplits[chunk._fromAggressiveSplittingIndex];
if(splitData.hash !== chunk.hash || incorrectSize) {
if(chunk._fromAggressiveSplitting) {
chunk._aggressiveSplittingInvalid = true;
splitData.invalid = true;
} else {
splitData.hash = chunk.hash;
}
}
}
});
records.aggressiveSplits = records.aggressiveSplits.filter((splitData) => {
return !splitData.invalid;
});
});
compilation.plugin("need-additional-seal", (callback) => {
const invalid = compilation.chunks.some((chunk) => {
return chunk._aggressiveSplittingInvalid;
});
if(invalid)
return true;
});
});
}
}
module.exports = AggressiveSplittingPlugin;

View File

@@ -0,0 +1,53 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class ChunkModuleIdRangePlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
const options = this.options;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("module-ids", (modules) => {
const chunk = this.chunks.find((chunk) => chunk.name === options.name);
if(!chunk) throw new Error("ChunkModuleIdRangePlugin: Chunk with name '" + options.name + "' was not found");
let currentId = options.start;
let chunkModules;
if(options.order) {
chunkModules = chunk.modules.slice();
switch(options.order) {
case "index":
chunkModules.sort((a, b) => {
return a.index - b.index;
});
break;
case "index2":
chunkModules.sort((a, b) => {
return a.index2 - b.index2;
});
break;
default:
throw new Error("ChunkModuleIdRangePlugin: unexpected value of order");
}
} else {
chunkModules = modules.filter((m) => {
return m.chunks.indexOf(chunk) >= 0;
});
}
for(let i = 0; i < chunkModules.length; i++) {
const m = chunkModules[i];
if(m.id === null) {
m.id = currentId++;
}
if(options.end && currentId > options.end)
break;
}
});
});
}
}
module.exports = ChunkModuleIdRangePlugin;

View File

@@ -0,0 +1,404 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
let nextIdent = 0;
class CommonsChunkPlugin {
constructor(options) {
if(arguments.length > 1) {
throw new Error(`Deprecation notice: CommonsChunkPlugin now only takes a single argument. Either an options
object *or* the name of the chunk.
Example: if your old code looked like this:
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js')
You would change it to:
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' })
The available options are:
name: string
names: string[]
filename: string
minChunks: number
chunks: string[]
children: boolean
async: boolean
minSize: number`);
}
const normalizedOptions = this.normalizeOptions(options);
this.chunkNames = normalizedOptions.chunkNames;
this.filenameTemplate = normalizedOptions.filenameTemplate;
this.minChunks = normalizedOptions.minChunks;
this.selectedChunks = normalizedOptions.selectedChunks;
this.children = normalizedOptions.children;
this.deepChildren = normalizedOptions.deepChildren;
this.async = normalizedOptions.async;
this.minSize = normalizedOptions.minSize;
this.ident = __filename + (nextIdent++);
}
normalizeOptions(options) {
if(Array.isArray(options)) {
return {
chunkNames: options,
};
}
if(typeof options === "string") {
return {
chunkNames: [options],
};
}
// options.children and options.chunk may not be used together
if(options.children && options.chunks) {
throw new Error("You can't and it does not make any sense to use \"children\" and \"chunk\" options together.");
}
/**
* options.async and options.filename are also not possible together
* as filename specifies how the chunk is called but "async" implies
* that webpack will take care of loading this file.
*/
if(options.async && options.filename) {
throw new Error(`You can not specify a filename if you use the "async" option.
You can however specify the name of the async chunk by passing the desired string as the "async" option.`);
}
/**
* Make sure this is either an array or undefined.
* "name" can be a string and
* "names" a string or an array
*/
const chunkNames = options.name || options.names ? [].concat(options.name || options.names) : undefined;
return {
chunkNames: chunkNames,
filenameTemplate: options.filename,
minChunks: options.minChunks,
selectedChunks: options.chunks,
children: options.children,
deepChildren: options.deepChildren,
async: options.async,
minSize: options.minSize
};
}
apply(compiler) {
compiler.plugin("this-compilation", (compilation) => {
compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], (chunks) => {
// only optimize once
if(compilation[this.ident]) return;
compilation[this.ident] = true;
/**
* Creates a list of "common"" chunks based on the options.
* The list is made up of preexisting or newly created chunks.
* - If chunk has the name as specified in the chunkNames it is put in the list
* - If no chunk with the name as given in chunkNames exists a new chunk is created and added to the list
*
* These chunks are the "targets" for extracted modules.
*/
const targetChunks = this.getTargetChunks(chunks, compilation, this.chunkNames, this.children, this.async);
// iterate over all our new chunks
targetChunks.forEach((targetChunk, idx) => {
/**
* These chunks are subject to get "common" modules extracted and moved to the common chunk
*/
const affectedChunks = this.getAffectedChunks(compilation, chunks, targetChunk, targetChunks, idx, this.selectedChunks, this.async, this.children, this.deepChildren);
// bail if no chunk is affected
if(!affectedChunks) {
return;
}
// If we are async create an async chunk now
// override the "commonChunk" with the newly created async one and use it as commonChunk from now on
let asyncChunk;
if(this.async) {
// If async chunk is one of the affected chunks, just use it
asyncChunk = affectedChunks.filter(c => c.name === this.async)[0];
// Elsewise create a new one
if(!asyncChunk) {
asyncChunk = this.createAsyncChunk(
compilation,
targetChunks.length <= 1 || typeof this.async !== "string" ? this.async :
targetChunk.name ? `${this.async}-${targetChunk.name}` :
true,
targetChunk
);
}
targetChunk = asyncChunk;
}
/**
* Check which modules are "common" and could be extracted to a "common" chunk
*/
const extractableModules = this.getExtractableModules(this.minChunks, affectedChunks, targetChunk);
// If the minSize option is set check if the size extracted from the chunk is reached
// else bail out here.
// As all modules/commons are interlinked with each other, common modules would be extracted
// if we reach this mark at a later common chunk. (quirky I guess).
if(this.minSize) {
const modulesSize = this.calculateModulesSize(extractableModules);
// if too small, bail
if(modulesSize < this.minSize)
return;
}
// Remove modules that are moved to commons chunk from their original chunks
// return all chunks that are affected by having modules removed - we need them later (apparently)
const chunksWithExtractedModules = this.extractModulesAndReturnAffectedChunks(extractableModules, affectedChunks);
// connect all extracted modules with the common chunk
this.addExtractedModulesToTargetChunk(targetChunk, extractableModules);
// set filenameTemplate for chunk
if(this.filenameTemplate)
targetChunk.filenameTemplate = this.filenameTemplate;
// if we are async connect the blocks of the "reallyUsedChunk" - the ones that had modules removed -
// with the commonChunk and get the origins for the asyncChunk (remember "asyncChunk === commonChunk" at this moment).
// bail out
if(this.async) {
this.moveExtractedChunkBlocksToTargetChunk(chunksWithExtractedModules, targetChunk);
asyncChunk.origins = this.extractOriginsOfChunksWithExtractedModules(chunksWithExtractedModules);
return;
}
// we are not in "async" mode
// connect used chunks with commonChunk - shouldnt this be reallyUsedChunks here?
this.makeTargetChunkParentOfAffectedChunks(affectedChunks, targetChunk);
});
return true;
});
});
}
getTargetChunks(allChunks, compilation, chunkNames, children, asyncOption) {
const asyncOrNoSelectedChunk = children || asyncOption;
// we have specified chunk names
if(chunkNames) {
// map chunks by chunkName for quick access
const allChunksNameMap = allChunks.reduce((map, chunk) => {
if(chunk.name) {
map.set(chunk.name, chunk);
}
return map;
}, new Map());
// Ensure we have a chunk per specified chunk name.
// Reuse existing chunks if possible
return chunkNames.map(chunkName => {
if(allChunksNameMap.has(chunkName)) {
return allChunksNameMap.get(chunkName);
}
// add the filtered chunks to the compilation
return compilation.addChunk(chunkName);
});
}
// we dont have named chunks specified, so we just take all of them
if(asyncOrNoSelectedChunk) {
return allChunks;
}
/**
* No chunk name(s) was specified nor is this an async/children commons chunk
*/
throw new Error(`You did not specify any valid target chunk settings.
Take a look at the "name"/"names" or async/children option.`);
}
getAffectedUnnamedChunks(affectedChunks, targetChunk, rootChunk, asyncOption, deepChildrenOption) {
let chunks = targetChunk.chunks;
chunks && chunks.forEach((chunk) => {
if(chunk.isInitial()) {
return;
}
// If all the parents of a chunk are either
// a) the target chunk we started with
// b) themselves affected chunks
// we can assume that this chunk is an affected chunk too, as there is no way a chunk that
// isn't only depending on the target chunk is a parent of the chunk tested
if(asyncOption || chunk.parents.every((parentChunk) => parentChunk === rootChunk || affectedChunks.has(parentChunk))) {
// This check not only dedupes the affectedChunks but also guarantees we avoid endless loops
if(!affectedChunks.has(chunk)) {
// We mutate the affected chunks before going deeper, so the deeper levels and other branches
// have the information of this chunk being affected for their assertion if a chunk should
// not be affected
affectedChunks.add(chunk);
// We recurse down to all the children of the chunk, applying the same assumption.
// This guarantees that if a chunk should be an affected chunk,
// at the latest the last connection to the same chunk meets the
// condition to add it to the affected chunks.
if(deepChildrenOption === true) {
this.getAffectedUnnamedChunks(affectedChunks, chunk, rootChunk, asyncOption, deepChildrenOption);
}
}
}
});
}
getAffectedChunks(compilation, allChunks, targetChunk, targetChunks, currentIndex, selectedChunks, asyncOption, childrenOption, deepChildrenOption) {
const asyncOrNoSelectedChunk = childrenOption || asyncOption;
if(Array.isArray(selectedChunks)) {
return allChunks.filter(chunk => {
const notCommmonChunk = chunk !== targetChunk;
const isSelectedChunk = selectedChunks.indexOf(chunk.name) > -1;
return notCommmonChunk && isSelectedChunk;
});
}
if(asyncOrNoSelectedChunk) {
let affectedChunks = new Set();
this.getAffectedUnnamedChunks(affectedChunks, targetChunk, targetChunk, asyncOption, deepChildrenOption);
return Array.from(affectedChunks);
}
/**
* past this point only entry chunks are allowed to become commonChunks
*/
if(targetChunk.parents.length > 0) {
compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + targetChunk.name + ")"));
return;
}
/**
* If we find a "targetchunk" that is also a normal chunk (meaning it is probably specified as an entry)
* and the current target chunk comes after that and the found chunk has a runtime*
* make that chunk be an 'affected' chunk of the current target chunk.
*
* To understand what that means take a look at the "examples/chunkhash", this basically will
* result in the runtime to be extracted to the current target chunk.
*
* *runtime: the "runtime" is the "webpack"-block you may have seen in the bundles that resolves modules etc.
*/
return allChunks.filter((chunk) => {
const found = targetChunks.indexOf(chunk);
if(found >= currentIndex) return false;
return chunk.hasRuntime();
});
}
createAsyncChunk(compilation, asyncOption, targetChunk) {
const asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined);
asyncChunk.chunkReason = "async commons chunk";
asyncChunk.extraAsync = true;
asyncChunk.addParent(targetChunk);
targetChunk.addChunk(asyncChunk);
return asyncChunk;
}
// If minChunks is a function use that
// otherwhise check if a module is used at least minChunks or 2 or usedChunks.length time
getModuleFilter(minChunks, targetChunk, usedChunksLength) {
if(typeof minChunks === "function") {
return minChunks;
}
const minCount = (minChunks || Math.max(2, usedChunksLength));
const isUsedAtLeastMinTimes = (module, count) => count >= minCount;
return isUsedAtLeastMinTimes;
}
getExtractableModules(minChunks, usedChunks, targetChunk) {
if(minChunks === Infinity) {
return [];
}
// count how many chunks contain a module
const commonModulesToCountMap = usedChunks.reduce((map, chunk) => {
for(const module of chunk.modulesIterable) {
const count = map.has(module) ? map.get(module) : 0;
map.set(module, count + 1);
}
return map;
}, new Map());
// filter by minChunks
const moduleFilterCount = this.getModuleFilter(minChunks, targetChunk, usedChunks.length);
// filter by condition
const moduleFilterCondition = (module, chunk) => {
if(!module.chunkCondition) {
return true;
}
return module.chunkCondition(chunk);
};
return Array.from(commonModulesToCountMap).filter(entry => {
const module = entry[0];
const count = entry[1];
// if the module passes both filters, keep it.
return moduleFilterCount(module, count) && moduleFilterCondition(module, targetChunk);
}).map(entry => entry[0]);
}
calculateModulesSize(modules) {
return modules.reduce((totalSize, module) => totalSize + module.size(), 0);
}
extractModulesAndReturnAffectedChunks(reallyUsedModules, usedChunks) {
return reallyUsedModules.reduce((affectedChunksSet, module) => {
for(const chunk of usedChunks) {
// removeChunk returns true if the chunk was contained and succesfully removed
// false if the module did not have a connection to the chunk in question
if(module.removeChunk(chunk)) {
affectedChunksSet.add(chunk);
}
}
return affectedChunksSet;
}, new Set());
}
addExtractedModulesToTargetChunk(chunk, modules) {
for(const module of modules) {
chunk.addModule(module);
module.addChunk(chunk);
}
}
makeTargetChunkParentOfAffectedChunks(usedChunks, commonChunk) {
for(const chunk of usedChunks) {
// set commonChunk as new sole parent
chunk.parents = [commonChunk];
// add chunk to commonChunk
commonChunk.addChunk(chunk);
for(const entrypoint of chunk.entrypoints) {
entrypoint.insertChunk(commonChunk, chunk);
}
}
}
moveExtractedChunkBlocksToTargetChunk(chunks, targetChunk) {
for(const chunk of chunks) {
if(chunk === targetChunk) continue;
for(const block of chunk.blocks) {
if(block.chunks.indexOf(targetChunk) === -1) {
block.chunks.unshift(targetChunk);
}
targetChunk.addBlock(block);
}
}
}
extractOriginsOfChunksWithExtractedModules(chunks) {
const origins = [];
for(const chunk of chunks) {
for(const origin of chunk.origins) {
const newOrigin = Object.create(origin);
newOrigin.reasons = (origin.reasons || []).concat("async commons");
origins.push(newOrigin);
}
}
return origins;
}
}
module.exports = CommonsChunkPlugin;

View File

@@ -0,0 +1,817 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const Module = require("../Module");
const Template = require("../Template");
const Parser = require("../Parser");
const acorn = require("acorn");
const escope = require("escope");
const ReplaceSource = require("webpack-sources/lib/ReplaceSource");
const ConcatSource = require("webpack-sources/lib/ConcatSource");
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency");
const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
function ensureNsObjSource(info, moduleToInfoMap, requestShortener) {
if(!info.hasNamespaceObject) {
info.hasNamespaceObject = true;
const name = info.exportMap.get(true);
const nsObj = [`var ${name} = {};`];
for(const exportName of info.module.providedExports) {
const finalName = getFinalName(info, exportName, moduleToInfoMap, requestShortener, false);
nsObj.push(`__webpack_require__.d(${name}, ${JSON.stringify(exportName)}, function() { return ${finalName}; });`);
}
info.namespaceObjectSource = nsObj.join("\n") + "\n";
}
}
function getExternalImport(importedModule, info, exportName, asCall) {
if(exportName === true) return info.name;
const used = importedModule.isUsed(exportName);
if(!used) return "/* unused reexport */undefined";
if(info.interop && exportName === "default") {
return asCall ? `${info.interopName}()` : `${info.interopName}.a`;
}
// TODO use Template.toNormalComment when merging with pure-module
const comment = used !== exportName ? ` /* ${exportName} */` : "";
const reference = `${info.name}[${JSON.stringify(used)}${comment}]`;
if(asCall)
return `Object(${reference})`;
return reference;
}
function getFinalName(info, exportName, moduleToInfoMap, requestShortener, asCall) {
switch(info.type) {
case "concatenated":
{
const directExport = info.exportMap.get(exportName);
if(directExport) {
if(exportName === true)
ensureNsObjSource(info, moduleToInfoMap, requestShortener);
const name = info.internalNames.get(directExport);
if(!name)
throw new Error(`The export "${directExport}" in "${info.module.readableIdentifier(requestShortener)}" has no internal name`);
return name;
}
const reexport = info.reexportMap.get(exportName);
if(reexport) {
const refInfo = moduleToInfoMap.get(reexport.module);
if(refInfo) {
// module is in the concatenation
return getFinalName(refInfo, reexport.exportName, moduleToInfoMap, requestShortener, asCall);
}
}
const problem = `Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier(requestShortener)}"` +
` (known exports: ${Array.from(info.exportMap.keys()).filter(name => name !== true).join(" ")}, ` +
`known reexports: ${Array.from(info.reexportMap.keys()).join(" ")})`;
// TODO use Template.toNormalComment when merging with pure-module
return `/* ${problem} */ undefined`;
}
case "external":
{
const importedModule = info.module;
return getExternalImport(importedModule, info, exportName, asCall);
}
}
}
function getSymbolsFromScope(s, untilScope) {
const allUsedNames = new Set();
let scope = s;
while(scope) {
if(untilScope === scope) break;
scope.variables.forEach(variable => allUsedNames.add(variable.name));
scope = scope.upper;
}
return allUsedNames;
}
function getAllReferences(variable) {
let set = variable.references;
// Look for inner scope variables too (like in class Foo { t() { Foo } })
const identifiers = new Set(variable.identifiers);
for(const scope of variable.scope.childScopes) {
for(const innerVar of scope.variables) {
if(innerVar.identifiers.some(id => identifiers.has(id))) {
set = set.concat(innerVar.references);
break;
}
}
}
return set;
}
function reduceSet(a, b) {
for(const item of b)
a.add(item);
return a;
}
function getPathInAst(ast, node) {
if(ast === node) {
return [];
}
const nr = node.range;
var i;
if(Array.isArray(ast)) {
for(i = 0; i < ast.length; i++) {
const enterResult = enterNode(ast[i]);
if(typeof enterResult !== "undefined")
return enterResult;
}
} else if(ast && typeof ast === "object") {
const keys = Object.keys(ast);
for(i = 0; i < keys.length; i++) {
const value = ast[keys[i]];
if(Array.isArray(value)) {
const pathResult = getPathInAst(value, node);
if(typeof pathResult !== "undefined")
return pathResult;
} else if(value && typeof value === "object") {
const enterResult = enterNode(value);
if(typeof enterResult !== "undefined")
return enterResult;
}
}
}
function enterNode(n) {
const r = n.range;
if(r) {
if(r[0] <= nr[0] && r[1] >= nr[1]) {
const path = getPathInAst(n, node);
if(path) {
path.push(n);
return path;
}
}
}
return undefined;
}
}
class ConcatenatedModule extends Module {
constructor(rootModule, modules) {
super();
super.setChunks(rootModule._chunks);
this.rootModule = rootModule;
this.usedExports = rootModule.usedExports;
this.providedExports = rootModule.providedExports;
this.optimizationBailout = rootModule.optimizationBailout;
this.used = rootModule.used;
this.index = rootModule.index;
this.index2 = rootModule.index2;
this.depth = rootModule.depth;
this.built = modules.some(m => m.built);
this.cacheable = modules.every(m => m.cacheable);
const modulesSet = new Set(modules);
this.reasons = rootModule.reasons.filter(reason => !(reason.dependency instanceof HarmonyImportDependency) || !modulesSet.has(reason.module));
this.meta = rootModule.meta;
this.moduleArgument = rootModule.moduleArgument;
this.exportsArgument = rootModule.exportsArgument;
this.strict = true;
this._numberOfConcatenatedModules = modules.length;
this.dependencies = [];
this.dependenciesWarnings = [];
this.dependenciesErrors = [];
this.fileDependencies = [];
this.contextDependencies = [];
this.warnings = [];
this.errors = [];
this.assets = {};
this._orderedConcatenationList = this._createOrderedConcatenationList(rootModule, modulesSet);
for(const info of this._orderedConcatenationList) {
if(info.type === "concatenated") {
const m = info.module;
// populate dependencies
m.dependencies.filter(dep => !(dep instanceof HarmonyImportDependency) || !modulesSet.has(dep.module))
.forEach(d => this.dependencies.push(d));
// populate dep warning
m.dependenciesWarnings.forEach(depWarning => this.dependenciesWarnings.push(depWarning));
// populate dep errors
m.dependenciesErrors.forEach(depError => this.dependenciesErrors.push(depError));
// populate file dependencies
if(m.fileDependencies) m.fileDependencies.forEach(file => this.fileDependencies.push(file));
// populate context dependencies
if(m.contextDependencies) m.contextDependencies.forEach(context => this.contextDependencies.push(context));
// populate warnings
m.warnings.forEach(warning => this.warnings.push(warning));
// populate errors
m.errors.forEach(error => this.errors.push(error));
Object.assign(this.assets, m.assets);
}
}
}
get modules() {
return this._orderedConcatenationList
.filter(info => info.type === "concatenated")
.map(info => info.module);
}
identifier() {
return this._orderedConcatenationList.map(info => {
switch(info.type) {
case "concatenated":
return info.module.identifier();
}
}).filter(Boolean).join(" ");
}
readableIdentifier(requestShortener) {
return this.rootModule.readableIdentifier(requestShortener) + ` + ${this._numberOfConcatenatedModules - 1} modules`;
}
libIdent(options) {
return this.rootModule.libIdent(options);
}
nameForCondition() {
return this.rootModule.nameForCondition();
}
build(options, compilation, resolver, fs, callback) {
throw new Error("Cannot build this module. It should be already built.");
}
size() {
// Guess size from embedded modules
return this._orderedConcatenationList.reduce((sum, info) => {
switch(info.type) {
case "concatenated":
return sum + info.module.size();
case "external":
return sum + 5;
}
return sum;
}, 0);
}
_createOrderedConcatenationList(rootModule, modulesSet) {
const list = [];
const set = new Set();
function getConcatenatedImports(module) {
// TODO need changes when merging with the pure-module branch
const allDeps = module.dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module);
return allDeps.map(dep => () => dep.module);
}
function enterModule(getModule) {
const module = getModule();
if(set.has(module)) return;
set.add(module);
if(modulesSet.has(module)) {
const imports = getConcatenatedImports(module);
imports.forEach(enterModule);
list.push({
type: "concatenated",
module
});
} else {
list.push({
type: "external",
get module() {
// We need to use a getter here, because the module in the dependency
// could be replaced by some other process (i. e. also replaced with a
// concatenated module)
return getModule();
}
});
}
}
enterModule(() => rootModule);
return list;
}
source(dependencyTemplates, outputOptions, requestShortener) {
// Metainfo for each module
const modulesWithInfo = this._orderedConcatenationList.map((info, idx) => {
switch(info.type) {
case "concatenated":
{
const exportMap = new Map();
const reexportMap = new Map();
info.module.dependencies.forEach(dep => {
if(dep instanceof HarmonyExportSpecifierDependency) {
if(!exportMap.has(dep.name))
exportMap.set(dep.name, dep.id);
} else if(dep instanceof HarmonyExportExpressionDependency) {
if(!exportMap.has("default"))
exportMap.set("default", "__WEBPACK_MODULE_DEFAULT_EXPORT__");
} else if(dep instanceof HarmonyExportImportedSpecifierDependency) {
const exportName = dep.name;
const importName = dep.id;
const importedModule = dep.importDependency.module;
if(exportName && importName) {
if(!reexportMap.has(exportName)) {
reexportMap.set(exportName, {
module: importedModule,
exportName: importName,
dependency: dep
});
}
} else if(exportName) {
if(!reexportMap.has(exportName)) {
reexportMap.set(exportName, {
module: importedModule,
exportName: true,
dependency: dep
});
}
} else if(importedModule) {
importedModule.providedExports.forEach(name => {
if(dep.activeExports.has(name) || name === "default")
return;
if(!reexportMap.has(name)) {
reexportMap.set(name, {
module: importedModule,
exportName: name,
dependency: dep
});
}
});
}
}
});
return {
type: "concatenated",
module: info.module,
index: idx,
ast: undefined,
source: undefined,
globalScope: undefined,
moduleScope: undefined,
internalNames: new Map(),
exportMap: exportMap,
reexportMap: reexportMap,
hasNamespaceObject: false,
namespaceObjectSource: null
};
}
case "external":
return {
type: "external",
module: info.module,
index: idx,
name: undefined,
interopName: undefined,
interop: undefined
};
default:
throw new Error(`Unsupported concatenation entry type ${info.type}`);
}
});
// Create mapping from module to info
const moduleToInfoMap = new Map();
modulesWithInfo.forEach(m => moduleToInfoMap.set(m.module, m));
// Configure template decorators for dependencies
const innerDependencyTemplates = new Map(dependencyTemplates);
innerDependencyTemplates.set(HarmonyImportSpecifierDependency, new HarmonyImportSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportSpecifierDependency),
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyImportDependency, new HarmonyImportDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportDependency),
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyExportSpecifierDependency, new HarmonyExportSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportSpecifierDependency),
this.rootModule
));
innerDependencyTemplates.set(HarmonyExportExpressionDependency, new HarmonyExportExpressionDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportExpressionDependency),
this.rootModule,
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyExportImportedSpecifierDependency, new HarmonyExportImportedSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportImportedSpecifierDependency),
this.rootModule,
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyCompatibilityDependency, new HarmonyCompatibilityDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyCompatibilityDependency),
this.rootModule,
moduleToInfoMap
));
innerDependencyTemplates.set("hash", innerDependencyTemplates.get("hash") + this.rootModule.identifier());
// Generate source code and analyse scopes
// Prepare a ReplaceSource for the final source
modulesWithInfo.forEach(info => {
if(info.type === "concatenated") {
const m = info.module;
const source = m.source(innerDependencyTemplates, outputOptions, requestShortener);
const code = source.source();
let ast;
try {
ast = acorn.parse(code, {
ranges: true,
locations: true,
ecmaVersion: Parser.ECMA_VERSION,
sourceType: "module"
});
} catch(err) {
if(err.loc && typeof err.loc === "object" && typeof err.loc.line === "number") {
const lineNumber = err.loc.line;
const lines = code.split("\n");
err.message += "\n| " + lines.slice(Math.max(0, lineNumber - 3), lineNumber + 2).join("\n| ");
}
throw err;
}
const scopeManager = escope.analyze(ast, {
ecmaVersion: 6,
sourceType: "module",
optimistic: true,
ignoreEval: true,
impliedStrict: true
});
const globalScope = scopeManager.acquire(ast);
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(source);
info.ast = ast;
info.source = resultSource;
info.globalScope = globalScope;
info.moduleScope = moduleScope;
}
});
// List of all used names to avoid conflicts
const allUsedNames = new Set([
"__WEBPACK_MODULE_DEFAULT_EXPORT__", // avoid using this internal name
"abstract", "arguments", "async", "await", "boolean", "break", "byte", "case", "catch", "char", "class",
"const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval",
"export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if",
"implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new",
"null", "package", "private", "protected", "public", "return", "short", "static", "super",
"switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof",
"var", "void", "volatile", "while", "with", "yield",
"module", "__dirname", "__filename", "exports",
"Array", "Date", "eval", "function", "hasOwnProperty", "Infinity", "isFinite", "isNaN",
"isPrototypeOf", "length", "Math", "NaN", "name", "Number", "Object", "prototype", "String",
"toString", "undefined", "valueOf",
"alert", "all", "anchor", "anchors", "area", "assign", "blur", "button", "checkbox",
"clearInterval", "clearTimeout", "clientInformation", "close", "closed", "confirm", "constructor",
"crypto", "decodeURI", "decodeURIComponent", "defaultStatus", "document", "element", "elements",
"embed", "embeds", "encodeURI", "encodeURIComponent", "escape", "event", "fileUpload", "focus",
"form", "forms", "frame", "innerHeight", "innerWidth", "layer", "layers", "link", "location",
"mimeTypes", "navigate", "navigator", "frames", "frameRate", "hidden", "history", "image",
"images", "offscreenBuffering", "open", "opener", "option", "outerHeight", "outerWidth",
"packages", "pageXOffset", "pageYOffset", "parent", "parseFloat", "parseInt", "password", "pkcs11",
"plugin", "prompt", "propertyIsEnum", "radio", "reset", "screenX", "screenY", "scroll", "secure",
"select", "self", "setInterval", "setTimeout", "status", "submit", "taint", "text", "textarea",
"top", "unescape", "untaint", "window",
"onblur", "onclick", "onerror", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onmouseover",
"onload", "onmouseup", "onmousedown", "onsubmit"
]);
// get all global names
modulesWithInfo.forEach(info => {
if(info.globalScope) {
info.globalScope.through.forEach(reference => {
const name = reference.identifier.name;
if(/^__WEBPACK_MODULE_REFERENCE__\d+_([\da-f]+|ns)(_call)?__$/.test(name)) {
for(const s of getSymbolsFromScope(reference.from, info.moduleScope)) {
allUsedNames.add(s);
}
} else {
allUsedNames.add(name);
}
});
}
});
// generate names for symbols
modulesWithInfo.forEach(info => {
switch(info.type) {
case "concatenated":
{
const namespaceObjectName = this.findNewName("namespaceObject", allUsedNames, null, info.module.readableIdentifier(requestShortener));
allUsedNames.add(namespaceObjectName);
info.internalNames.set(namespaceObjectName, namespaceObjectName);
info.exportMap.set(true, namespaceObjectName);
info.moduleScope.variables.forEach(variable => {
const name = variable.name;
if(allUsedNames.has(name)) {
const references = getAllReferences(variable);
const symbolsInReferences = references.map(ref => getSymbolsFromScope(ref.from, info.moduleScope)).reduce(reduceSet, new Set());
const newName = this.findNewName(name, allUsedNames, symbolsInReferences, info.module.readableIdentifier(requestShortener));
allUsedNames.add(newName);
info.internalNames.set(name, newName);
const source = info.source;
const allIdentifiers = new Set(references.map(r => r.identifier).concat(variable.identifiers));
for(const identifier of allIdentifiers) {
const r = identifier.range;
const path = getPathInAst(info.ast, identifier);
if(path && path.length > 1 && path[1].type === "Property" && path[1].shorthand) {
source.insert(r[1], `: ${newName}`);
} else {
source.replace(r[0], r[1] - 1, newName);
}
}
} else {
allUsedNames.add(name);
info.internalNames.set(name, name);
}
});
break;
}
case "external":
{
info.interop = info.module.meta && !info.module.meta.harmonyModule;
const externalName = this.findNewName("", allUsedNames, null, info.module.readableIdentifier(requestShortener));
allUsedNames.add(externalName);
info.name = externalName;
if(info.interop) {
const externalNameInterop = this.findNewName("default", allUsedNames, null, info.module.readableIdentifier(requestShortener));
allUsedNames.add(externalNameInterop);
info.interopName = externalNameInterop;
}
break;
}
}
});
// Find and replace referenced to modules
modulesWithInfo.forEach(info => {
if(info.type === "concatenated") {
info.globalScope.through.forEach(reference => {
const name = reference.identifier.name;
const match = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?__$/.exec(name);
if(match) {
const referencedModule = modulesWithInfo[+match[1]];
let exportName;
if(match[2] === "ns") {
exportName = true;
} else {
const exportData = match[2];
exportName = new Buffer(exportData, "hex").toString("utf-8"); // eslint-disable-line node/no-deprecated-api
}
const asCall = !!match[3];
const finalName = getFinalName(referencedModule, exportName, moduleToInfoMap, requestShortener, asCall);
const r = reference.identifier.range;
const source = info.source;
source.replace(r[0], r[1] - 1, finalName);
}
});
}
});
const result = new ConcatSource();
// add harmony compatibility flag (must be first because of possible circular dependencies)
const usedExports = this.rootModule.usedExports;
if(usedExports === true) {
result.add(`Object.defineProperty(${this.exportsArgument || "exports"}, "__esModule", { value: true });\n`);
}
// define required namespace objects (must be before evaluation modules)
modulesWithInfo.forEach(info => {
if(info.namespaceObjectSource) {
result.add(info.namespaceObjectSource);
}
});
// evaluate modules in order
modulesWithInfo.forEach(info => {
switch(info.type) {
case "concatenated":
result.add(`\n// CONCATENATED MODULE: ${info.module.readableIdentifier(requestShortener)}\n`);
result.add(info.source);
break;
case "external":
result.add(`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(requestShortener)}\n`);
result.add(`var ${info.name} = __webpack_require__(${JSON.stringify(info.module.id)});\n`);
if(info.interop) {
result.add(`var ${info.interopName} = /*#__PURE__*/__webpack_require__.n(${info.name});\n`);
}
break;
default:
throw new Error(`Unsupported concatenation entry type ${info.type}`);
}
});
return result;
}
findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
let name = oldName;
if(name === "__WEBPACK_MODULE_DEFAULT_EXPORT__")
name = "";
// Remove uncool stuff
extraInfo = extraInfo.replace(/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g, "");
const splittedInfo = extraInfo.split("/");
while(splittedInfo.length) {
name = splittedInfo.pop() + (name ? "_" + name : "");
const nameIdent = Template.toIdentifier(name);
if(!usedNamed1.has(nameIdent) && (!usedNamed2 || !usedNamed2.has(nameIdent))) return nameIdent;
}
let i = 0;
let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
while(usedNamed1.has(nameWithNumber) || (usedNamed2 && usedNamed2.has(nameWithNumber))) {
i++;
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
}
return nameWithNumber;
}
updateHash(hash) {
for(const info of this._orderedConcatenationList) {
switch(info.type) {
case "concatenated":
info.module.updateHash(hash);
break;
case "external":
hash.update(`${info.module.id}`);
break;
}
}
super.updateHash(hash);
}
}
class HarmonyImportSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.importDependency.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
let content;
if(dep.id === null) {
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__`;
} else if(dep.namespaceObjectAsContext) {
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__[${JSON.stringify(dep.id)}]`;
} else {
const exportData = new Buffer(dep.id, "utf-8").toString("hex"); // eslint-disable-line node/no-deprecated-api
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${dep.call ? "_call" : ""}__`;
}
if(dep.shorthand) {
content = dep.name + ": " + content;
}
source.replace(dep.range[0], dep.range[1] - 1, content);
}
}
class HarmonyImportDependencyConcatenatedTemplate {
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
source.replace(dep.range[0], dep.range[1] - 1, "");
}
}
class HarmonyExportSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
}
}
}
class HarmonyExportExpressionDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
let content = "/* harmony default export */ var __WEBPACK_MODULE_DEFAULT_EXPORT__ = ";
if(dep.originModule === this.rootModule) {
const used = dep.originModule.isUsed("default");
const exportsName = dep.originModule.exportsArgument || "exports";
if(used) content += `${exportsName}[${JSON.stringify(used)}] = `;
}
if(dep.range) {
source.replace(dep.rangeStatement[0], dep.range[0] - 1, content + "(");
source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");");
return;
}
source.replace(dep.rangeStatement[0], dep.rangeStatement[1] - 1, content);
}
}
class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule, modulesMap) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
this.modulesMap = modulesMap;
}
getExports(dep) {
const importModule = dep.importDependency.module;
if(dep.id) {
// export { named } from "module"
return [{
name: dep.name,
id: dep.id,
module: importModule
}];
}
if(dep.name) {
// export * as abc from "module"
return [{
name: dep.name,
id: true,
module: importModule
}];
}
// export * from "module"
return importModule.providedExports.filter(exp => exp !== "default" && !dep.activeExports.has(exp)).map(exp => {
return {
name: exp,
id: exp,
module: importModule
};
});
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
if(this.modulesMap.get(dep.importDependency.module)) {
const exportDefs = this.getExports(dep);
exportDefs.forEach(def => {
const info = this.modulesMap.get(def.module);
const used = dep.originModule.isUsed(def.name);
if(!used) {
source.insert(-1, `/* unused concated harmony import ${dep.name} */\n`);
}
let finalName;
if(def.id === true) {
finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__`;
} else {
const exportData = new Buffer(def.id, "utf-8").toString("hex"); // eslint-disable-line node/no-deprecated-api
finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}__`;
}
const exportsName = this.rootModule.exportsArgument || "exports";
const content = `/* concated harmony reexport */__webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${finalName}; });\n`;
source.insert(-1, content);
});
} else {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
}
}
}
}
class HarmonyCompatibilityDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule, modulesMap) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
this.modulesMap = modulesMap;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
// do nothing
}
}
module.exports = ConcatenatedModule;

View File

@@ -0,0 +1,15 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class DedupePlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.warnings.push(new Error("DedupePlugin: This plugin was removed from webpack. Remove it from your configuration."));
});
}
}
module.exports = DedupePlugin;

View File

@@ -0,0 +1,40 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class EnsureChunkConditionsPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
const triesMap = new Map();
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
let changed = false;
chunks.forEach((chunk) => {
chunk.forEachModule((module) => {
if(!module.chunkCondition) return;
if(!module.chunkCondition(chunk)) {
let usedChunks = triesMap.get(module);
if(!usedChunks) triesMap.set(module, usedChunks = new Set());
usedChunks.add(chunk);
const newChunks = [];
chunk.parents.forEach((parent) => {
if(!usedChunks.has(parent)) {
parent.addModule(module);
module.addChunk(parent);
newChunks.push(parent);
}
});
module.rewriteChunkInReasons(chunk, newChunks);
chunk.removeModule(module);
changed = true;
}
});
});
if(changed) return true;
});
});
}
}
module.exports = EnsureChunkConditionsPlugin;

View File

@@ -0,0 +1,35 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class FlagIncludedChunksPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunk-ids", (chunks) => {
chunks.forEach((chunkA) => {
chunks.forEach((chunkB) => {
// as we iterate the same iterables twice
// skip if we find ourselves
if(chunkA === chunkB) return;
// instead of swapping A and B just bail
// as we loop twice the current A will be B and B then A
if(chunkA.getNumberOfModules() < chunkB.getNumberOfModules()) return;
if(chunkB.getNumberOfModules() === 0) return;
// is chunkB in chunkA?
for(const m of chunkB.modulesIterable) {
if(!chunkA.containsModule(m)) return;
}
chunkA.ids.push(chunkB.id);
});
});
});
});
}
}
module.exports = FlagIncludedChunksPlugin;

View File

@@ -0,0 +1,59 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class LimitChunkCountPlugin {
constructor(options) {
if(options !== undefined && typeof options !== "object" || Array.isArray(options)) {
throw new Error("Argument should be an options object.\nFor more info on options, see https://webpack.js.org/plugins/");
}
this.options = options || {};
}
apply(compiler) {
const options = this.options;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunks-advanced", (chunks) => {
const maxChunks = options.maxChunks;
if(!maxChunks) return;
if(maxChunks < 1) return;
if(chunks.length <= maxChunks) return;
if(chunks.length > maxChunks) {
const sortedExtendedPairCombinations = chunks.reduce((combinations, a, idx) => {
// create combination pairs
for(let i = 0; i < idx; i++) {
const b = chunks[i];
combinations.push([b, a]);
}
return combinations;
}, []).map((pair) => {
// extend combination pairs with size and integrated size
const a = pair[0].size(options);
const b = pair[1].size(options);
const ab = pair[0].integratedSize(pair[1], options);
return [a + b - ab, ab, pair[0], pair[1], a, b];
}).filter((extendedPair) => {
// filter pairs that do not have an integratedSize
// meaning they can NOT be integrated!
return extendedPair[1] !== false;
}).sort((a, b) => { // sadly javascript does an inplace sort here
// sort them by size
const diff = b[0] - a[0];
if(diff !== 0) return diff;
return a[1] - b[1];
});
const pair = sortedExtendedPairCombinations[0];
if(pair && pair[2].integrate(pair[3], "limit")) {
chunks.splice(chunks.indexOf(pair[3]), 1);
return true;
}
}
});
});
}
}
module.exports = LimitChunkCountPlugin;

View File

@@ -0,0 +1,28 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class MergeDuplicateChunksPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunks-basic", (chunks) => {
const map = Object.create(null);
chunks.slice().forEach((chunk) => {
if(chunk.hasRuntime() || chunk.hasEntryModule()) return;
const ident = chunk.getModulesIdent();
const otherChunk = map[ident];
if(otherChunk) {
if(otherChunk.integrate(chunk, "duplicate"))
chunks.splice(chunks.indexOf(chunk), 1);
return;
}
map[ident] = chunk;
});
});
});
}
}
module.exports = MergeDuplicateChunksPlugin;

View File

@@ -0,0 +1,65 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class MinChunkSizePlugin {
constructor(options) {
if(typeof options !== "object" || Array.isArray(options)) {
throw new Error("Argument should be an options object.\nFor more info on options, see https://webpack.js.org/plugins/");
}
this.options = options;
}
apply(compiler) {
const options = this.options;
const minChunkSize = options.minChunkSize;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunks-advanced", (chunks) => {
const equalOptions = {
chunkOverhead: 1,
entryChunkMultiplicator: 1
};
const sortedSizeFilteredExtendedPairCombinations = chunks.reduce((combinations, a, idx) => {
// create combination pairs
for(let i = 0; i < idx; i++) {
const b = chunks[i];
combinations.push([b, a]);
}
return combinations;
}, []).filter((pair) => {
// check if one of the chunks sizes is smaller than the minChunkSize
const p0SmallerThanMinChunkSize = pair[0].size(equalOptions) < minChunkSize;
const p1SmallerThanMinChunkSize = pair[1].size(equalOptions) < minChunkSize;
return p0SmallerThanMinChunkSize || p1SmallerThanMinChunkSize;
}).map((pair) => {
// extend combination pairs with size and integrated size
const a = pair[0].size(options);
const b = pair[1].size(options);
const ab = pair[0].integratedSize(pair[1], options);
return [a + b - ab, ab, pair[0], pair[1]];
}).filter((pair) => {
// filter pairs that do not have an integratedSize
// meaning they can NOT be integrated!
return pair[1] !== false;
}).sort((a, b) => { // sadly javascript does an inplace sort here
// sort by size
const diff = b[0] - a[0];
if(diff !== 0) return diff;
return a[1] - b[1];
});
if(sortedSizeFilteredExtendedPairCombinations.length === 0) return;
const pair = sortedSizeFilteredExtendedPairCombinations[0];
pair[2].integrate(pair[3], "min-size");
chunks.splice(chunks.indexOf(pair[3]), 1);
return true;
});
});
}
}
module.exports = MinChunkSizePlugin;

View File

@@ -0,0 +1,308 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const ModuleHotAcceptDependency = require("../dependencies/ModuleHotAcceptDependency");
const ModuleHotDeclineDependency = require("../dependencies/ModuleHotDeclineDependency");
const ConcatenatedModule = require("./ConcatenatedModule");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
function formatBailoutReason(msg) {
return "ModuleConcatenation bailout: " + msg;
}
class ModuleConcatenationPlugin {
constructor(options) {
if(typeof options !== "object") options = {};
this.options = options;
}
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
params.normalModuleFactory.plugin("parser", (parser, parserOptions) => {
parser.plugin("call eval", () => {
parser.state.module.meta.hasEval = true;
});
});
const bailoutReasonMap = new Map();
function setBailoutReason(module, reason) {
bailoutReasonMap.set(module, reason);
module.optimizationBailout.push(typeof reason === "function" ? (rs) => formatBailoutReason(reason(rs)) : formatBailoutReason(reason));
}
function getBailoutReason(module, requestShortener) {
const reason = bailoutReasonMap.get(module);
if(typeof reason === "function") return reason(requestShortener);
return reason;
}
compilation.plugin("optimize-chunk-modules", (chunks, modules) => {
const relevantModules = [];
const possibleInners = new Set();
for(const module of modules) {
// Only harmony modules are valid for optimization
if(!module.meta || !module.meta.harmonyModule || !module.dependencies.some(d => d instanceof HarmonyCompatibilityDependency)) {
setBailoutReason(module, "Module is not an ECMAScript module");
continue;
}
// Because of variable renaming we can't use modules with eval
if(module.meta && module.meta.hasEval) {
setBailoutReason(module, "Module uses eval()");
continue;
}
// Exports must be known (and not dynamic)
if(!Array.isArray(module.providedExports)) {
setBailoutReason(module, "Module exports are unknown");
continue;
}
// Using dependency variables is not possible as this wraps the code in a function
if(module.variables.length > 0) {
setBailoutReason(module, `Module uses injected variables (${module.variables.map(v => v.name).join(", ")})`);
continue;
}
// Hot Module Replacement need it's own module to work correctly
if(module.dependencies.some(dep => dep instanceof ModuleHotAcceptDependency || dep instanceof ModuleHotDeclineDependency)) {
setBailoutReason(module, "Module uses Hot Module Replacement");
continue;
}
relevantModules.push(module);
// Module must not be the entry points
if(module.getChunks().some(chunk => chunk.entryModule === module)) {
setBailoutReason(module, "Module is an entry point");
continue;
}
// Module must only be used by Harmony Imports
const nonHarmonyReasons = module.reasons.filter(reason => !(reason.dependency instanceof HarmonyImportDependency));
if(nonHarmonyReasons.length > 0) {
const importingModules = new Set(nonHarmonyReasons.map(r => r.module));
const importingModuleTypes = new Map(Array.from(importingModules).map(m => [m, new Set(nonHarmonyReasons.filter(r => r.module === m).map(r => r.dependency.type).sort())]));
setBailoutReason(module, (requestShortener) => {
const names = Array.from(importingModules).map(m => `${m.readableIdentifier(requestShortener)} (referenced with ${Array.from(importingModuleTypes.get(m)).join(", ")})`).sort();
return `Module is referenced from these modules with unsupported syntax: ${names.join(", ")}`;
});
continue;
}
possibleInners.add(module);
}
// sort by depth
// modules with lower depth are more likely suited as roots
// this improves performance, because modules already selected as inner are skipped
relevantModules.sort((a, b) => {
return a.depth - b.depth;
});
const concatConfigurations = [];
const usedAsInner = new Set();
for(const currentRoot of relevantModules) {
// when used by another configuration as inner:
// the other configuration is better and we can skip this one
if(usedAsInner.has(currentRoot))
continue;
// create a configuration with the root
const currentConfiguration = new ConcatConfiguration(currentRoot);
// cache failures to add modules
const failureCache = new Map();
// try to add all imports
for(const imp of this.getImports(currentRoot)) {
const problem = this.tryToAdd(currentConfiguration, imp, possibleInners, failureCache);
if(problem) {
failureCache.set(imp, problem);
currentConfiguration.addWarning(imp, problem);
}
}
if(!currentConfiguration.isEmpty()) {
concatConfigurations.push(currentConfiguration);
for(const module of currentConfiguration.modules) {
if(module !== currentConfiguration.rootModule)
usedAsInner.add(module);
}
}
}
// HACK: Sort configurations by length and start with the longest one
// to get the biggers groups possible. Used modules are marked with usedModules
// TODO: Allow to reuse existing configuration while trying to add dependencies.
// This would improve performance. O(n^2) -> O(n)
concatConfigurations.sort((a, b) => {
return b.modules.size - a.modules.size;
});
const usedModules = new Set();
for(const concatConfiguration of concatConfigurations) {
if(usedModules.has(concatConfiguration.rootModule))
continue;
const newModule = new ConcatenatedModule(concatConfiguration.rootModule, Array.from(concatConfiguration.modules));
concatConfiguration.sortWarnings();
for(const warning of concatConfiguration.warnings) {
newModule.optimizationBailout.push((requestShortener) => {
const reason = getBailoutReason(warning[0], requestShortener);
const reasonWithPrefix = reason ? ` (<- ${reason})` : "";
if(warning[0] === warning[1])
return formatBailoutReason(`Cannot concat with ${warning[0].readableIdentifier(requestShortener)}${reasonWithPrefix}`);
else
return formatBailoutReason(`Cannot concat with ${warning[0].readableIdentifier(requestShortener)} because of ${warning[1].readableIdentifier(requestShortener)}${reasonWithPrefix}`);
});
}
const chunks = concatConfiguration.rootModule.getChunks();
for(const m of concatConfiguration.modules) {
usedModules.add(m);
chunks.forEach(chunk => chunk.removeModule(m));
}
chunks.forEach(chunk => {
chunk.addModule(newModule);
newModule.addChunk(chunk);
if(chunk.entryModule === concatConfiguration.rootModule)
chunk.entryModule = newModule;
});
compilation.modules.push(newModule);
newModule.reasons.forEach(reason => reason.dependency.module = newModule);
newModule.dependencies.forEach(dep => {
if(dep.module) {
dep.module.reasons.forEach(reason => {
if(reason.dependency === dep)
reason.module = newModule;
});
}
});
}
compilation.modules = compilation.modules.filter(m => !usedModules.has(m));
});
});
}
getImports(module) {
return Array.from(new Set(module.dependencies
// Only harmony Dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module)
// Dependencies are simple enough to concat them
.filter(dep => {
return !module.dependencies.some(d =>
d instanceof HarmonyExportImportedSpecifierDependency &&
d.importDependency === dep &&
!d.id &&
!Array.isArray(dep.module.providedExports)
);
})
// Take the imported module
.map(dep => dep.module)
));
}
tryToAdd(config, module, possibleModules, failureCache) {
const cacheEntry = failureCache.get(module);
if(cacheEntry) {
return cacheEntry;
}
// Already added?
if(config.has(module)) {
return null;
}
// Not possible to add?
if(!possibleModules.has(module)) {
failureCache.set(module, module); // cache failures for performance
return module;
}
// module must be in the same chunks
if(!config.rootModule.hasEqualsChunks(module)) {
failureCache.set(module, module); // cache failures for performance
return module;
}
// Clone config to make experimental changes
const testConfig = config.clone();
// Add the module
testConfig.add(module);
// Every module which depends on the added module must be in the configuration too.
for(const reason of module.reasons) {
const problem = this.tryToAdd(testConfig, reason.module, possibleModules, failureCache);
if(problem) {
failureCache.set(module, problem); // cache failures for performance
return problem;
}
}
// Eagerly try to add imports too if possible
for(const imp of this.getImports(module)) {
const problem = this.tryToAdd(testConfig, imp, possibleModules, failureCache);
if(problem) {
config.addWarning(module, problem);
}
}
// Commit experimental changes
config.set(testConfig);
return null;
}
}
class ConcatConfiguration {
constructor(rootModule) {
this.rootModule = rootModule;
this.modules = new Set([rootModule]);
this.warnings = new Map();
}
add(module) {
this.modules.add(module);
}
has(module) {
return this.modules.has(module);
}
isEmpty() {
return this.modules.size === 1;
}
addWarning(module, problem) {
this.warnings.set(module, problem);
}
sortWarnings() {
this.warnings = new Map(Array.from(this.warnings).sort((a, b) => {
const ai = a[0].identifier();
const bi = b[0].identifier();
if(ai < bi) return -1;
if(ai > bi) return 1;
return 0;
}));
}
clone() {
const clone = new ConcatConfiguration(this.rootModule);
for(const module of this.modules)
clone.add(module);
for(const pair of this.warnings)
clone.addWarning(pair[0], pair[1]);
return clone;
}
set(config) {
this.rootModule = config.rootModule;
this.modules = new Set(config.modules);
this.warnings = new Map(config.warnings);
}
}
module.exports = ModuleConcatenationPlugin;

View File

@@ -0,0 +1,102 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class OccurrenceOrderPlugin {
constructor(preferEntry) {
if(preferEntry !== undefined && typeof preferEntry !== "boolean") {
throw new Error("Argument should be a boolean.\nFor more info on this plugin, see https://webpack.js.org/plugins/");
}
this.preferEntry = preferEntry;
}
apply(compiler) {
const preferEntry = this.preferEntry;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-module-order", (modules) => {
const occursInInitialChunksMap = new Map();
const occursInAllChunksMap = new Map();
const initialChunkChunkMap = new Map();
const entryCountMap = new Map();
modules.forEach(m => {
let initial = 0;
let entry = 0;
m.forEachChunk(c => {
if(c.isInitial()) initial++;
if(c.entryModule === m) entry++;
});
initialChunkChunkMap.set(m, initial);
entryCountMap.set(m, entry);
});
const countOccursInEntry = (sum, r) => {
if(!r.module) return sum;
return sum + initialChunkChunkMap.get(r.module);
};
const countOccurs = (sum, r) => {
if(!r.module) return sum;
return sum + r.module.getNumberOfChunks();
};
if(preferEntry) {
modules.forEach(m => {
const result = m.reasons.reduce(countOccursInEntry, 0) + initialChunkChunkMap.get(m) + entryCountMap.get(m);
occursInInitialChunksMap.set(m, result);
});
}
modules.forEach(m => {
const result = m.reasons.reduce(countOccurs, 0) + m.getNumberOfChunks() + entryCountMap.get(m);
occursInAllChunksMap.set(m, result);
});
modules.sort((a, b) => {
if(preferEntry) {
const aEntryOccurs = occursInInitialChunksMap.get(a);
const bEntryOccurs = occursInInitialChunksMap.get(b);
if(aEntryOccurs > bEntryOccurs) return -1;
if(aEntryOccurs < bEntryOccurs) return 1;
}
const aOccurs = occursInAllChunksMap.get(a);
const bOccurs = occursInAllChunksMap.get(b);
if(aOccurs > bOccurs) return -1;
if(aOccurs < bOccurs) return 1;
if(a.index > b.index) return 1;
if(a.index < b.index) return -1;
return 0;
});
});
compilation.plugin("optimize-chunk-order", (chunks) => {
const occursInInitialChunksMap = new Map();
chunks.forEach(c => {
const result = c.parents.reduce((sum, p) => {
if(p.isInitial()) return sum + 1;
return sum;
}, 0);
return occursInInitialChunksMap.set(c, result);
});
function occurs(c) {
return c.blocks.length;
}
chunks.sort((a, b) => {
const aEntryOccurs = occursInInitialChunksMap.get(a);
const bEntryOccurs = occursInInitialChunksMap.get(b);
if(aEntryOccurs > bEntryOccurs) return -1;
if(aEntryOccurs < bEntryOccurs) return 1;
const aOccurs = occurs(a);
const bOccurs = occurs(b);
if(aOccurs > bOccurs) return -1;
if(aOccurs < bOccurs) return 1;
return a.compareTo(b);
});
});
});
}
}
module.exports = OccurrenceOrderPlugin;

View File

@@ -0,0 +1,21 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class RemoveEmptyChunksPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
chunks.filter((chunk) => chunk.isEmpty() && !chunk.hasRuntime() && !chunk.hasEntryModule())
.forEach((chunk) => {
chunk.remove("empty");
chunks.splice(chunks.indexOf(chunk), 1);
});
});
});
}
}
module.exports = RemoveEmptyChunksPlugin;

View File

@@ -0,0 +1,65 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
function hasModule(chunk, module, checkedChunks) {
if(chunk.containsModule(module)) return [chunk];
if(chunk.parents.length === 0) return false;
return allHaveModule(chunk.parents.filter((c) => {
return !checkedChunks.has(c);
}), module, checkedChunks);
}
function allHaveModule(someChunks, module, checkedChunks) {
if(!checkedChunks) checkedChunks = new Set();
var chunks = new Set();
for(var i = 0; i < someChunks.length; i++) {
checkedChunks.add(someChunks[i]);
var subChunks = hasModule(someChunks[i], module, checkedChunks);
if(!subChunks) return false;
for(var index = 0; index < subChunks.length; index++) {
var item = subChunks[index];
chunks.add(item);
}
}
return chunks;
}
class RemoveParentModulesPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
for(var index = 0; index < chunks.length; index++) {
var chunk = chunks[index];
if(chunk.parents.length === 0) continue;
// TODO consider Map when performance has improved https://gist.github.com/sokra/b36098368da7b8f6792fd7c85fca6311
var cache = Object.create(null);
var modules = chunk.getModules();
for(var i = 0; i < modules.length; i++) {
var module = modules[i];
var dId = module.getChunkIdsIdent();
var parentChunksWithModule;
if(dId === null) {
parentChunksWithModule = allHaveModule(chunk.parents, module);
} else if(dId in cache) {
parentChunksWithModule = cache[dId];
} else {
parentChunksWithModule = cache[dId] = allHaveModule(chunk.parents, module);
}
if(parentChunksWithModule) {
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));
chunk.removeModule(module);
}
}
}
});
});
}
}
module.exports = RemoveParentModulesPlugin;

View File

@@ -0,0 +1,9 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
module.exports = UglifyJsPlugin;