Merge branch 'master' into jest

# Conflicts:
#	lib/Chunk.js
#	lib/HotModuleReplacement.runtime.js
#	lib/Module.js
#	lib/MultiCompiler.js
#	lib/Parser.js
#	lib/Stats.js
#	test/BenchmarkTestCases.benchmark.js
#	test/CachePlugin.unittest.js
#	test/CaseSensitiveModulesWarning.unittest.js
#	test/Chunk.unittest.js
#	test/Compiler-caching.test.js
#	test/Compiler.test.js
#	test/ConfigTestCases.test.js
#	test/ContextModuleFactory.unittest.js
#	test/DependenciesBlockVariable.unittest.js
#	test/Errors.test.js
#	test/Examples.test.js
#	test/HotModuleReplacementPlugin.test.js
#	test/HotTestCases.test.js
#	test/Integration.test.js
#	test/LocalModulesHelpers.unittest.js
#	test/ModuleDependencyError.unittest.js
#	test/ModuleReason.unittest.js
#	test/MultiStats.unittest.js
#	test/NodeTemplatePlugin.test.js
#	test/NodeWatchFileSystem.unittest.js
#	test/NormalModule.unittest.js
#	test/NullDependency.unittest.js
#	test/Parser.unittest.js
#	test/ProfilingPlugin.unittest.js
#	test/RawModule.unittest.js
#	test/RuleSet.unittest.js
#	test/Schemas.lint.js
#	test/SideEffectsFlagPlugin.unittest.js
#	test/SizeFormatHelpers.unittest.js
#	test/SourceMapDevToolModuleOptionsPlugin.unittest.js
#	test/Stats.test.js
#	test/Stats.unittest.js
#	test/StatsTestCases.test.js
#	test/Template.unittest.js
#	test/TestCases.test.js
#	test/Validation.test.js
#	test/WatchDetection.test.js
#	test/WatchTestCases.test.js
#	test/WatcherEvents.test.js
#	test/WebEnvironmentPlugin.unittest.js
#	test/WebpackMissingModule.unittest.js
#	test/compareLocations.unittest.js
#	test/identifier.unittest.js
This commit is contained in:
Bazyli Brzóska 2018-02-25 12:00:09 +01:00
commit 650cf52e56
124 changed files with 6371 additions and 3906 deletions

View File

@ -162,6 +162,7 @@ class Chunk {
if (this._modules.size < otherChunk._modules.size) return 1;
const a = this._modules[Symbol.iterator]();
const b = otherChunk._modules[Symbol.iterator]();
// eslint-disable-next-line
while (true) {
// eslint-disable-line
const aItem = a.next();

View File

@ -12,6 +12,7 @@ module.exports = function() {
var hotCurrentParents = []; // eslint-disable-line no-unused-vars
var hotCurrentParentsTemp = []; // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotCreateRequire(moduleId) {
// eslint-disable-line no-unused-vars
var me = installedModules[moduleId];
@ -80,6 +81,7 @@ module.exports = function() {
return fn;
}
// eslint-disable-next-line no-unused-vars
function hotCreateModule(moduleId) {
// eslint-disable-line no-unused-vars
var hot = {
@ -206,6 +208,7 @@ module.exports = function() {
});
}
// eslint-disable-next-line no-unused-vars
function hotAddUpdateChunk(chunkId, moreModules) {
// eslint-disable-line no-unused-vars
if (!hotAvailableFilesMap[chunkId] || !hotRequestedFilesMap[chunkId])

View File

@ -163,8 +163,8 @@ class Module extends DependenciesBlock {
otherModule._chunks.sortWith(sortByDebugId);
const a = this._chunks[Symbol.iterator]();
const b = otherModule._chunks[Symbol.iterator]();
// eslint-disable-next-line no-constant-condition
while (true) {
// eslint-disable-line
const aItem = a.next();
const bItem = b.next();
if (aItem.done) return true;

View File

@ -34,8 +34,8 @@ module.exports = class MultiCompiler extends Tapable {
for (const compiler of this.compilers) {
let compilerDone = false;
const compilerIndex = index++;
// eslint-disable-next-line no-loop-func
compiler.hooks.done.tap("MultiCompiler", stats => {
// eslint-disable-line no-loop-func
if (!compilerDone) {
compilerDone = true;
doneCompilers++;
@ -45,8 +45,8 @@ module.exports = class MultiCompiler extends Tapable {
this.hooks.done.call(new MultiStats(compilerStats));
}
});
// eslint-disable-next-line no-loop-func
compiler.hooks.invalid.tap("MultiCompiler", () => {
// eslint-disable-line no-loop-func
if (compilerDone) {
compilerDone = false;
doneCompilers--;

View File

@ -2055,7 +2055,9 @@ class Parser extends Tapable {
try {
ast = acorn.parse(code, parserOptions);
threw = false;
} catch (e) {}
} catch (e) {
threw = true;
}
}
if (threw) {

View File

@ -97,7 +97,7 @@ class Stats {
if (typeof item === "string") {
const regExp = new RegExp(
`[\\\\/]${item.replace(
/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
/[-[\]{}()*+?.\\^$|]/g,
"\\$&"
)}([\\\\/]|$|!|\\?)`
); // eslint-disable-line no-useless-escape

View File

@ -6,7 +6,7 @@ let inspector = undefined;
try {
inspector = require("inspector"); // eslint-disable-line node/no-missing-require
} catch(e) {
} catch (e) {
console.log("Unable to CPU profile in < node 8.0");
}
@ -21,14 +21,14 @@ class Profiler {
}
startProfiling() {
if(this.inspector === undefined) {
if (this.inspector === undefined) {
return Promise.resolve();
}
try {
this.session = new inspector.Session();
this.session.connect();
} catch(_) {
} catch (_) {
this.session = undefined;
return Promise.resolve();
}
@ -38,15 +38,15 @@ class Profiler {
interval: 100
}),
this.sendCommand("Profiler.enable"),
this.sendCommand("Profiler.start"),
this.sendCommand("Profiler.start")
]);
}
sendCommand(method, params) {
if(this.hasSession()) {
if (this.hasSession()) {
return new Promise((res, rej) => {
return this.session.post(method, params, (err, params) => {
if(err !== null) {
if (err !== null) {
rej(err);
} else {
res(params);
@ -59,7 +59,7 @@ class Profiler {
}
destroy() {
if(this.hasSession()) {
if (this.hasSession()) {
this.session.disconnect();
}
@ -94,11 +94,13 @@ function createTrace(outputPath) {
data: {
sessionId: "-1",
page: "0xfff",
frames: [{
frame: "0xfff",
url: "webpack",
name: ""
}]
frames: [
{
frame: "0xfff",
url: "webpack",
name: ""
}
]
}
}
});
@ -110,7 +112,7 @@ function createTrace(outputPath) {
args: {
data: {
sessionId: "-1"
},
}
}
});
@ -136,83 +138,97 @@ class ProfilingPlugin {
// Compiler Hooks
Object.keys(compiler.hooks).forEach(hookName => {
compiler.hooks[hookName].intercept(makeInterceptorFor("Compiler", tracer)(hookName));
compiler.hooks[hookName].intercept(
makeInterceptorFor("Compiler", tracer)(hookName)
);
});
Object.keys(compiler.resolverFactory.hooks).forEach(hookName => {
compiler.resolverFactory.hooks[hookName].intercept(makeInterceptorFor("Resolver", tracer)(hookName));
compiler.resolverFactory.hooks[hookName].intercept(
makeInterceptorFor("Resolver", tracer)(hookName)
);
});
compiler.hooks.compilation.tap(pluginName, (compilation, {
normalModuleFactory,
contextModuleFactory
}) => {
interceptAllHooksFor(compilation, tracer, "Compilation");
interceptAllHooksFor(normalModuleFactory, tracer, "Normal Module Factory");
interceptAllHooksFor(contextModuleFactory, tracer, "Context Module Factory");
interceptAllParserHooks(normalModuleFactory, tracer);
interceptTemplateInstancesFrom(compilation, tracer);
});
compiler.hooks.compilation.tap(
pluginName,
(compilation, { normalModuleFactory, contextModuleFactory }) => {
interceptAllHooksFor(compilation, tracer, "Compilation");
interceptAllHooksFor(
normalModuleFactory,
tracer,
"Normal Module Factory"
);
interceptAllHooksFor(
contextModuleFactory,
tracer,
"Context Module Factory"
);
interceptAllParserHooks(normalModuleFactory, tracer);
interceptTemplateInstancesFrom(compilation, tracer);
}
);
// We need to write out the CPU profile when we are all done.
compiler.hooks.done.tap({
name: pluginName,
stage: Infinity
}, () => {
tracer.profiler.stopProfiling().then((parsedResults) => {
compiler.hooks.done.tap(
{
name: pluginName,
stage: Infinity
},
() => {
tracer.profiler.stopProfiling().then(parsedResults => {
if (parsedResults === undefined) {
tracer.profiler.destroy();
tracer.trace.flush();
return;
}
const cpuStartTime = parsedResults.profile.startTime;
const cpuEndTime = parsedResults.profile.endTime;
tracer.trace.completeEvent({
name: "TaskQueueManager::ProcessTaskFromWorkQueue",
id: ++tracer.counter,
cat: ["toplevel"],
ts: cpuStartTime,
args: {
src_file: "../../ipc/ipc_moji_bootstrap.cc",
src_func: "Accept"
}
});
tracer.trace.completeEvent({
name: "EvaluateScript",
id: ++tracer.counter,
cat: ["devtools.timeline"],
ts: cpuStartTime,
dur: cpuEndTime - cpuStartTime,
args: {
data: {
url: "webpack",
lineNumber: 1,
columnNumber: 1,
frame: "0xFFF"
}
}
});
tracer.trace.instantEvent({
name: "CpuProfile",
id: ++tracer.counter,
cat: ["disabled-by-default-devtools.timeline"],
ts: cpuEndTime,
args: {
data: {
cpuProfile: parsedResults.profile
}
}
});
if(parsedResults === undefined) {
tracer.profiler.destroy();
tracer.trace.flush();
return;
}
const cpuStartTime = parsedResults.profile.startTime;
const cpuEndTime = parsedResults.profile.endTime;
tracer.trace.completeEvent({
name: "TaskQueueManager::ProcessTaskFromWorkQueue",
id: ++tracer.counter,
cat: ["toplevel"],
ts: cpuStartTime,
args: {
src_file: "../../ipc/ipc_moji_bootstrap.cc",
src_func: "Accept"
}
});
tracer.trace.completeEvent({
name: "EvaluateScript",
id: ++tracer.counter,
cat: ["devtools.timeline"],
ts: cpuStartTime,
dur: cpuEndTime - cpuStartTime,
args: {
data: {
url: "webpack",
lineNumber: 1,
columnNumber: 1,
frame: "0xFFF"
}
}
});
tracer.trace.instantEvent({
name: "CpuProfile",
id: ++tracer.counter,
cat: ["disabled-by-default-devtools.timeline"],
ts: cpuEndTime,
args: {
data: {
cpuProfile: parsedResults.profile
}
}
});
tracer.profiler.destroy();
tracer.trace.flush();
});
});
}
);
}
}
@ -224,12 +240,10 @@ const interceptTemplateInstancesFrom = (compilation, tracer) => {
moduleTemplates
} = compilation;
const {
javascript,
webassembly
} = moduleTemplates;
const { javascript, webassembly } = moduleTemplates;
[{
[
{
instance: mainTemplate,
name: "MainTemplate"
},
@ -251,15 +265,19 @@ const interceptTemplateInstancesFrom = (compilation, tracer) => {
}
].forEach(templateObject => {
Object.keys(templateObject.instance.hooks).forEach(hookName => {
templateObject.instance.hooks[hookName].intercept(makeInterceptorFor(templateObject.name, tracer)(hookName));
templateObject.instance.hooks[hookName].intercept(
makeInterceptorFor(templateObject.name, tracer)(hookName)
);
});
});
};
const interceptAllHooksFor = (instance, tracer, logLabel) => {
if(Reflect.has(instance, "hooks")) {
if (Reflect.has(instance, "hooks")) {
Object.keys(instance.hooks).forEach(hookName => {
instance.hooks[hookName].intercept(makeInterceptorFor(logLabel, tracer)(hookName));
instance.hooks[hookName].intercept(
makeInterceptorFor(logLabel, tracer)(hookName)
);
});
}
};
@ -274,28 +292,27 @@ const interceptAllParserHooks = (moduleFactory, tracer) => {
];
moduleTypes.forEach(moduleType => {
moduleFactory.hooks.parser.for(moduleType).tap("ProfilingPlugin", (parser, parserOpts) => {
interceptAllHooksFor(parser, tracer, "Parser");
});
moduleFactory.hooks.parser
.for(moduleType)
.tap("ProfilingPlugin", (parser, parserOpts) => {
interceptAllHooksFor(parser, tracer, "Parser");
});
});
};
const makeInterceptorFor = (instance, tracer) => (hookName) => ({
register: ({
name,
type,
fn
}) => {
const makeInterceptorFor = (instance, tracer) => hookName => ({
register: ({ name, type, fn }) => {
const newFn = makeNewProfiledTapFn(hookName, tracer, {
name,
type,
fn
});
return({ // eslint-disable-line
return {
// eslint-disable-line
name,
type,
fn: newFn
});
};
}
});
@ -305,16 +322,13 @@ const makeInterceptorFor = (instance, tracer) => (hookName) => ({
* @param {{name: string, type: string, fn: Function}} opts Options for the profiled fn.
* @returns {*} Chainable hooked function.
*/
const makeNewProfiledTapFn = (hookName, tracer, {
name,
type,
fn
}) => {
const makeNewProfiledTapFn = (hookName, tracer, { name, type, fn }) => {
const defaultCategory = ["blink.user_timing"];
switch(type) {
switch (type) {
case "promise":
return(...args) => { // eslint-disable-line
return (...args) => {
// eslint-disable-line
const id = ++tracer.counter;
tracer.trace.begin({
name,
@ -331,7 +345,8 @@ const makeNewProfiledTapFn = (hookName, tracer, {
});
};
case "async":
return(...args) => { // eslint-disable-line
return (...args) => {
// eslint-disable-line
const id = ++tracer.counter;
tracer.trace.begin({
name,
@ -349,11 +364,12 @@ const makeNewProfiledTapFn = (hookName, tracer, {
});
};
case "sync":
return(...args) => { // eslint-disable-line
return (...args) => {
// eslint-disable-line
const id = ++tracer.counter;
// Do not instrument outself due to the CPU
// profile needing to be the last event in the trace.
if(name === pluginName) {
if (name === pluginName) {
return fn(...args);
}
@ -365,7 +381,7 @@ const makeNewProfiledTapFn = (hookName, tracer, {
let r;
try {
r = fn(...args);
} catch(error) {
} catch (error) {
tracer.trace.end({
name,
id,

View File

@ -28,10 +28,7 @@ AMDDefineDependency.Template = class AMDDefineDependencyTemplate {
`!(__WEBPACK_AMD_DEFINE_RESULT__ = (#).call(exports, __webpack_require__, exports, module),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))`
],
o: [
"",
"!(module.exports = #)"
],
o: ["", "!(module.exports = #)"],
of: [
"var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;",
`!(__WEBPACK_AMD_DEFINE_FACTORY__ = (#),
@ -45,10 +42,7 @@ AMDDefineDependency.Template = class AMDDefineDependencyTemplate {
`!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, __WEBPACK_AMD_DEFINE_RESULT__ = (#).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))`
],
ao: [
"",
"!(#, module.exports = #)"
],
ao: ["", "!(#, module.exports = #)"],
aof: [
"var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;",
`!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, __WEBPACK_AMD_DEFINE_FACTORY__ = (#),
@ -60,10 +54,7 @@ AMDDefineDependency.Template = class AMDDefineDependencyTemplate {
"var XXX, XXXmodule;",
"!(XXXmodule = { id: YYY, exports: {}, loaded: false }, XXX = #.call(XXXmodule.exports, __webpack_require__, XXXmodule.exports, XXXmodule), XXXmodule.loaded = true, XXX === undefined && (XXX = XXXmodule.exports))"
],
lo: [
"var XXX;",
"!(XXX = #)"
],
lo: ["var XXX;", "!(XXX = #)"],
lof: [
"var XXX, XXXfactory, XXXmodule;",
"!(XXXfactory = (#), (XXXmodule = { id: YYY, exports: {}, loaded: false }), XXX = (typeof XXXfactory === 'function' ? (XXXfactory.call(XXXmodule.exports, __webpack_require__, XXXmodule.exports, XXXmodule)) : XXXfactory), (XXXmodule.loaded = true), XXX === undefined && (XXX = XXXmodule.exports))"
@ -72,10 +63,7 @@ AMDDefineDependency.Template = class AMDDefineDependencyTemplate {
"var __WEBPACK_AMD_DEFINE_ARRAY__, XXX;",
"!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, XXX = ((#).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)))"
],
lao: [
"var XXX;",
"!(#, XXX = #)"
],
lao: ["var XXX;", "!(#, XXX = #)"],
laof: [
"var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_FACTORY__, XXX;",
`!(__WEBPACK_AMD_DEFINE_ARRAY__ = #, __WEBPACK_AMD_DEFINE_FACTORY__ = (#),
@ -94,7 +82,11 @@ AMDDefineDependency.Template = class AMDDefineDependencyTemplate {
}
localModuleVar(dependency) {
return dependency.localModule && dependency.localModule.used && dependency.localModule.variableName();
return (
dependency.localModule &&
dependency.localModule.used &&
dependency.localModule.variableName()
);
}
branch(dependency) {
@ -107,35 +99,37 @@ AMDDefineDependency.Template = class AMDDefineDependencyTemplate {
replace(dependency, source, definition, text) {
const localModuleVar = this.localModuleVar(dependency);
if(localModuleVar) {
if (localModuleVar) {
text = text.replace(/XXX/g, localModuleVar.replace(/\$/g, "$$$$"));
definition = definition.replace(/XXX/g, localModuleVar.replace(/\$/g, "$$$$"));
definition = definition.replace(
/XXX/g,
localModuleVar.replace(/\$/g, "$$$$")
);
}
if(dependency.namedModule) {
if (dependency.namedModule) {
text = text.replace(/YYY/g, JSON.stringify(dependency.namedModule));
}
const texts = text.split("#");
if(definition) source.insert(0, definition);
if (definition) source.insert(0, definition);
let current = dependency.range[0];
if(dependency.arrayRange) {
if (dependency.arrayRange) {
source.replace(current, dependency.arrayRange[0] - 1, texts.shift());
current = dependency.arrayRange[1];
}
if(dependency.objectRange) {
if (dependency.objectRange) {
source.replace(current, dependency.objectRange[0] - 1, texts.shift());
current = dependency.objectRange[1];
} else if(dependency.functionRange) {
} else if (dependency.functionRange) {
source.replace(current, dependency.functionRange[0] - 1, texts.shift());
current = dependency.functionRange[1];
}
source.replace(current, dependency.range[1] - 1, texts.shift());
if(texts.length > 0)
throw new Error("Implementation error");
if (texts.length > 0) throw new Error("Implementation error");
}
};

View File

@ -14,24 +14,24 @@ const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const LocalModulesHelpers = require("./LocalModulesHelpers");
const isBoundFunctionExpression = expr => {
if(expr.type !== "CallExpression") return false;
if(expr.callee.type !== "MemberExpression") return false;
if(expr.callee.computed) return false;
if(expr.callee.object.type !== "FunctionExpression") return false;
if(expr.callee.property.type !== "Identifier") return false;
if(expr.callee.property.name !== "bind") return false;
if (expr.type !== "CallExpression") return false;
if (expr.callee.type !== "MemberExpression") return false;
if (expr.callee.computed) return false;
if (expr.callee.object.type !== "FunctionExpression") return false;
if (expr.callee.property.type !== "Identifier") return false;
if (expr.callee.property.name !== "bind") return false;
return true;
};
function isUnboundFunctionExpression(expr) {
if(expr.type === "FunctionExpression") return true;
if(expr.type === "ArrowFunctionExpression") return true;
if (expr.type === "FunctionExpression") return true;
if (expr.type === "ArrowFunctionExpression") return true;
return false;
}
function isCallable(expr) {
if(isUnboundFunctionExpression(expr)) return true;
if(isBoundFunctionExpression(expr)) return true;
if (isUnboundFunctionExpression(expr)) return true;
if (isBoundFunctionExpression(expr)) return true;
return false;
}
@ -40,36 +40,57 @@ class AMDDefineDependencyParserPlugin {
this.options = options;
}
newDefineDependency(range, arrayRange, functionRange, objectRange, namedModule) {
return new AMDDefineDependency(range, arrayRange, functionRange, objectRange, namedModule);
newDefineDependency(
range,
arrayRange,
functionRange,
objectRange,
namedModule
) {
return new AMDDefineDependency(
range,
arrayRange,
functionRange,
objectRange,
namedModule
);
}
apply(parser) {
const options = this.options;
const processArray = (expr, param, identifiers, namedModule) => {
if(param.isArray()) {
if (param.isArray()) {
param.items.forEach((param, idx) => {
if(param.isString() && ["require", "module", "exports"].includes(param.string))
if (
param.isString() &&
["require", "module", "exports"].includes(param.string)
)
identifiers[idx] = param.string;
const result = processItem(expr, param, namedModule);
if(result === undefined) {
if (result === undefined) {
processContext(expr, param);
}
});
return true;
} else if(param.isConstArray()) {
} else if (param.isConstArray()) {
const deps = [];
param.array.forEach((request, idx) => {
let dep;
let localModule;
if(request === "require") {
if (request === "require") {
identifiers[idx] = request;
dep = "__webpack_require__";
} else if(["exports", "module"].includes(request)) {
} else if (["exports", "module"].includes(request)) {
identifiers[idx] = request;
dep = request;
} else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, request)) { // eslint-disable-line no-cond-assign
} else if (
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
request
))
) {
// eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
@ -89,21 +110,28 @@ class AMDDefineDependencyParserPlugin {
}
};
const processItem = (expr, param, namedModule) => {
if(param.isConditional()) {
param.options.forEach((param) => {
if (param.isConditional()) {
param.options.forEach(param => {
const result = processItem(expr, param);
if(result === undefined) {
if (result === undefined) {
processContext(expr, param);
}
});
return true;
} else if(param.isString()) {
} else if (param.isString()) {
let dep, localModule;
if(param.string === "require") {
if (param.string === "require") {
dep = new ConstDependency("__webpack_require__", param.range);
} else if(["require", "exports", "module"].includes(param.string)) {
} else if (["require", "exports", "module"].includes(param.string)) {
dep = new ConstDependency(param.string, param.range);
} else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, param.string, namedModule)) { // eslint-disable-line no-cond-assign
} else if (
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
param.string,
namedModule
))
) {
// eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule, param.range);
} else {
dep = new AMDRequireItemDependency(param.string, param.range);
@ -115,154 +143,169 @@ class AMDDefineDependencyParserPlugin {
}
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
if(!dep) return;
const dep = ContextDependencyHelpers.create(
AMDRequireContextDependency,
param.range,
param,
expr,
options
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.call.for("define").tap("AMDDefineDependencyParserPlugin", (expr) => {
let array, fn, obj, namedModule;
switch(expr.arguments.length) {
case 1:
if(isCallable(expr.arguments[0])) {
// define(f() {...})
fn = expr.arguments[0];
} else if(expr.arguments[0].type === "ObjectExpression") {
// define({...})
obj = expr.arguments[0];
} else {
// define(expr)
// unclear if function or object
obj = fn = expr.arguments[0];
}
break;
case 2:
if(expr.arguments[0].type === "Literal") {
parser.hooks.call
.for("define")
.tap("AMDDefineDependencyParserPlugin", expr => {
let array, fn, obj, namedModule;
switch (expr.arguments.length) {
case 1:
if (isCallable(expr.arguments[0])) {
// define(f() {...})
fn = expr.arguments[0];
} else if (expr.arguments[0].type === "ObjectExpression") {
// define({...})
obj = expr.arguments[0];
} else {
// define(expr)
// unclear if function or object
obj = fn = expr.arguments[0];
}
break;
case 2:
if (expr.arguments[0].type === "Literal") {
namedModule = expr.arguments[0].value;
// define("...", ...)
if (isCallable(expr.arguments[1])) {
// define("...", f() {...})
fn = expr.arguments[1];
} else if (expr.arguments[1].type === "ObjectExpression") {
// define("...", {...})
obj = expr.arguments[1];
} else {
// define("...", expr)
// unclear if function or object
obj = fn = expr.arguments[1];
}
} else {
array = expr.arguments[0];
if (isCallable(expr.arguments[1])) {
// define([...], f() {})
fn = expr.arguments[1];
} else if (expr.arguments[1].type === "ObjectExpression") {
// define([...], {...})
obj = expr.arguments[1];
} else {
// define([...], expr)
// unclear if function or object
obj = fn = expr.arguments[1];
}
}
break;
case 3:
// define("...", [...], f() {...})
namedModule = expr.arguments[0].value;
// define("...", ...)
if(isCallable(expr.arguments[1])) {
// define("...", f() {...})
fn = expr.arguments[1];
} else if(expr.arguments[1].type === "ObjectExpression") {
// define("...", {...})
obj = expr.arguments[1];
array = expr.arguments[1];
if (isCallable(expr.arguments[2])) {
// define("...", [...], f() {})
fn = expr.arguments[2];
} else if (expr.arguments[2].type === "ObjectExpression") {
// define("...", [...], {...})
obj = expr.arguments[2];
} else {
// define("...", expr)
// define("...", [...], expr)
// unclear if function or object
obj = fn = expr.arguments[1];
obj = fn = expr.arguments[2];
}
} else {
array = expr.arguments[0];
if(isCallable(expr.arguments[1])) {
// define([...], f() {})
fn = expr.arguments[1];
} else if(expr.arguments[1].type === "ObjectExpression") {
// define([...], {...})
obj = expr.arguments[1];
} else {
// define([...], expr)
// unclear if function or object
obj = fn = expr.arguments[1];
}
}
break;
case 3:
// define("...", [...], f() {...})
namedModule = expr.arguments[0].value;
array = expr.arguments[1];
if(isCallable(expr.arguments[2])) {
// define("...", [...], f() {})
fn = expr.arguments[2];
} else if(expr.arguments[2].type === "ObjectExpression") {
// define("...", [...], {...})
obj = expr.arguments[2];
} else {
// define("...", [...], expr)
// unclear if function or object
obj = fn = expr.arguments[2];
}
break;
default:
return;
}
let fnParams = null;
let fnParamsOffset = 0;
if(fn) {
if(isUnboundFunctionExpression(fn)) fnParams = fn.params;
else if(isBoundFunctionExpression(fn)) {
fnParams = fn.callee.object.params;
fnParamsOffset = fn.arguments.length - 1;
if(fnParamsOffset < 0) fnParamsOffset = 0;
break;
default:
return;
}
}
let fnRenames = parser.scope.renames.createChild();
let identifiers;
if(array) {
identifiers = {};
const param = parser.evaluateExpression(array);
const result = processArray(expr, param, identifiers, namedModule);
if(!result) return;
if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if(identifiers[idx]) {
fnRenames.set(param.name, identifiers[idx]);
return false;
let fnParams = null;
let fnParamsOffset = 0;
if (fn) {
if (isUnboundFunctionExpression(fn)) fnParams = fn.params;
else if (isBoundFunctionExpression(fn)) {
fnParams = fn.callee.object.params;
fnParamsOffset = fn.arguments.length - 1;
if (fnParamsOffset < 0) fnParamsOffset = 0;
}
return true;
});
} else {
identifiers = ["require", "exports", "module"];
if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if(identifiers[idx]) {
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;
});
}
let inTry;
if(fn && isUnboundFunctionExpression(fn)) {
inTry = parser.scope.inTry;
parser.inScope(fnParams, () => {
parser.scope.renames = fnRenames;
parser.scope.inTry = inTry;
if(fn.body.type === "BlockStatement")
parser.walkStatement(fn.body);
else
parser.walkExpression(fn.body);
});
} else if(fn && isBoundFunctionExpression(fn)) {
inTry = parser.scope.inTry;
parser.inScope(fn.callee.object.params.filter((i) => !["require", "module", "exports"].includes(i.name)), () => {
parser.scope.renames = fnRenames;
parser.scope.inTry = inTry;
if(fn.callee.object.body.type === "BlockStatement")
parser.walkStatement(fn.callee.object.body);
else
parser.walkExpression(fn.callee.object.body);
});
if(fn.arguments)
parser.walkExpressions(fn.arguments);
} else if(fn || obj) {
parser.walkExpression(fn || obj);
}
}
let fnRenames = parser.scope.renames.createChild();
let identifiers;
if (array) {
identifiers = {};
const param = parser.evaluateExpression(array);
const result = processArray(expr, param, identifiers, namedModule);
if (!result) return;
if (fnParams)
fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if (identifiers[idx]) {
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;
});
} else {
identifiers = ["require", "exports", "module"];
if (fnParams)
fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if (identifiers[idx]) {
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;
});
}
let inTry;
if (fn && isUnboundFunctionExpression(fn)) {
inTry = parser.scope.inTry;
parser.inScope(fnParams, () => {
parser.scope.renames = fnRenames;
parser.scope.inTry = inTry;
if (fn.body.type === "BlockStatement")
parser.walkStatement(fn.body);
else parser.walkExpression(fn.body);
});
} else if (fn && isBoundFunctionExpression(fn)) {
inTry = parser.scope.inTry;
parser.inScope(
fn.callee.object.params.filter(
i => !["require", "module", "exports"].includes(i.name)
),
() => {
parser.scope.renames = fnRenames;
parser.scope.inTry = inTry;
if (fn.callee.object.body.type === "BlockStatement")
parser.walkStatement(fn.callee.object.body);
else parser.walkExpression(fn.callee.object.body);
}
);
if (fn.arguments) parser.walkExpressions(fn.arguments);
} else if (fn || obj) {
parser.walkExpression(fn || obj);
}
const dep = this.newDefineDependency(
expr.range,
array ? array.range : null,
fn ? fn.range : null,
obj ? obj.range : null,
namedModule ? namedModule : null
);
dep.loc = expr.loc;
if(namedModule) {
dep.localModule = LocalModulesHelpers.addLocalModule(parser.state, namedModule);
}
parser.state.current.addDependency(dep);
return true;
});
const dep = this.newDefineDependency(
expr.range,
array ? array.range : null,
fn ? fn.range : null,
obj ? obj.range : null,
namedModule ? namedModule : null
);
dep.loc = expr.loc;
if (namedModule) {
dep.localModule = LocalModulesHelpers.addLocalModule(
parser.state,
namedModule
);
}
parser.state.current.addDependency(dep);
return true;
});
}
}
module.exports = AMDDefineDependencyParserPlugin;

View File

@ -31,90 +31,219 @@ class AMDPlugin {
apply(compiler) {
const options = this.options;
const amdOptions = this.amdOptions;
compiler.hooks.compilation.tap("AMDPlugin", (compilation, {
contextModuleFactory,
normalModuleFactory
}) => {
compilation.dependencyFactories.set(AMDRequireDependency, new NullFactory());
compilation.dependencyTemplates.set(AMDRequireDependency, new AMDRequireDependency.Template());
compiler.hooks.compilation.tap(
"AMDPlugin",
(compilation, { contextModuleFactory, normalModuleFactory }) => {
compilation.dependencyFactories.set(
AMDRequireDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
AMDRequireDependency,
new AMDRequireDependency.Template()
);
compilation.dependencyFactories.set(AMDRequireItemDependency, normalModuleFactory);
compilation.dependencyTemplates.set(AMDRequireItemDependency, new AMDRequireItemDependency.Template());
compilation.dependencyFactories.set(
AMDRequireItemDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
AMDRequireItemDependency,
new AMDRequireItemDependency.Template()
);
compilation.dependencyFactories.set(AMDRequireArrayDependency, new NullFactory());
compilation.dependencyTemplates.set(AMDRequireArrayDependency, new AMDRequireArrayDependency.Template());
compilation.dependencyFactories.set(
AMDRequireArrayDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
AMDRequireArrayDependency,
new AMDRequireArrayDependency.Template()
);
compilation.dependencyFactories.set(AMDRequireContextDependency, contextModuleFactory);
compilation.dependencyTemplates.set(AMDRequireContextDependency, new AMDRequireContextDependency.Template());
compilation.dependencyFactories.set(
AMDRequireContextDependency,
contextModuleFactory
);
compilation.dependencyTemplates.set(
AMDRequireContextDependency,
new AMDRequireContextDependency.Template()
);
compilation.dependencyFactories.set(AMDDefineDependency, new NullFactory());
compilation.dependencyTemplates.set(AMDDefineDependency, new AMDDefineDependency.Template());
compilation.dependencyFactories.set(
AMDDefineDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
AMDDefineDependency,
new AMDDefineDependency.Template()
);
compilation.dependencyFactories.set(UnsupportedDependency, new NullFactory());
compilation.dependencyTemplates.set(UnsupportedDependency, new UnsupportedDependency.Template());
compilation.dependencyFactories.set(
UnsupportedDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
UnsupportedDependency,
new UnsupportedDependency.Template()
);
compilation.dependencyFactories.set(LocalModuleDependency, new NullFactory());
compilation.dependencyTemplates.set(LocalModuleDependency, new LocalModuleDependency.Template());
compilation.dependencyFactories.set(
LocalModuleDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
LocalModuleDependency,
new LocalModuleDependency.Template()
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.amd !== "undefined" && !parserOptions.amd)
return;
const handler = (parser, parserOptions) => {
if (typeof parserOptions.amd !== "undefined" && !parserOptions.amd)
return;
const setExpressionToModule = (outerExpr, module) => {
parser.hooks.expression.for(outerExpr).tap("AMDPlugin", (expr) => {
const dep = new AMDRequireItemDependency(module, expr.range);
dep.userRequest = outerExpr;
const setExpressionToModule = (outerExpr, module) => {
parser.hooks.expression.for(outerExpr).tap("AMDPlugin", expr => {
const dep = new AMDRequireItemDependency(module, expr.range);
dep.userRequest = outerExpr;
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
});
};
new AMDRequireDependenciesBlockParserPlugin(options).apply(parser);
new AMDDefineDependencyParserPlugin(options).apply(parser);
setExpressionToModule("require.amd", "!!webpack amd options");
setExpressionToModule("define.amd", "!!webpack amd options");
setExpressionToModule("define", "!!webpack amd define");
parser.hooks.expression
.for("__webpack_amd_options__")
.tap("AMDPlugin", () =>
parser.state.current.addVariable(
"__webpack_amd_options__",
JSON.stringify(amdOptions)
)
);
parser.hooks.evaluateTypeof
.for("define.amd")
.tap(
"AMDPlugin",
ParserHelpers.evaluateToString(typeof amdOptions)
);
parser.hooks.evaluateTypeof
.for("require.amd")
.tap(
"AMDPlugin",
ParserHelpers.evaluateToString(typeof amdOptions)
);
parser.hooks.evaluateIdentifier
.for("define.amd")
.tap(
"AMDPlugin",
ParserHelpers.evaluateToIdentifier("define.amd", true)
);
parser.hooks.evaluateIdentifier
.for("require.amd")
.tap(
"AMDPlugin",
ParserHelpers.evaluateToIdentifier("require.amd", true)
);
parser.hooks.typeof
.for("define")
.tap(
"AMDPlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("function")
)
);
parser.hooks.evaluateTypeof
.for("define")
.tap("AMDPlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.canRename
.for("define")
.tap("AMDPlugin", ParserHelpers.approve);
parser.hooks.rename.for("define").tap("AMDPlugin", expr => {
const dep = new AMDRequireItemDependency(
"!!webpack amd define",
expr.range
);
dep.userRequest = "define";
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
return false;
});
parser.hooks.typeof
.for("require")
.tap(
"AMDPlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("function")
)
);
parser.hooks.evaluateTypeof
.for("require")
.tap("AMDPlugin", ParserHelpers.evaluateToString("function"));
};
new AMDRequireDependenciesBlockParserPlugin(options).apply(parser);
new AMDDefineDependencyParserPlugin(options).apply(parser);
setExpressionToModule("require.amd", "!!webpack amd options");
setExpressionToModule("define.amd", "!!webpack amd options");
setExpressionToModule("define", "!!webpack amd define");
parser.hooks.expression.for("__webpack_amd_options__").tap("AMDPlugin", () =>
parser.state.current.addVariable("__webpack_amd_options__", JSON.stringify(amdOptions)));
parser.hooks.evaluateTypeof.for("define.amd").tap("AMDPlugin", ParserHelpers.evaluateToString(typeof amdOptions));
parser.hooks.evaluateTypeof.for("require.amd").tap("AMDPlugin", ParserHelpers.evaluateToString(typeof amdOptions));
parser.hooks.evaluateIdentifier.for("define.amd").tap("AMDPlugin", ParserHelpers.evaluateToIdentifier("define.amd", true));
parser.hooks.evaluateIdentifier.for("require.amd").tap("AMDPlugin", ParserHelpers.evaluateToIdentifier("require.amd", true));
parser.hooks.typeof.for("define").tap("AMDPlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("function")));
parser.hooks.evaluateTypeof.for("define").tap("AMDPlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.canRename.for("define").tap("AMDPlugin", ParserHelpers.approve);
parser.hooks.rename.for("define").tap("AMDPlugin", (expr) => {
const dep = new AMDRequireItemDependency("!!webpack amd define", expr.range);
dep.userRequest = "define";
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return false;
});
parser.hooks.typeof.for("require").tap("AMDPlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("function")));
parser.hooks.evaluateTypeof.for("require").tap("AMDPlugin", ParserHelpers.evaluateToString("function"));
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("AMDPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("AMDPlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("AMDPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("AMDPlugin", handler);
}
);
compiler.hooks.afterResolvers.tap("AMDPlugin", () => {
compiler.resolverFactory.hooks.resolver.for("normal").tap("AMDPlugin", resolver => {
new AliasPlugin("described-resolve", {
name: "amdefine",
alias: path.join(__dirname, "..", "..", "buildin", "amd-define.js")
}, "resolve").apply(resolver);
new AliasPlugin("described-resolve", {
name: "webpack amd options",
alias: path.join(__dirname, "..", "..", "buildin", "amd-options.js")
}, "resolve").apply(resolver);
new AliasPlugin("described-resolve", {
name: "webpack amd define",
alias: path.join(__dirname, "..", "..", "buildin", "amd-define.js")
}, "resolve").apply(resolver);
});
compiler.resolverFactory.hooks.resolver
.for("normal")
.tap("AMDPlugin", resolver => {
new AliasPlugin(
"described-resolve",
{
name: "amdefine",
alias: path.join(
__dirname,
"..",
"..",
"buildin",
"amd-define.js"
)
},
"resolve"
).apply(resolver);
new AliasPlugin(
"described-resolve",
{
name: "webpack amd options",
alias: path.join(
__dirname,
"..",
"..",
"buildin",
"amd-options.js"
)
},
"resolve"
).apply(resolver);
new AliasPlugin(
"described-resolve",
{
name: "webpack amd define",
alias: path.join(
__dirname,
"..",
"..",
"buildin",
"amd-define.js"
)
},
"resolve"
).apply(resolver);
});
});
}
}

View File

@ -24,18 +24,18 @@ AMDRequireArrayDependency.Template = class AMDRequireArrayDependencyTemplate {
}
getContent(dep, runtime) {
const requires = dep.depsArray.map((dependency) => {
const requires = dep.depsArray.map(dependency => {
return this.contentForDependency(dependency, runtime);
});
return `[${requires.join(", ")}]`;
}
contentForDependency(dep, runtime) {
if(typeof dep === "string") {
if (typeof dep === "string") {
return dep;
}
if(dep.localModule) {
if (dep.localModule) {
return dep.localModule.variableName();
} else {
return runtime.moduleExports({

View File

@ -7,7 +7,15 @@ const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const AMDRequireDependency = require("./AMDRequireDependency");
module.exports = class AMDRequireDependenciesBlock extends AsyncDependenciesBlock {
constructor(expr, arrayRange, functionRange, errorCallbackRange, module, loc, request) {
constructor(
expr,
arrayRange,
functionRange,
errorCallbackRange,
module,
loc,
request
) {
super(null, module, loc, request);
this.expr = expr;
this.outerRange = expr.range;
@ -15,13 +23,13 @@ module.exports = class AMDRequireDependenciesBlock extends AsyncDependenciesBloc
this.functionRange = functionRange;
this.errorCallbackRange = errorCallbackRange;
this.bindThis = true;
if(arrayRange && functionRange && errorCallbackRange) {
if (arrayRange && functionRange && errorCallbackRange) {
this.range = [arrayRange[0], errorCallbackRange[1]];
} else if(arrayRange && functionRange) {
} else if (arrayRange && functionRange) {
this.range = [arrayRange[0], functionRange[1]];
} else if(arrayRange) {
} else if (arrayRange) {
this.range = arrayRange;
} else if(functionRange) {
} else if (functionRange) {
this.range = functionRange;
} else {
this.range = expr.range;

View File

@ -24,17 +24,19 @@ class AMDRequireDependenciesBlockParserPlugin {
processFunctionArgument(parser, expression) {
let bindThis = true;
const fnData = getFunctionExpression(expression);
if(fnData) {
parser.inScope(fnData.fn.params.filter((i) => {
return !["require", "module", "exports"].includes(i.name);
}), () => {
if(fnData.fn.body.type === "BlockStatement")
parser.walkStatement(fnData.fn.body);
else
parser.walkExpression(fnData.fn.body);
});
if (fnData) {
parser.inScope(
fnData.fn.params.filter(i => {
return !["require", "module", "exports"].includes(i.name);
}),
() => {
if (fnData.fn.body.type === "BlockStatement")
parser.walkStatement(fnData.fn.body);
else parser.walkExpression(fnData.fn.body);
}
);
parser.walkExpressions(fnData.expressions);
if(fnData.needThis === false) {
if (fnData.needThis === false) {
bindThis = false;
}
} else {
@ -47,43 +49,51 @@ class AMDRequireDependenciesBlockParserPlugin {
const options = this.options;
const processArrayForRequestString = param => {
if(param.isArray()) {
const result = param.items.map(item => processItemForRequestString(item));
if(result.every(Boolean))
return result.join(" ");
} else if(param.isConstArray()) {
if (param.isArray()) {
const result = param.items.map(item =>
processItemForRequestString(item)
);
if (result.every(Boolean)) return result.join(" ");
} else if (param.isConstArray()) {
return param.array.join(" ");
}
};
const processItemForRequestString = param => {
if(param.isConditional()) {
const result = param.options.map(item => processItemForRequestString(item));
if(result.every(Boolean))
return result.join("|");
} else if(param.isString()) {
if (param.isConditional()) {
const result = param.options.map(item =>
processItemForRequestString(item)
);
if (result.every(Boolean)) return result.join("|");
} else if (param.isString()) {
return param.string;
}
};
const processArray = (expr, param) => {
if(param.isArray()) {
for(const p of param.items) {
if (param.isArray()) {
for (const p of param.items) {
const result = processItem(expr, p);
if(result === undefined) {
if (result === undefined) {
processContext(expr, p);
}
}
return true;
} else if(param.isConstArray()) {
} else if (param.isConstArray()) {
const deps = [];
for(const request of param.array) {
for (const request of param.array) {
let dep, localModule;
if(request === "require") {
if (request === "require") {
dep = "__webpack_require__";
} else if(["exports", "module"].includes(request)) {
} else if (["exports", "module"].includes(request)) {
dep = request;
} else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, request)) { // eslint-disable-line no-cond-assign
} else if (
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
request
))
) {
// eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
@ -103,23 +113,35 @@ class AMDRequireDependenciesBlockParserPlugin {
}
};
const processItem = (expr, param) => {
if(param.isConditional()) {
for(const p of param.options) {
if (param.isConditional()) {
for (const p of param.options) {
const result = processItem(expr, p);
if(result === undefined) {
if (result === undefined) {
processContext(expr, p);
}
}
return true;
} else if(param.isString()) {
} else if (param.isString()) {
let dep, localModule;
if(param.string === "require") {
if (param.string === "require") {
dep = new ConstDependency("__webpack_require__", param.string);
} else if(param.string === "module") {
dep = new ConstDependency(parser.state.module.buildInfo.moduleArgument, param.range);
} else if(param.string === "exports") {
dep = new ConstDependency(parser.state.module.buildInfo.exportsArgument, param.range);
} else if(localModule = LocalModulesHelpers.getLocalModule(parser.state, param.string)) { // eslint-disable-line no-cond-assign
} else if (param.string === "module") {
dep = new ConstDependency(
parser.state.module.buildInfo.moduleArgument,
param.range
);
} else if (param.string === "exports") {
dep = new ConstDependency(
parser.state.module.buildInfo.exportsArgument,
param.range
);
} else if (
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
param.string
))
) {
// eslint-disable-line no-cond-assign
dep = new LocalModuleDependency(localModule, param.range);
} else {
dep = new AMDRequireItemDependency(param.string, param.range);
@ -131,70 +153,89 @@ class AMDRequireDependenciesBlockParserPlugin {
}
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(AMDRequireContextDependency, param.range, param, expr, options);
if(!dep) return;
const dep = ContextDependencyHelpers.create(
AMDRequireContextDependency,
param.range,
param,
expr,
options
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.call.for("require").tap("AMDRequireDependenciesBlockParserPlugin", (expr) => {
let param;
let dep;
let result;
parser.hooks.call
.for("require")
.tap("AMDRequireDependenciesBlockParserPlugin", expr => {
let param;
let dep;
let result;
const old = parser.state.current;
const old = parser.state.current;
if(expr.arguments.length >= 1) {
param = parser.evaluateExpression(expr.arguments[0]);
dep = new AMDRequireDependenciesBlock(
expr,
param.range,
(expr.arguments.length > 1) ? expr.arguments[1].range : null,
(expr.arguments.length > 2) ? expr.arguments[2].range : null,
parser.state.module,
expr.loc,
processArrayForRequestString(param)
);
parser.state.current = dep;
}
if (expr.arguments.length >= 1) {
param = parser.evaluateExpression(expr.arguments[0]);
dep = new AMDRequireDependenciesBlock(
expr,
param.range,
expr.arguments.length > 1 ? expr.arguments[1].range : null,
expr.arguments.length > 2 ? expr.arguments[2].range : null,
parser.state.module,
expr.loc,
processArrayForRequestString(param)
);
parser.state.current = dep;
}
if(expr.arguments.length === 1) {
parser.inScope([], () => {
result = processArray(expr, param);
});
parser.state.current = old;
if(!result) return;
parser.state.current.addBlock(dep);
return true;
}
if(expr.arguments.length === 2 || expr.arguments.length === 3) {
try {
if (expr.arguments.length === 1) {
parser.inScope([], () => {
result = processArray(expr, param);
});
if(!result) {
dep = new UnsupportedDependency("unsupported", expr.range);
old.addDependency(dep);
if(parser.state.module)
parser.state.module.errors.push(new UnsupportedFeatureWarning(parser.state.module, "Cannot statically analyse 'require(..., ...)' in line " + expr.loc.start.line));
dep = null;
return true;
}
dep.functionBindThis = this.processFunctionArgument(parser, expr.arguments[1]);
if(expr.arguments.length === 3) {
dep.errorCallbackBindThis = this.processFunctionArgument(parser, expr.arguments[2]);
}
} finally {
parser.state.current = old;
if(dep)
parser.state.current.addBlock(dep);
if (!result) return;
parser.state.current.addBlock(dep);
return true;
}
return true;
}
});
if (expr.arguments.length === 2 || expr.arguments.length === 3) {
try {
parser.inScope([], () => {
result = processArray(expr, param);
});
if (!result) {
dep = new UnsupportedDependency("unsupported", expr.range);
old.addDependency(dep);
if (parser.state.module)
parser.state.module.errors.push(
new UnsupportedFeatureWarning(
parser.state.module,
"Cannot statically analyse 'require(..., ...)' in line " +
expr.loc.start.line
)
);
dep = null;
return true;
}
dep.functionBindThis = this.processFunctionArgument(
parser,
expr.arguments[1]
);
if (expr.arguments.length === 3) {
dep.errorCallbackBindThis = this.processFunctionArgument(
parser,
expr.arguments[2]
);
}
} finally {
parser.state.current = old;
if (dep) parser.state.current.addBlock(dep);
}
return true;
}
});
}
}
module.exports = AMDRequireDependenciesBlockParserPlugin;

View File

@ -21,47 +21,113 @@ AMDRequireDependency.Template = class AMDRequireDependencyTemplate {
});
// has array range but no function range
if(depBlock.arrayRange && !depBlock.functionRange) {
if (depBlock.arrayRange && !depBlock.functionRange) {
const startBlock = `${promise}.then(function() {`;
const endBlock = `;}).catch(${runtime.onError()})`;
source.replace(depBlock.outerRange[0], depBlock.arrayRange[0] - 1, startBlock);
source.replace(depBlock.arrayRange[1], depBlock.outerRange[1] - 1, endBlock);
source.replace(
depBlock.outerRange[0],
depBlock.arrayRange[0] - 1,
startBlock
);
source.replace(
depBlock.arrayRange[1],
depBlock.outerRange[1] - 1,
endBlock
);
return;
}
// has function range but no array range
if(depBlock.functionRange && !depBlock.arrayRange) {
if (depBlock.functionRange && !depBlock.arrayRange) {
const startBlock = `${promise}.then((`;
const endBlock = `).bind(exports, __webpack_require__, exports, module)).catch(${runtime.onError()})`;
source.replace(depBlock.outerRange[0], depBlock.functionRange[0] - 1, startBlock);
source.replace(depBlock.functionRange[1], depBlock.outerRange[1] - 1, endBlock);
source.replace(
depBlock.outerRange[0],
depBlock.functionRange[0] - 1,
startBlock
);
source.replace(
depBlock.functionRange[1],
depBlock.outerRange[1] - 1,
endBlock
);
return;
}
// has array range, function range, and errorCallbackRange
if(depBlock.arrayRange && depBlock.functionRange && depBlock.errorCallbackRange) {
if (
depBlock.arrayRange &&
depBlock.functionRange &&
depBlock.errorCallbackRange
) {
const startBlock = `${promise}.then(function() { `;
const errorRangeBlock = `}${depBlock.functionBindThis ? ".bind(this)" : ""}).catch(`;
const endBlock = `${depBlock.errorCallbackBindThis ? ".bind(this)" : ""})`;
const errorRangeBlock = `}${
depBlock.functionBindThis ? ".bind(this)" : ""
}).catch(`;
const endBlock = `${
depBlock.errorCallbackBindThis ? ".bind(this)" : ""
})`;
source.replace(depBlock.outerRange[0], depBlock.arrayRange[0] - 1, startBlock);
source.insert(depBlock.arrayRange[0] + 0.9, "var __WEBPACK_AMD_REQUIRE_ARRAY__ = ");
source.replace(depBlock.arrayRange[1], depBlock.functionRange[0] - 1, "; (");
source.insert(depBlock.functionRange[1], ").apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);");
source.replace(depBlock.functionRange[1], depBlock.errorCallbackRange[0] - 1, errorRangeBlock);
source.replace(depBlock.errorCallbackRange[1], depBlock.outerRange[1] - 1, endBlock);
source.replace(
depBlock.outerRange[0],
depBlock.arrayRange[0] - 1,
startBlock
);
source.insert(
depBlock.arrayRange[0] + 0.9,
"var __WEBPACK_AMD_REQUIRE_ARRAY__ = "
);
source.replace(
depBlock.arrayRange[1],
depBlock.functionRange[0] - 1,
"; ("
);
source.insert(
depBlock.functionRange[1],
").apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);"
);
source.replace(
depBlock.functionRange[1],
depBlock.errorCallbackRange[0] - 1,
errorRangeBlock
);
source.replace(
depBlock.errorCallbackRange[1],
depBlock.outerRange[1] - 1,
endBlock
);
return;
}
// has array range, function range, but no errorCallbackRange
if(depBlock.arrayRange && depBlock.functionRange) {
if (depBlock.arrayRange && depBlock.functionRange) {
const startBlock = `${promise}.then(function() { `;
const endBlock = `}${depBlock.functionBindThis ? ".bind(this)" : ""}).catch(${runtime.onError()})`;
source.replace(depBlock.outerRange[0], depBlock.arrayRange[0] - 1, startBlock);
source.insert(depBlock.arrayRange[0] + 0.9, "var __WEBPACK_AMD_REQUIRE_ARRAY__ = ");
source.replace(depBlock.arrayRange[1], depBlock.functionRange[0] - 1, "; (");
source.insert(depBlock.functionRange[1], ").apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);");
source.replace(depBlock.functionRange[1], depBlock.outerRange[1] - 1, endBlock);
const endBlock = `}${
depBlock.functionBindThis ? ".bind(this)" : ""
}).catch(${runtime.onError()})`;
source.replace(
depBlock.outerRange[0],
depBlock.arrayRange[0] - 1,
startBlock
);
source.insert(
depBlock.arrayRange[0] + 0.9,
"var __WEBPACK_AMD_REQUIRE_ARRAY__ = "
);
source.replace(
depBlock.arrayRange[1],
depBlock.functionRange[0] - 1,
"; ("
);
source.insert(
depBlock.functionRange[1],
").apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);"
);
source.replace(
depBlock.functionRange[1],
depBlock.outerRange[1] - 1,
endBlock
);
}
}
};

View File

@ -25,66 +25,137 @@ class CommonJsPlugin {
apply(compiler) {
const options = this.options;
compiler.hooks.compilation.tap("CommonJsPlugin", (compilation, {
contextModuleFactory,
normalModuleFactory
}) => {
compilation.dependencyFactories.set(CommonJsRequireDependency, normalModuleFactory);
compilation.dependencyTemplates.set(CommonJsRequireDependency, new CommonJsRequireDependency.Template());
compiler.hooks.compilation.tap(
"CommonJsPlugin",
(compilation, { contextModuleFactory, normalModuleFactory }) => {
compilation.dependencyFactories.set(
CommonJsRequireDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CommonJsRequireDependency,
new CommonJsRequireDependency.Template()
);
compilation.dependencyFactories.set(CommonJsRequireContextDependency, contextModuleFactory);
compilation.dependencyTemplates.set(CommonJsRequireContextDependency, new CommonJsRequireContextDependency.Template());
compilation.dependencyFactories.set(
CommonJsRequireContextDependency,
contextModuleFactory
);
compilation.dependencyTemplates.set(
CommonJsRequireContextDependency,
new CommonJsRequireContextDependency.Template()
);
compilation.dependencyFactories.set(RequireResolveDependency, normalModuleFactory);
compilation.dependencyTemplates.set(RequireResolveDependency, new RequireResolveDependency.Template());
compilation.dependencyFactories.set(
RequireResolveDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
RequireResolveDependency,
new RequireResolveDependency.Template()
);
compilation.dependencyFactories.set(RequireResolveContextDependency, contextModuleFactory);
compilation.dependencyTemplates.set(RequireResolveContextDependency, new RequireResolveContextDependency.Template());
compilation.dependencyFactories.set(
RequireResolveContextDependency,
contextModuleFactory
);
compilation.dependencyTemplates.set(
RequireResolveContextDependency,
new RequireResolveContextDependency.Template()
);
compilation.dependencyFactories.set(RequireResolveHeaderDependency, new NullFactory());
compilation.dependencyTemplates.set(RequireResolveHeaderDependency, new RequireResolveHeaderDependency.Template());
compilation.dependencyFactories.set(
RequireResolveHeaderDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
RequireResolveHeaderDependency,
new RequireResolveHeaderDependency.Template()
);
compilation.dependencyFactories.set(RequireHeaderDependency, new NullFactory());
compilation.dependencyTemplates.set(RequireHeaderDependency, new RequireHeaderDependency.Template());
compilation.dependencyFactories.set(
RequireHeaderDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
RequireHeaderDependency,
new RequireHeaderDependency.Template()
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.commonjs !== "undefined" && !parserOptions.commonjs)
return;
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.commonjs !== "undefined" &&
!parserOptions.commonjs
)
return;
const requireExpressions = ["require", "require.resolve", "require.resolveWeak"];
for(let expression of requireExpressions) {
parser.hooks.typeof.for(expression).tap("CommonJsPlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("function")));
parser.hooks.evaluateTypeof.for(expression).tap("CommonJsPlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.evaluateIdentifier.for(expression).tap("CommonJsPlugin", ParserHelpers.evaluateToIdentifier(expression, true));
}
const requireExpressions = [
"require",
"require.resolve",
"require.resolveWeak"
];
for (let expression of requireExpressions) {
parser.hooks.typeof
.for(expression)
.tap(
"CommonJsPlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("function")
)
);
parser.hooks.evaluateTypeof
.for(expression)
.tap(
"CommonJsPlugin",
ParserHelpers.evaluateToString("function")
);
parser.hooks.evaluateIdentifier
.for(expression)
.tap(
"CommonJsPlugin",
ParserHelpers.evaluateToIdentifier(expression, true)
);
}
parser.hooks.evaluateTypeof.for("module").tap("CommonJsPlugin", ParserHelpers.evaluateToString("object"));
parser.hooks.assign.for("require").tap("CommonJsPlugin", (expr) => {
// to not leak to global "require", we need to define a local require here.
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.scope.definitions.add("require");
return true;
});
parser.hooks.canRename.for("require").tap("CommonJsPlugin", () => true);
parser.hooks.rename.for("require").tap("CommonJsPlugin", (expr) => {
// define the require variable. It's still undefined, but not "not defined".
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return false;
});
parser.hooks.typeof.for("module").tap("CommonJsPlugin", () => true);
parser.hooks.evaluateTypeof.for("exports").tap("CommonJsPlugin", ParserHelpers.evaluateToString("object"));
parser.hooks.evaluateTypeof
.for("module")
.tap("CommonJsPlugin", ParserHelpers.evaluateToString("object"));
parser.hooks.assign.for("require").tap("CommonJsPlugin", expr => {
// to not leak to global "require", we need to define a local require here.
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.scope.definitions.add("require");
return true;
});
parser.hooks.canRename
.for("require")
.tap("CommonJsPlugin", () => true);
parser.hooks.rename.for("require").tap("CommonJsPlugin", expr => {
// define the require variable. It's still undefined, but not "not defined".
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return false;
});
parser.hooks.typeof.for("module").tap("CommonJsPlugin", () => true);
parser.hooks.evaluateTypeof
.for("exports")
.tap("CommonJsPlugin", ParserHelpers.evaluateToString("object"));
new CommonJsRequireDependencyParserPlugin(options).apply(parser);
new RequireResolveDependencyParserPlugin(options).apply(parser);
};
new CommonJsRequireDependencyParserPlugin(options).apply(parser);
new RequireResolveDependencyParserPlugin(options).apply(parser);
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("CommonJsPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("CommonJsPlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("CommonJsPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("CommonJsPlugin", handler);
}
);
}
}
module.exports = CommonJsPlugin;

View File

@ -16,7 +16,6 @@ class CommonJsRequireContextDependency extends ContextDependency {
get type() {
return "cjs require context";
}
}
CommonJsRequireContextDependency.Template = ContextDependencyTemplateAsRequireCall;

View File

@ -21,7 +21,7 @@ class CommonJsRequireDependencyParserPlugin {
const options = this.options;
const processItem = (expr, param) => {
if(param.isString()) {
if (param.isString()) {
const dep = new CommonJsRequireDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
@ -30,67 +30,96 @@ class CommonJsRequireDependencyParserPlugin {
}
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(CommonJsRequireContextDependency, expr.range, param, expr, options);
if(!dep) return;
const dep = ContextDependencyHelpers.create(
CommonJsRequireContextDependency,
expr.range,
param,
expr,
options
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.expression.for("require.cache").tap("CommonJsRequireDependencyParserPlugin", ParserHelpers.toConstantDependencyWithWebpackRequire(parser, "__webpack_require__.c"));
parser.hooks.expression.for("require").tap("CommonJsRequireDependencyParserPlugin", (expr) => {
const dep = new CommonJsRequireContextDependency({
request: options.unknownContextRequest,
recursive: options.unknownContextRecursive,
regExp: options.unknownContextRegExp,
mode: "sync"
}, expr.range);
dep.critical = options.unknownContextCritical && "require function is used in a way in which dependencies cannot be statically extracted";
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
});
parser.hooks.call.for("require").tap("CommonJsRequireDependencyParserPlugin", (expr) => {
if(expr.arguments.length !== 1) return;
let localModule;
const param = parser.evaluateExpression(expr.arguments[0]);
if(param.isConditional()) {
let isExpression = false;
const prevLength = parser.state.current.dependencies.length;
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
for(const p of param.options) {
const result = processItem(expr, p);
if(result === undefined) {
isExpression = true;
}
}
if(isExpression) {
parser.state.current.dependencies.length = prevLength;
} else {
return true;
}
}
if(param.isString() && (localModule = LocalModulesHelpers.getLocalModule(parser.state, param.string))) {
const dep = new LocalModuleDependency(localModule, expr.range);
parser.hooks.expression
.for("require.cache")
.tap(
"CommonJsRequireDependencyParserPlugin",
ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
"__webpack_require__.c"
)
);
parser.hooks.expression
.for("require")
.tap("CommonJsRequireDependencyParserPlugin", expr => {
const dep = new CommonJsRequireContextDependency(
{
request: options.unknownContextRequest,
recursive: options.unknownContextRecursive,
regExp: options.unknownContextRegExp,
mode: "sync"
},
expr.range
);
dep.critical =
options.unknownContextCritical &&
"require function is used in a way in which dependencies cannot be statically extracted";
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
} else {
const result = processItem(expr, param);
if(result === undefined) {
processContext(expr, param);
} else {
});
parser.hooks.call
.for("require")
.tap("CommonJsRequireDependencyParserPlugin", expr => {
if (expr.arguments.length !== 1) return;
let localModule;
const param = parser.evaluateExpression(expr.arguments[0]);
if (param.isConditional()) {
let isExpression = false;
const prevLength = parser.state.current.dependencies.length;
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
for (const p of param.options) {
const result = processItem(expr, p);
if (result === undefined) {
isExpression = true;
}
}
if (isExpression) {
parser.state.current.dependencies.length = prevLength;
} else {
return true;
}
}
return true;
}
});
if (
param.isString() &&
(localModule = LocalModulesHelpers.getLocalModule(
parser.state,
param.string
))
) {
const dep = new LocalModuleDependency(localModule, expr.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
} else {
const result = processItem(expr, param);
if (result === undefined) {
processContext(expr, param);
} else {
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
}
return true;
}
});
}
}
module.exports = CommonJsRequireDependencyParserPlugin;

View File

@ -21,7 +21,7 @@ class ConstDependency extends NullDependency {
ConstDependency.Template = class ConstDependencyTemplate {
apply(dep, source) {
if(typeof dep.range === "number") {
if (typeof dep.range === "number") {
source.insert(dep.range, dep.expression);
return;
}

View File

@ -6,7 +6,7 @@
const Dependency = require("../Dependency");
const CriticalDependencyWarning = require("./CriticalDependencyWarning");
const regExpToString = r => r ? r + "" : "";
const regExpToString = r => (r ? r + "" : "");
class ContextDependency extends Dependency {
// options: { request, recursive, regExp, include, exclude, mode, chunkName }
@ -15,25 +15,33 @@ class ContextDependency extends Dependency {
this.options = options;
this.userRequest = this.options.request;
this.hadGlobalOrStickyRegExp = false;
if(this.options.regExp.global || this.options.regExp.sticky) {
if (this.options.regExp.global || this.options.regExp.sticky) {
this.options.regExp = null;
this.hadGlobalOrStickyRegExp = true;
}
}
getResourceIdentifier() {
return `context${this.options.request} ${this.options.recursive} ` +
`${regExpToString(this.options.regExp)} ${regExpToString(this.options.include)} ${regExpToString(this.options.exclude)} ` +
`${this.options.mode} ${this.options.chunkName}`;
return (
`context${this.options.request} ${this.options.recursive} ` +
`${regExpToString(this.options.regExp)} ${regExpToString(
this.options.include
)} ${regExpToString(this.options.exclude)} ` +
`${this.options.mode} ${this.options.chunkName}`
);
}
getWarnings() {
let warnings = super.getWarnings() || [];
if(this.critical) {
if (this.critical) {
warnings.push(new CriticalDependencyWarning(this.critical));
}
if(this.hadGlobalOrStickyRegExp) {
warnings.push(new CriticalDependencyWarning("Contexts can't use RegExps with the 'g' or 'y' flags."));
if (this.hadGlobalOrStickyRegExp) {
warnings.push(
new CriticalDependencyWarning(
"Contexts can't use RegExps with the 'g' or 'y' flags."
)
);
}
return warnings;
}
@ -42,10 +50,14 @@ class ContextDependency extends Dependency {
Object.defineProperty(ContextDependency.prototype, "async", {
configurable: false,
get() {
throw new Error("ContextDependency.async was removed. Use ContextDependency.options.mode instead.");
throw new Error(
"ContextDependency.async was removed. Use ContextDependency.options.mode instead."
);
},
set() {
throw new Error("ContextDependency.async was removed. Pass options.mode to constructor instead");
throw new Error(
"ContextDependency.async was removed. Pass options.mode to constructor instead"
);
}
});

View File

@ -15,7 +15,14 @@ const quotemeta = str => {
return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");
};
ContextDependencyHelpers.create = (Dep, range, param, expr, options, contextOptions) => {
ContextDependencyHelpers.create = (
Dep,
range,
param,
expr,
options,
contextOptions
) => {
let dep;
let prefix;
let postfix;
@ -24,63 +31,112 @@ ContextDependencyHelpers.create = (Dep, range, param, expr, options, contextOpti
let idx;
let context;
let regExp;
if(param.isTemplateString()) {
if (param.isTemplateString()) {
prefix = param.quasis[0].string;
postfix = param.quasis.length > 1 ? param.quasis[param.quasis.length - 1].string : "";
postfix =
param.quasis.length > 1
? param.quasis[param.quasis.length - 1].string
: "";
prefixRange = [param.quasis[0].range[0], param.quasis[0].range[1]];
valueRange = param.range;
idx = prefix.lastIndexOf("/");
context = ".";
if(idx >= 0) {
if (idx >= 0) {
context = prefix.substr(0, idx);
prefix = `.${prefix.substr(idx)}`;
}
// If there are more than two quasis, maybe the generated RegExp can be more precise?
regExp = new RegExp(`^${quotemeta(prefix)}${options.wrappedContextRegExp.source}${quotemeta(postfix)}$`);
dep = new Dep(Object.assign({
request: context,
recursive: options.wrappedContextRecursive,
regExp,
mode: "sync"
}, contextOptions), range, valueRange);
regExp = new RegExp(
`^${quotemeta(prefix)}${options.wrappedContextRegExp.source}${quotemeta(
postfix
)}$`
);
dep = new Dep(
Object.assign(
{
request: context,
recursive: options.wrappedContextRecursive,
regExp,
mode: "sync"
},
contextOptions
),
range,
valueRange
);
dep.loc = expr.loc;
dep.replaces = [{
range: prefixRange,
value: prefix
}];
dep.critical = options.wrappedContextCritical && "a part of the request of a dependency is an expression";
dep.replaces = [
{
range: prefixRange,
value: prefix
}
];
dep.critical =
options.wrappedContextCritical &&
"a part of the request of a dependency is an expression";
return dep;
} else if(param.isWrapped() && (param.prefix && param.prefix.isString() || param.postfix && param.postfix.isString())) {
} else if (
param.isWrapped() &&
((param.prefix && param.prefix.isString()) ||
(param.postfix && param.postfix.isString()))
) {
prefix = param.prefix && param.prefix.isString() ? param.prefix.string : "";
postfix = param.postfix && param.postfix.isString() ? param.postfix.string : "";
prefixRange = param.prefix && param.prefix.isString() ? param.prefix.range : null;
valueRange = [prefixRange ? prefixRange[1] : param.range[0], param.range[1]];
postfix =
param.postfix && param.postfix.isString() ? param.postfix.string : "";
prefixRange =
param.prefix && param.prefix.isString() ? param.prefix.range : null;
valueRange = [
prefixRange ? prefixRange[1] : param.range[0],
param.range[1]
];
idx = prefix.lastIndexOf("/");
context = ".";
if(idx >= 0) {
if (idx >= 0) {
context = prefix.substr(0, idx);
prefix = `.${prefix.substr(idx)}`;
}
regExp = new RegExp(`^${quotemeta(prefix)}${options.wrappedContextRegExp.source}${quotemeta(postfix)}$`);
dep = new Dep(Object.assign({
request: context,
recursive: options.wrappedContextRecursive,
regExp,
mode: "sync"
}, contextOptions), range, valueRange);
regExp = new RegExp(
`^${quotemeta(prefix)}${options.wrappedContextRegExp.source}${quotemeta(
postfix
)}$`
);
dep = new Dep(
Object.assign(
{
request: context,
recursive: options.wrappedContextRecursive,
regExp,
mode: "sync"
},
contextOptions
),
range,
valueRange
);
dep.loc = expr.loc;
dep.prepend = param.prefix && param.prefix.isString() ? prefix : null;
dep.critical = options.wrappedContextCritical && "a part of the request of a dependency is an expression";
dep.critical =
options.wrappedContextCritical &&
"a part of the request of a dependency is an expression";
return dep;
} else {
dep = new Dep(Object.assign({
request: options.exprContextRequest,
recursive: options.exprContextRecursive,
regExp: options.exprContextRegExp,
mode: "sync"
}, contextOptions), range, param.range);
dep = new Dep(
Object.assign(
{
request: options.exprContextRequest,
recursive: options.exprContextRecursive,
regExp: options.exprContextRegExp,
mode: "sync"
},
contextOptions
),
range,
param.range
);
dep.loc = expr.loc;
dep.critical = options.exprContextCritical && "the request of a dependency is an expression";
dep.critical =
options.exprContextCritical &&
"the request of a dependency is an expression";
return dep;
}
};

View File

@ -5,25 +5,34 @@
"use strict";
class ContextDependencyTemplateAsId {
apply(dep, source, runtime) {
const moduleExports = runtime.moduleExports({
module: dep.module,
request: dep.request
});
if(dep.module) {
if(dep.valueRange) {
if(Array.isArray(dep.replaces)) {
for(let i = 0; i < dep.replaces.length; i++) {
if (dep.module) {
if (dep.valueRange) {
if (Array.isArray(dep.replaces)) {
for (let i = 0; i < dep.replaces.length; i++) {
const rep = dep.replaces[i];
source.replace(rep.range[0], rep.range[1] - 1, rep.value);
}
}
source.replace(dep.valueRange[1], dep.range[1] - 1, ")");
source.replace(dep.range[0], dep.valueRange[0] - 1, `${moduleExports}.resolve(${typeof dep.prepend === "string" ? JSON.stringify(dep.prepend) : ""}`);
source.replace(
dep.range[0],
dep.valueRange[0] - 1,
`${moduleExports}.resolve(${
typeof dep.prepend === "string" ? JSON.stringify(dep.prepend) : ""
}`
);
} else {
source.replace(dep.range[0], dep.range[1] - 1, `${moduleExports}.resolve`);
source.replace(
dep.range[0],
dep.range[1] - 1,
`${moduleExports}.resolve`
);
}
} else {
source.replace(dep.range[0], dep.range[1] - 1, moduleExports);

View File

@ -5,23 +5,28 @@
"use strict";
class ContextDependencyTemplateAsRequireCall {
apply(dep, source, runtime) {
const moduleExports = runtime.moduleExports({
module: dep.module,
request: dep.request
});
if(dep.module) {
if(dep.valueRange) {
if(Array.isArray(dep.replaces)) {
for(let i = 0; i < dep.replaces.length; i++) {
if (dep.module) {
if (dep.valueRange) {
if (Array.isArray(dep.replaces)) {
for (let i = 0; i < dep.replaces.length; i++) {
const rep = dep.replaces[i];
source.replace(rep.range[0], rep.range[1] - 1, rep.value);
}
}
source.replace(dep.valueRange[1], dep.range[1] - 1, ")");
source.replace(dep.range[0], dep.valueRange[0] - 1, `${moduleExports}(${typeof dep.prepend === "string" ? JSON.stringify(dep.prepend) : ""}`);
source.replace(
dep.range[0],
dep.valueRange[0] - 1,
`${moduleExports}(${
typeof dep.prepend === "string" ? JSON.stringify(dep.prepend) : ""
}`
);
} else {
source.replace(dep.range[0], dep.range[1] - 1, moduleExports);
}

View File

@ -8,7 +8,7 @@ const ModuleDependency = require("./ModuleDependency");
class ContextElementDependency extends ModuleDependency {
constructor(request, userRequest) {
super(request);
if(userRequest) {
if (userRequest) {
this.userRequest = userRequest;
}
}

View File

@ -23,12 +23,17 @@ class HarmonyAcceptDependency extends NullDependency {
HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate {
apply(dep, source, runtime) {
const content = dep.dependencies
.filter(dependency => HarmonyImportDependency.Template.isImportEmitted(dependency, source))
.filter(dependency =>
HarmonyImportDependency.Template.isImportEmitted(dependency, source)
)
.map(dependency => dependency.getImportStatement(true, runtime))
.join("");
if(dep.hasCallback) {
source.insert(dep.range[0], `function(__WEBPACK_OUTDATED_DEPENDENCIES__) { ${content}(`);
if (dep.hasCallback) {
source.insert(
dep.range[0],
`function(__WEBPACK_OUTDATED_DEPENDENCIES__) { ${content}(`
);
source.insert(dep.range[1], ")(__WEBPACK_OUTDATED_DEPENDENCIES__); }");
return;
}

View File

@ -19,7 +19,7 @@ class HarmonyCompatibilityDependency extends NullDependency {
HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate {
apply(dep, source, runtime) {
const usedExports = dep.originModule.usedExports;
if(usedExports !== false && !Array.isArray(usedExports)) {
if (usedExports !== false && !Array.isArray(usedExports)) {
const content = runtime.defineEsModuleFlagStatement({
exportsArgument: dep.originModule.exportsArgument
});

View File

@ -9,12 +9,14 @@ const HarmonyInitDependency = require("./HarmonyInitDependency");
module.exports = class HarmonyDetectionParserPlugin {
apply(parser) {
parser.hooks.program.tap("HarmonyDetectionParserPlugin", (ast) => {
parser.hooks.program.tap("HarmonyDetectionParserPlugin", ast => {
const isStrictHarmony = parser.state.module.type === "javascript/esm";
const isHarmony = isStrictHarmony || ast.body.some(statement => {
return /^(Import|Export).*Declaration$/.test(statement.type);
});
if(isHarmony) {
const isHarmony =
isStrictHarmony ||
ast.body.some(statement => {
return /^(Import|Export).*Declaration$/.test(statement.type);
});
if (isHarmony) {
const module = parser.state.module;
const compatDep = new HarmonyCompatibilityDependency(module);
compatDep.loc = {
@ -47,7 +49,7 @@ module.exports = class HarmonyDetectionParserPlugin {
module.buildMeta.exportsType = "namespace";
module.buildInfo.strict = true;
module.buildInfo.exportsArgument = "__webpack_exports__";
if(isStrictHarmony) {
if (isStrictHarmony) {
module.buildMeta.strictHarmonyModule = true;
module.buildInfo.moduleArgument = "__webpack_module__";
}
@ -56,23 +58,33 @@ module.exports = class HarmonyDetectionParserPlugin {
const skipInHarmony = () => {
const module = parser.state.module;
if(module && module.buildMeta && module.buildMeta.exportsType)
if (module && module.buildMeta && module.buildMeta.exportsType)
return true;
};
const nullInHarmony = () => {
const module = parser.state.module;
if(module && module.buildMeta && module.buildMeta.exportsType)
if (module && module.buildMeta && module.buildMeta.exportsType)
return null;
};
const nonHarmonyIdentifiers = ["define", "exports"];
for(const identifer of nonHarmonyIdentifiers) {
parser.hooks.evaluateTypeof.for(identifer).tap("HarmonyDetectionParserPlugin", nullInHarmony);
parser.hooks.typeof.for(identifer).tap("HarmonyDetectionParserPlugin", skipInHarmony);
parser.hooks.evaluate.for(identifer).tap("HarmonyDetectionParserPlugin", nullInHarmony);
parser.hooks.expression.for(identifer).tap("HarmonyDetectionParserPlugin", skipInHarmony);
parser.hooks.call.for(identifer).tap("HarmonyDetectionParserPlugin", skipInHarmony);
for (const identifer of nonHarmonyIdentifiers) {
parser.hooks.evaluateTypeof
.for(identifer)
.tap("HarmonyDetectionParserPlugin", nullInHarmony);
parser.hooks.typeof
.for(identifer)
.tap("HarmonyDetectionParserPlugin", skipInHarmony);
parser.hooks.evaluate
.for(identifer)
.tap("HarmonyDetectionParserPlugin", nullInHarmony);
parser.hooks.expression
.for(identifer)
.tap("HarmonyDetectionParserPlugin", skipInHarmony);
parser.hooks.call
.for(identifer)
.tap("HarmonyDetectionParserPlugin", skipInHarmony);
}
}
};

View File

@ -17,65 +17,123 @@ module.exports = class HarmonyExportDependencyParserPlugin {
}
apply(parser) {
parser.hooks.export.tap("HarmonyExportDependencyParserPlugin", statement => {
const dep = new HarmonyExportHeaderDependency(statement.declaration && statement.declaration.range, statement.range);
dep.loc = Object.create(statement.loc);
dep.loc.index = -1;
parser.state.current.addDependency(dep);
return true;
});
parser.hooks.exportImport.tap("HarmonyExportDependencyParserPlugin", (statement, source) => {
parser.state.lastHarmonyImportOrder = (parser.state.lastHarmonyImportOrder || 0) + 1;
const clearDep = new ConstDependency("", statement.range);
clearDep.loc = Object.create(statement.loc);
clearDep.loc.index = -1;
parser.state.current.addDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope);
sideEffectDep.loc = Object.create(statement.loc);
sideEffectDep.loc.index = -1;
parser.state.current.addDependency(sideEffectDep);
return true;
});
parser.hooks.exportExpression.tap("HarmonyExportDependencyParserPlugin", (statement, expr) => {
const dep = new HarmonyExportExpressionDependency(parser.state.module, expr.range, statement.range);
dep.loc = Object.create(statement.loc);
dep.loc.index = -1;
parser.state.current.addDependency(dep);
return true;
});
parser.hooks.exportDeclaration.tap("HarmonyExportDependencyParserPlugin", statement => {});
parser.hooks.exportSpecifier.tap("HarmonyExportDependencyParserPlugin", (statement, id, name, idx) => {
const rename = parser.scope.renames.get(id);
let dep;
const harmonyNamedExports = parser.state.harmonyNamedExports = parser.state.harmonyNamedExports || new Set();
harmonyNamedExports.add(name);
if(rename === "imported var") {
const settings = parser.state.harmonySpecifier.get(id);
dep = new HarmonyExportImportedSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, harmonyNamedExports, null, this.strictExportPresence);
} else {
dep = new HarmonyExportSpecifierDependency(parser.state.module, id, name);
parser.hooks.export.tap(
"HarmonyExportDependencyParserPlugin",
statement => {
const dep = new HarmonyExportHeaderDependency(
statement.declaration && statement.declaration.range,
statement.range
);
dep.loc = Object.create(statement.loc);
dep.loc.index = -1;
parser.state.current.addDependency(dep);
return true;
}
dep.loc = Object.create(statement.loc);
dep.loc.index = idx;
parser.state.current.addDependency(dep);
return true;
});
parser.hooks.exportImportSpecifier.tap("HarmonyExportDependencyParserPlugin", (statement, source, id, name, idx) => {
const harmonyNamedExports = parser.state.harmonyNamedExports = parser.state.harmonyNamedExports || new Set();
let harmonyStarExports = null;
if(name) {
);
parser.hooks.exportImport.tap(
"HarmonyExportDependencyParserPlugin",
(statement, source) => {
parser.state.lastHarmonyImportOrder =
(parser.state.lastHarmonyImportOrder || 0) + 1;
const clearDep = new ConstDependency("", statement.range);
clearDep.loc = Object.create(statement.loc);
clearDep.loc.index = -1;
parser.state.current.addDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(
source,
parser.state.module,
parser.state.lastHarmonyImportOrder,
parser.state.harmonyParserScope
);
sideEffectDep.loc = Object.create(statement.loc);
sideEffectDep.loc.index = -1;
parser.state.current.addDependency(sideEffectDep);
return true;
}
);
parser.hooks.exportExpression.tap(
"HarmonyExportDependencyParserPlugin",
(statement, expr) => {
const dep = new HarmonyExportExpressionDependency(
parser.state.module,
expr.range,
statement.range
);
dep.loc = Object.create(statement.loc);
dep.loc.index = -1;
parser.state.current.addDependency(dep);
return true;
}
);
parser.hooks.exportDeclaration.tap(
"HarmonyExportDependencyParserPlugin",
statement => {}
);
parser.hooks.exportSpecifier.tap(
"HarmonyExportDependencyParserPlugin",
(statement, id, name, idx) => {
const rename = parser.scope.renames.get(id);
let dep;
const harmonyNamedExports = (parser.state.harmonyNamedExports =
parser.state.harmonyNamedExports || new Set());
harmonyNamedExports.add(name);
} else {
harmonyStarExports = parser.state.harmonyStarExports = parser.state.harmonyStarExports || [];
if (rename === "imported var") {
const settings = parser.state.harmonySpecifier.get(id);
dep = new HarmonyExportImportedSpecifierDependency(
settings.source,
parser.state.module,
settings.sourceOrder,
parser.state.harmonyParserScope,
settings.id,
name,
harmonyNamedExports,
null,
this.strictExportPresence
);
} else {
dep = new HarmonyExportSpecifierDependency(
parser.state.module,
id,
name
);
}
dep.loc = Object.create(statement.loc);
dep.loc.index = idx;
parser.state.current.addDependency(dep);
return true;
}
const dep = new HarmonyExportImportedSpecifierDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope, id, name, harmonyNamedExports, harmonyStarExports && harmonyStarExports.slice(), this.strictExportPresence);
if(harmonyStarExports) {
harmonyStarExports.push(dep);
);
parser.hooks.exportImportSpecifier.tap(
"HarmonyExportDependencyParserPlugin",
(statement, source, id, name, idx) => {
const harmonyNamedExports = (parser.state.harmonyNamedExports =
parser.state.harmonyNamedExports || new Set());
let harmonyStarExports = null;
if (name) {
harmonyNamedExports.add(name);
} else {
harmonyStarExports = parser.state.harmonyStarExports =
parser.state.harmonyStarExports || [];
}
const dep = new HarmonyExportImportedSpecifierDependency(
source,
parser.state.module,
parser.state.lastHarmonyImportOrder,
parser.state.harmonyParserScope,
id,
name,
harmonyNamedExports,
harmonyStarExports && harmonyStarExports.slice(),
this.strictExportPresence
);
if (harmonyStarExports) {
harmonyStarExports.push(dep);
}
dep.loc = Object.create(statement.loc);
dep.loc.index = idx;
parser.state.current.addDependency(dep);
return true;
}
dep.loc = Object.create(statement.loc);
dep.loc.index = idx;
parser.state.current.addDependency(dep);
return true;
});
);
}
};

View File

@ -29,7 +29,7 @@ HarmonyExportExpressionDependency.Template = class HarmonyExportDependencyTempla
const used = dep.originModule.isUsed("default");
const content = this.getContent(dep.originModule, used);
if(dep.range) {
if (dep.range) {
source.replace(dep.rangeStatement[0], dep.range[0] - 1, content + "(");
source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");");
return;
@ -40,8 +40,10 @@ HarmonyExportExpressionDependency.Template = class HarmonyExportDependencyTempla
getContent(module, used) {
const exportsName = module.exportsArgument;
if(used) {
return `/* harmony default export */ ${exportsName}[${JSON.stringify(used)}] = `;
if (used) {
return `/* harmony default export */ ${exportsName}[${JSON.stringify(
used
)}] = `;
}
return "/* unused harmony default export */ var _unused_webpack_default_export = ";
}

View File

@ -20,7 +20,9 @@ class HarmonyExportHeaderDependency extends NullDependency {
HarmonyExportHeaderDependency.Template = class HarmonyExportDependencyTemplate {
apply(dep, source) {
const content = "";
const replaceUntil = dep.range ? dep.range[0] - 1 : dep.rangeStatement[1] - 1;
const replaceUntil = dep.range
? dep.range[0] - 1
: dep.rangeStatement[1] - 1;
source.replace(dep.rangeStatement[0], replaceUntil, content);
}
};

View File

@ -7,7 +7,17 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
const Template = require("../Template");
class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
constructor(request, originModule, sourceOrder, parserScope, id, name, activeExports, otherStarExports, strictExportPresence) {
constructor(
request,
originModule,
sourceOrder,
parserScope,
id,
name,
activeExports,
otherStarExports,
strictExportPresence
) {
super(request, originModule, sourceOrder, parserScope);
this.id = id;
this.name = name;
@ -26,24 +36,28 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
const used = this.originModule.isUsed(name);
const importedModule = this.module;
if(!importedModule) {
if (!importedModule) {
return {
type: "missing",
userRequest: this.userRequest
};
}
if(!ignoreUnused && (name ? !used : this.originModule.usedExports === false)) {
if (
!ignoreUnused &&
(name ? !used : this.originModule.usedExports === false)
) {
return {
type: "unused",
name: name || "*"
};
}
const isNotAHarmonyModule = importedModule.buildMeta && !importedModule.buildMeta.exportsType;
const isNotAHarmonyModule =
importedModule.buildMeta && !importedModule.buildMeta.exportsType;
const strictHarmonyModule = this.originModule.buildMeta.strictHarmonyModule;
if(name && id === "default" && isNotAHarmonyModule) {
if(strictHarmonyModule) {
if (name && id === "default" && isNotAHarmonyModule) {
if (strictHarmonyModule) {
return {
type: "reexport-non-harmony-default-strict",
module: importedModule,
@ -58,10 +72,10 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
}
if(name) {
if (name) {
// export { name as name }
if(id) {
if(isNotAHarmonyModule && strictHarmonyModule) {
if (id) {
if (isNotAHarmonyModule && strictHarmonyModule) {
return {
type: "rexport-non-harmony-undefined",
module: importedModule,
@ -71,15 +85,13 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
return {
type: "safe-reexport",
module: importedModule,
map: new Map([
[name, id]
])
map: new Map([[name, id]])
};
}
}
// export { * as name }
if(isNotAHarmonyModule && strictHarmonyModule) {
if (isNotAHarmonyModule && strictHarmonyModule) {
return {
type: "reexport-fake-namespace-object",
module: importedModule,
@ -95,22 +107,29 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
const hasUsedExports = Array.isArray(this.originModule.usedExports);
const hasProvidedExports = Array.isArray(importedModule.buildMeta.providedExports);
const hasProvidedExports = Array.isArray(
importedModule.buildMeta.providedExports
);
const activeFromOtherStarExports = this._discoverActiveExportsFromOtherStartExports();
// export *
if(hasUsedExports) {
if (hasUsedExports) {
// reexport * with known used exports
if(hasProvidedExports) {
const map = new Map(this.originModule.usedExports.filter((id) => {
if(id === "default") return false;
if(this.activeExports.has(id)) return false;
if(activeFromOtherStarExports.has(id)) return false;
if(!importedModule.buildMeta.providedExports.includes(id)) return false;
return true;
}).map(item => [item, item]));
if (hasProvidedExports) {
const map = new Map(
this.originModule.usedExports
.filter(id => {
if (id === "default") return false;
if (this.activeExports.has(id)) return false;
if (activeFromOtherStarExports.has(id)) return false;
if (!importedModule.buildMeta.providedExports.includes(id))
return false;
return true;
})
.map(item => [item, item])
);
if(map.size === 0) {
if (map.size === 0) {
return {
type: "empty-star"
};
@ -123,15 +142,19 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
};
}
const map = new Map(this.originModule.usedExports.filter(id => {
if(id === "default") return false;
if(this.activeExports.has(id)) return false;
if(activeFromOtherStarExports.has(id)) return false;
const map = new Map(
this.originModule.usedExports
.filter(id => {
if (id === "default") return false;
if (this.activeExports.has(id)) return false;
if (activeFromOtherStarExports.has(id)) return false;
return true;
}).map(item => [item, item]));
return true;
})
.map(item => [item, item])
);
if(map.size === 0) {
if (map.size === 0) {
return {
type: "empty-star"
};
@ -144,19 +167,20 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
};
}
if(hasProvidedExports) {
const map = new Map(importedModule.buildMeta.providedExports
.filter(id => {
if(id === "default") return false;
if(this.activeExports.has(id)) return false;
if(activeFromOtherStarExports.has(id)) return false;
if (hasProvidedExports) {
const map = new Map(
importedModule.buildMeta.providedExports
.filter(id => {
if (id === "default") return false;
if (this.activeExports.has(id)) return false;
if (activeFromOtherStarExports.has(id)) return false;
return true;
})
.map(item => [item, item])
return true;
})
.map(item => [item, item])
);
if(map.size === 0) {
if (map.size === 0) {
return {
type: "empty-star"
};
@ -178,7 +202,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
getReference() {
const mode = this.getMode();
switch(mode.type) {
switch (mode.type) {
case "missing":
case "unused":
case "empty-star":
@ -218,14 +242,16 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
_discoverActiveExportsFromOtherStartExports() {
if(!this.otherStarExports)
return new Set();
if (!this.otherStarExports) return new Set();
const result = new Set();
// try to learn impossible exports from other star exports with provided exports
for(const otherStarExport of this.otherStarExports) {
for (const otherStarExport of this.otherStarExports) {
const otherImportedModule = otherStarExport.module;
if(otherImportedModule && Array.isArray(otherImportedModule.buildMeta.providedExports)) {
for(const exportName of otherImportedModule.buildMeta.providedExports)
if (
otherImportedModule &&
Array.isArray(otherImportedModule.buildMeta.providedExports)
) {
for (const exportName of otherImportedModule.buildMeta.providedExports)
result.add(exportName);
}
}
@ -233,7 +259,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
getExports() {
if(this.name) {
if (this.name) {
return {
exports: [this.name]
};
@ -241,21 +267,23 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
const importedModule = this.module;
if(!importedModule) {
if (!importedModule) {
// no imported module available
return {
exports: null
};
}
if(Array.isArray(importedModule.buildMeta.providedExports)) {
if (Array.isArray(importedModule.buildMeta.providedExports)) {
return {
exports: importedModule.buildMeta.providedExports.filter(id => id !== "default"),
exports: importedModule.buildMeta.providedExports.filter(
id => id !== "default"
),
dependencies: [importedModule]
};
}
if(importedModule.buildMeta.providedExports) {
if (importedModule.buildMeta.providedExports) {
return {
exports: true
};
@ -268,14 +296,20 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
getWarnings() {
if(this.strictExportPresence || this.originModule.buildMeta.strictHarmonyModule) {
if (
this.strictExportPresence ||
this.originModule.buildMeta.strictHarmonyModule
) {
return [];
}
return this._getErrors();
}
getErrors() {
if(this.strictExportPresence || this.originModule.buildMeta.strictHarmonyModule) {
if (
this.strictExportPresence ||
this.originModule.buildMeta.strictHarmonyModule
) {
return this._getErrors();
}
return [];
@ -283,34 +317,46 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
_getErrors() {
const importedModule = this.module;
if(!importedModule) {
if (!importedModule) {
return;
}
if(!importedModule.buildMeta || !importedModule.buildMeta.exportsType) {
if (!importedModule.buildMeta || !importedModule.buildMeta.exportsType) {
// It's not an harmony module
if(this.originModule.buildMeta.strictHarmonyModule && this.id !== "default") {
if (
this.originModule.buildMeta.strictHarmonyModule &&
this.id !== "default"
) {
// In strict harmony modules we only support the default export
const exportName = this.id ? `the named export '${this.id}'` : "the namespace object";
const err = new Error(`Can't reexport ${exportName} from non EcmaScript module (only default export is available)`);
const exportName = this.id
? `the named export '${this.id}'`
: "the namespace object";
const err = new Error(
`Can't reexport ${
exportName
} from non EcmaScript module (only default export is available)`
);
err.hideStack = true;
return [err];
}
return;
}
if(!this.id) {
if (!this.id) {
return;
}
if(importedModule.isProvided(this.id) !== false) {
if (importedModule.isProvided(this.id) !== false) {
// It's provided or we are not sure
return;
}
// We are sure that it's not provided
const idIsNotNameMessage = this.id !== this.name ? ` (reexported as '${this.name}')` : "";
const errorMessage = `"export '${this.id}'${idIsNotNameMessage} was not found in '${this.userRequest}'`;
const idIsNotNameMessage =
this.id !== this.name ? ` (reexported as '${this.name}')` : "";
const errorMessage = `"export '${this.id}'${
idIsNotNameMessage
} was not found in '${this.userRequest}'`;
const err = new Error(errorMessage);
err.hideStack = true;
return [err];
@ -323,13 +369,17 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
getHashValue(importedModule) {
if(!importedModule) {
if (!importedModule) {
return "";
}
const stringifiedUsedExport = JSON.stringify(importedModule.usedExports);
const stringifiedProvidedExport = JSON.stringify(importedModule.buildMeta.providedExports);
return importedModule.used + stringifiedUsedExport + stringifiedProvidedExport;
const stringifiedProvidedExport = JSON.stringify(
importedModule.buildMeta.providedExports
);
return (
importedModule.used + stringifiedUsedExport + stringifiedProvidedExport
);
}
}
@ -343,36 +393,39 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
}
getHarmonyInitOrder(dep) {
if(dep.name) {
if (dep.name) {
const used = dep.originModule.isUsed(dep.name);
if(!used) return NaN;
if (!used) return NaN;
} else {
const importedModule = dep.module;
const activeFromOtherStarExports = dep._discoverActiveExportsFromOtherStartExports();
if(Array.isArray(dep.originModule.usedExports)) {
if (Array.isArray(dep.originModule.usedExports)) {
// we know which exports are used
const unused = dep.originModule.usedExports.every(id => {
if(id === "default") return true;
if(dep.activeExports.has(id)) return true;
if(importedModule.isProvided(id) === false) return true;
if(activeFromOtherStarExports.has(id)) return true;
if (id === "default") return true;
if (dep.activeExports.has(id)) return true;
if (importedModule.isProvided(id) === false) return true;
if (activeFromOtherStarExports.has(id)) return true;
return false;
});
if(unused) return NaN;
} else if(dep.originModule.usedExports && importedModule && Array.isArray(importedModule.buildMeta.providedExports)) {
if (unused) return NaN;
} else if (
dep.originModule.usedExports &&
importedModule &&
Array.isArray(importedModule.buildMeta.providedExports)
) {
// not sure which exports are used, but we know which are provided
const unused = importedModule.buildMeta.providedExports.every(id => {
if(id === "default") return true;
if(dep.activeExports.has(id)) return true;
if(activeFromOtherStarExports.has(id)) return true;
if (id === "default") return true;
if (dep.activeExports.has(id)) return true;
if (activeFromOtherStarExports.has(id)) return true;
return false;
});
if(unused) return NaN;
if (unused) return NaN;
}
}
return super.getHarmonyInitOrder(dep);
@ -384,55 +437,134 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
const importedModule = dep.module;
const importVar = dep.getImportVar();
switch(mode.type) {
switch (mode.type) {
case "missing":
return `throw new Error(${JSON.stringify(`Cannot find module '${mode.userRequest}'`)});\n`;
return `throw new Error(${JSON.stringify(
`Cannot find module '${mode.userRequest}'`
)});\n`;
case "unused":
return `${Template.toNormalComment(`unused harmony reexport ${mode.name}`)}\n`;
return `${Template.toNormalComment(
`unused harmony reexport ${mode.name}`
)}\n`;
case "reexport-non-harmony-default":
return "/* harmony reexport (default from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, null);
return (
"/* harmony reexport (default from non-hamory) */ " +
this.getReexportStatement(
module,
module.isUsed(mode.name),
importVar,
null
)
);
case "reexport-fake-namespace-object":
return "/* harmony reexport (fake namespace object from non-hamory) */ " + this.getReexportFakeNamespaceObjectStatement(module, module.isUsed(mode.name), importVar);
return (
"/* harmony reexport (fake namespace object from non-hamory) */ " +
this.getReexportFakeNamespaceObjectStatement(
module,
module.isUsed(mode.name),
importVar
)
);
case "rexport-non-harmony-undefined":
return "/* harmony reexport (non default export from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), "undefined", "");
return (
"/* harmony reexport (non default export from non-hamory) */ " +
this.getReexportStatement(
module,
module.isUsed(mode.name),
"undefined",
""
)
);
case "reexport-non-harmony-default-strict":
return "/* harmony reexport (default from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, "");
return (
"/* harmony reexport (default from non-hamory) */ " +
this.getReexportStatement(
module,
module.isUsed(mode.name),
importVar,
""
)
);
case "reexport-namespace-object":
return "/* harmony reexport (module object) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, "");
return (
"/* harmony reexport (module object) */ " +
this.getReexportStatement(
module,
module.isUsed(mode.name),
importVar,
""
)
);
case "empty-star":
return "/* empty/unused harmony star reexport */";
case "safe-reexport":
return Array.from(mode.map.entries()).map(item => {
return "/* harmony reexport (safe) */ " + this.getReexportStatement(module, module.isUsed(item[0]), importVar, importedModule.isUsed(item[1])) + "\n";
}).join("");
return Array.from(mode.map.entries())
.map(item => {
return (
"/* harmony reexport (safe) */ " +
this.getReexportStatement(
module,
module.isUsed(item[0]),
importVar,
importedModule.isUsed(item[1])
) +
"\n"
);
})
.join("");
case "checked-reexport":
return Array.from(mode.map.entries()).map(item => {
return "/* harmony reexport (checked) */ " + this.getConditionalReexportStatement(module, item[0], importVar, item[1]) + "\n";
}).join("");
return Array.from(mode.map.entries())
.map(item => {
return (
"/* harmony reexport (checked) */ " +
this.getConditionalReexportStatement(
module,
item[0],
importVar,
item[1]
) +
"\n"
);
})
.join("");
case "dynamic-reexport":
{
const activeExports = new Set([...dep.activeExports, ...dep._discoverActiveExportsFromOtherStartExports()]);
let content = "/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " + importVar + ") ";
case "dynamic-reexport": {
const activeExports = new Set([
...dep.activeExports,
...dep._discoverActiveExportsFromOtherStartExports()
]);
let content =
"/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " +
importVar +
") ";
// Filter out exports which are defined by other exports
// and filter out default export because it cannot be reexported with *
if(activeExports.length > 0)
content += "if(" + JSON.stringify(activeExports.concat("default")) + ".indexOf(__WEBPACK_IMPORT_KEY__) < 0) ";
else
content += "if(__WEBPACK_IMPORT_KEY__ !== 'default') ";
const exportsName = dep.originModule.exportsArgument;
return content + `(function(key) { __webpack_require__.d(${exportsName}, key, function() { return ${importVar}[key]; }) }(__WEBPACK_IMPORT_KEY__));\n`;
}
// Filter out exports which are defined by other exports
// and filter out default export because it cannot be reexported with *
if (activeExports.length > 0)
content +=
"if(" +
JSON.stringify(activeExports.concat("default")) +
".indexOf(__WEBPACK_IMPORT_KEY__) < 0) ";
else content += "if(__WEBPACK_IMPORT_KEY__ !== 'default') ";
const exportsName = dep.originModule.exportsArgument;
return (
content +
`(function(key) { __webpack_require__.d(${
exportsName
}, key, function() { return ${
importVar
}[key]; }) }(__WEBPACK_IMPORT_KEY__));\n`
);
}
default:
throw new Error(`Unknown mode ${mode.type}`);
@ -442,22 +574,30 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
getReexportStatement(module, key, name, valueKey) {
const exportsName = module.exportsArgument;
const returnValue = this.getReturnValue(valueKey);
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return ${name}${returnValue}; });\n`;
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(
key
)}, function() { return ${name}${returnValue}; });\n`;
}
getReexportFakeNamespaceObjectStatement(module, key, name) {
const exportsName = module.exportsArgument;
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return { "default": ${name} }; });\n`;
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(
key
)}, function() { return { "default": ${name} }; });\n`;
}
getConditionalReexportStatement(module, key, name, valueKey) {
const exportsName = module.exportsArgument;
const returnValue = this.getReturnValue(valueKey);
return `if(__webpack_require__.o(${name}, ${JSON.stringify(valueKey)})) __webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return ${name}${returnValue}; });\n`;
return `if(__webpack_require__.o(${name}, ${JSON.stringify(
valueKey
)})) __webpack_require__.d(${exportsName}, ${JSON.stringify(
key
)}, function() { return ${name}${returnValue}; });\n`;
}
getReturnValue(valueKey) {
if(valueKey === null) {
if (valueKey === null) {
return "_default.a";
}

View File

@ -38,13 +38,15 @@ HarmonyExportSpecifierDependency.Template = class HarmonyExportSpecifierDependen
getContent(dep) {
const used = dep.originModule.isUsed(dep.name);
if(!used) {
return `/* unused harmony export ${(dep.name || "namespace")} */\n`;
if (!used) {
return `/* unused harmony export ${dep.name || "namespace"} */\n`;
}
const exportsName = dep.originModule.exportsArgument;
return `/* harmony export (binding) */ __webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${dep.id}; });\n`;
return `/* harmony export (binding) */ __webpack_require__.d(${
exportsName
}, ${JSON.stringify(used)}, function() { return ${dep.id}; });\n`;
}
};

View File

@ -15,7 +15,7 @@ class HarmonyImportDependency extends ModuleDependency {
}
getReference() {
if(!this.module) return null;
if (!this.module) return null;
return {
module: this.module,
@ -26,10 +26,12 @@ class HarmonyImportDependency extends ModuleDependency {
getImportVar() {
let importVarMap = this.parserScope.importVarMap;
if(!importVarMap) this.parserScope.importVarMap = importVarMap = new Map();
if (!importVarMap) this.parserScope.importVarMap = importVarMap = new Map();
let importVar = importVarMap.get(this.module);
if(importVar) return importVar;
importVar = `${Template.toIdentifier(`${this.userRequest}`)}__WEBPACK_IMPORTED_MODULE_${importVarMap.size}__`;
if (importVar) return importVar;
importVar = `${Template.toIdentifier(
`${this.userRequest}`
)}__WEBPACK_IMPORTED_MODULE_${importVarMap.size}__`;
importVarMap.set(this.module, importVar);
return importVar;
}
@ -47,7 +49,11 @@ class HarmonyImportDependency extends ModuleDependency {
updateHash(hash) {
super.updateHash(hash);
const importedModule = this.module;
hash.update((importedModule && (!importedModule.buildMeta || importedModule.buildMeta.exportsType)) + "");
hash.update(
(importedModule &&
(!importedModule.buildMeta || importedModule.buildMeta.exportsType)) +
""
);
hash.update((importedModule && importedModule.id) + "");
}
}
@ -65,20 +71,23 @@ HarmonyImportDependency.Template = class HarmonyImportDependencyTemplate {
static isImportEmitted(dep, source) {
let sourceInfo = importEmittedMap.get(source);
if(!sourceInfo) return false;
if (!sourceInfo) return false;
const key = dep.module || dep.request;
return key && sourceInfo.emittedImports.get(key);
}
harmonyInit(dep, source, runtime) {
let sourceInfo = importEmittedMap.get(source);
if(!sourceInfo) {
importEmittedMap.set(source, sourceInfo = {
emittedImports: new Map()
});
if (!sourceInfo) {
importEmittedMap.set(
source,
(sourceInfo = {
emittedImports: new Map()
})
);
}
const key = dep.module || dep.request;
if(key && sourceInfo.emittedImports.get(key)) return;
if (key && sourceInfo.emittedImports.get(key)) return;
sourceInfo.emittedImports.set(key, true);
const content = dep.getImportStatement(false, runtime);
source.insert(-1, content);

View File

@ -18,118 +18,197 @@ module.exports = class HarmonyImportDependencyParserPlugin {
}
apply(parser) {
parser.hooks.import.tap("HarmonyImportDependencyParserPlugin", (statement, source) => {
parser.state.lastHarmonyImportOrder = (parser.state.lastHarmonyImportOrder || 0) + 1;
const clearDep = new ConstDependency("", statement.range);
clearDep.loc = statement.loc;
parser.state.module.addDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope);
sideEffectDep.loc = statement.loc;
parser.state.module.addDependency(sideEffectDep);
return true;
});
parser.hooks.importSpecifier.tap("HarmonyImportDependencyParserPlugin", (statement, source, id, name) => {
parser.scope.definitions.delete(name);
parser.scope.renames.set(name, "imported var");
if(!parser.state.harmonySpecifier) parser.state.harmonySpecifier = new Map();
parser.state.harmonySpecifier.set(name, {
source,
id,
sourceOrder: parser.state.lastHarmonyImportOrder
});
return true;
});
parser.hooks.expression.for("imported var").tap("HarmonyImportDependencyParserPlugin", (expr) => {
const name = expr.name;
const settings = parser.state.harmonySpecifier.get(name);
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, expr.range, this.strictExportPresence);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = true;
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
parser.hooks.expressionAnyMember.for("imported var").tap("HarmonyImportDependencyParserPlugin", (expr) => {
const name = expr.object.name;
const settings = parser.state.harmonySpecifier.get(name);
if(settings.id !== null)
return false;
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, expr.property.name || expr.property.value, name, expr.range, this.strictExportPresence);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = false;
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
if(this.strictThisContextOnImports) {
// only in case when we strictly follow the spec we need a special case here
parser.hooks.callAnyMember.for("imported var").tap("HarmonyImportDependencyParserPlugin", (expr) => {
if(expr.callee.type !== "MemberExpression") return;
if(expr.callee.object.type !== "Identifier") return;
const name = expr.callee.object.name;
parser.hooks.import.tap(
"HarmonyImportDependencyParserPlugin",
(statement, source) => {
parser.state.lastHarmonyImportOrder =
(parser.state.lastHarmonyImportOrder || 0) + 1;
const clearDep = new ConstDependency("", statement.range);
clearDep.loc = statement.loc;
parser.state.module.addDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(
source,
parser.state.module,
parser.state.lastHarmonyImportOrder,
parser.state.harmonyParserScope
);
sideEffectDep.loc = statement.loc;
parser.state.module.addDependency(sideEffectDep);
return true;
}
);
parser.hooks.importSpecifier.tap(
"HarmonyImportDependencyParserPlugin",
(statement, source, id, name) => {
parser.scope.definitions.delete(name);
parser.scope.renames.set(name, "imported var");
if (!parser.state.harmonySpecifier)
parser.state.harmonySpecifier = new Map();
parser.state.harmonySpecifier.set(name, {
source,
id,
sourceOrder: parser.state.lastHarmonyImportOrder
});
return true;
}
);
parser.hooks.expression
.for("imported var")
.tap("HarmonyImportDependencyParserPlugin", expr => {
const name = expr.name;
const settings = parser.state.harmonySpecifier.get(name);
if(settings.id !== null)
return false;
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, expr.callee.property.name || expr.callee.property.value, name, expr.callee.range, this.strictExportPresence);
const dep = new HarmonyImportSpecifierDependency(
settings.source,
parser.state.module,
settings.sourceOrder,
parser.state.harmonyParserScope,
settings.id,
name,
expr.range,
this.strictExportPresence
);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = false;
dep.namespaceObjectAsContext = true;
dep.loc = expr.callee.loc;
dep.directImport = true;
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
if(expr.arguments)
parser.walkExpressions(expr.arguments);
return true;
});
parser.hooks.expressionAnyMember
.for("imported var")
.tap("HarmonyImportDependencyParserPlugin", expr => {
const name = expr.object.name;
const settings = parser.state.harmonySpecifier.get(name);
if (settings.id !== null) return false;
const dep = new HarmonyImportSpecifierDependency(
settings.source,
parser.state.module,
settings.sourceOrder,
parser.state.harmonyParserScope,
expr.property.name || expr.property.value,
name,
expr.range,
this.strictExportPresence
);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = false;
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
if (this.strictThisContextOnImports) {
// only in case when we strictly follow the spec we need a special case here
parser.hooks.callAnyMember
.for("imported var")
.tap("HarmonyImportDependencyParserPlugin", expr => {
if (expr.callee.type !== "MemberExpression") return;
if (expr.callee.object.type !== "Identifier") return;
const name = expr.callee.object.name;
const settings = parser.state.harmonySpecifier.get(name);
if (settings.id !== null) return false;
const dep = new HarmonyImportSpecifierDependency(
settings.source,
parser.state.module,
settings.sourceOrder,
parser.state.harmonyParserScope,
expr.callee.property.name || expr.callee.property.value,
name,
expr.callee.range,
this.strictExportPresence
);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = false;
dep.namespaceObjectAsContext = true;
dep.loc = expr.callee.loc;
parser.state.module.addDependency(dep);
if (expr.arguments) parser.walkExpressions(expr.arguments);
return true;
});
}
parser.hooks.call.for("imported var").tap("HarmonyImportDependencyParserPlugin", (expr) => {
const args = expr.arguments;
const fullExpr = expr;
expr = expr.callee;
if(expr.type !== "Identifier") return;
const name = expr.name;
const settings = parser.state.harmonySpecifier.get(name);
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, expr.range, this.strictExportPresence);
dep.directImport = true;
dep.callArgs = args;
dep.call = fullExpr;
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
if(args)
parser.walkExpressions(args);
return true;
});
parser.hooks.call
.for("imported var")
.tap("HarmonyImportDependencyParserPlugin", expr => {
const args = expr.arguments;
const fullExpr = expr;
expr = expr.callee;
if (expr.type !== "Identifier") return;
const name = expr.name;
const settings = parser.state.harmonySpecifier.get(name);
const dep = new HarmonyImportSpecifierDependency(
settings.source,
parser.state.module,
settings.sourceOrder,
parser.state.harmonyParserScope,
settings.id,
name,
expr.range,
this.strictExportPresence
);
dep.directImport = true;
dep.callArgs = args;
dep.call = fullExpr;
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
if (args) parser.walkExpressions(args);
return true;
});
// TODO webpack 5: refactor this, no custom hooks
if(!parser.hooks.hotAcceptCallback)
parser.hooks.hotAcceptCallback = new SyncBailHook(["expression", "requests"]);
if(!parser.hooks.hotAcceptWithoutCallback)
parser.hooks.hotAcceptWithoutCallback = new SyncBailHook(["expression", "requests"]);
parser.hooks.hotAcceptCallback.tap("HarmonyImportDependencyParserPlugin", (expr, requests) => {
const dependencies = requests
.map(request => {
const dep = new HarmonyAcceptImportDependency(request, parser.state.module, parser.state.harmonyParserScope);
if (!parser.hooks.hotAcceptCallback)
parser.hooks.hotAcceptCallback = new SyncBailHook([
"expression",
"requests"
]);
if (!parser.hooks.hotAcceptWithoutCallback)
parser.hooks.hotAcceptWithoutCallback = new SyncBailHook([
"expression",
"requests"
]);
parser.hooks.hotAcceptCallback.tap(
"HarmonyImportDependencyParserPlugin",
(expr, requests) => {
const dependencies = requests.map(request => {
const dep = new HarmonyAcceptImportDependency(
request,
parser.state.module,
parser.state.harmonyParserScope
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return dep;
});
if(dependencies.length > 0) {
const dep = new HarmonyAcceptDependency(expr.range, dependencies, true);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
if (dependencies.length > 0) {
const dep = new HarmonyAcceptDependency(
expr.range,
dependencies,
true
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
}
}
});
parser.hooks.hotAcceptWithoutCallback.tap("HarmonyImportDependencyParserPlugin", (expr, requests) => {
const dependencies = requests
.map(request => {
const dep = new HarmonyAcceptImportDependency(request, parser.state.module, parser.state.harmonyParserScope);
);
parser.hooks.hotAcceptWithoutCallback.tap(
"HarmonyImportDependencyParserPlugin",
(expr, requests) => {
const dependencies = requests.map(request => {
const dep = new HarmonyAcceptImportDependency(
request,
parser.state.module,
parser.state.harmonyParserScope
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return dep;
});
if(dependencies.length > 0) {
const dep = new HarmonyAcceptDependency(expr.range, dependencies, false);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
if (dependencies.length > 0) {
const dep = new HarmonyAcceptDependency(
expr.range,
dependencies,
false
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
}
}
});
);
}
};

View File

@ -11,7 +11,7 @@ class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
}
getReference() {
if(this.module && this.module.factoryMeta.sideEffectFree) return null;
if (this.module && this.module.factoryMeta.sideEffectFree) return null;
return super.getReference();
}
@ -23,7 +23,7 @@ class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
HarmonyImportSideEffectDependency.Template = class HarmonyImportSideEffectDependencyTemplate extends HarmonyImportDependency.Template {
getHarmonyInitOrder(dep) {
if(dep.module && dep.module.factoryMeta.sideEffectFree) return NaN;
if (dep.module && dep.module.factoryMeta.sideEffectFree) return NaN;
return super.getHarmonyInitOrder(dep);
}
};

View File

@ -6,7 +6,16 @@
const HarmonyImportDependency = require("./HarmonyImportDependency");
class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
constructor(request, originModule, sourceOrder, parserScope, id, name, range, strictExportPresence) {
constructor(
request,
originModule,
sourceOrder,
parserScope,
id,
name,
range,
strictExportPresence
) {
super(request, originModule, sourceOrder, parserScope);
this.id = id === null ? null : `${id}`;
this.name = name;
@ -23,22 +32,29 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
}
getReference() {
if(!this.module) return null;
if (!this.module) return null;
return {
module: this.module,
importedNames: this.id && !this.namespaceObjectAsContext ? [this.id] : true
importedNames:
this.id && !this.namespaceObjectAsContext ? [this.id] : true
};
}
getWarnings() {
if(this.strictExportPresence || this.originModule.buildMeta.strictHarmonyModule) {
if (
this.strictExportPresence ||
this.originModule.buildMeta.strictHarmonyModule
) {
return [];
}
return this._getErrors();
}
getErrors() {
if(this.strictExportPresence || this.originModule.buildMeta.strictHarmonyModule) {
if (
this.strictExportPresence ||
this.originModule.buildMeta.strictHarmonyModule
) {
return this._getErrors();
}
return [];
@ -46,34 +62,46 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
_getErrors() {
const importedModule = this.module;
if(!importedModule) {
if (!importedModule) {
return;
}
if(!importedModule.buildMeta || !importedModule.buildMeta.exportsType) {
if (!importedModule.buildMeta || !importedModule.buildMeta.exportsType) {
// It's not an harmony module
if(this.originModule.buildMeta.strictHarmonyModule && this.id !== "default") {
if (
this.originModule.buildMeta.strictHarmonyModule &&
this.id !== "default"
) {
// In strict harmony modules we only support the default export
const exportName = this.id ? `the named export '${this.id}'` : "the namespace object";
const err = new Error(`Can't import ${exportName} from non EcmaScript module (only default export is available)`);
const exportName = this.id
? `the named export '${this.id}'`
: "the namespace object";
const err = new Error(
`Can't import ${
exportName
} from non EcmaScript module (only default export is available)`
);
err.hideStack = true;
return [err];
}
return;
}
if(!this.id) {
if (!this.id) {
return;
}
if(importedModule.isProvided(this.id) !== false) {
if (importedModule.isProvided(this.id) !== false) {
// It's provided or we are not sure
return;
}
// We are sure that it's not provided
const idIsNotNameMessage = this.id !== this.name ? ` (imported as '${this.name}')` : "";
const errorMessage = `"export '${this.id}'${idIsNotNameMessage} was not found in '${this.userRequest}'`;
const idIsNotNameMessage =
this.id !== this.name ? ` (imported as '${this.name}')` : "";
const errorMessage = `"export '${this.id}'${
idIsNotNameMessage
} was not found in '${this.userRequest}'`;
const err = new Error(errorMessage);
err.hideStack = true;
return [err];
@ -88,9 +116,18 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
super.updateHash(hash);
const importedModule = this.module;
hash.update((importedModule && this.id) + "");
hash.update((importedModule && this.id && importedModule.isUsed(this.id)) + "");
hash.update((importedModule && (!importedModule.buildMeta || importedModule.buildMeta.exportsType)) + "");
hash.update((importedModule && (importedModule.used + JSON.stringify(importedModule.usedExports))) + "");
hash.update(
(importedModule && this.id && importedModule.isUsed(this.id)) + ""
);
hash.update(
(importedModule &&
(!importedModule.buildMeta || importedModule.buildMeta.exportsType)) +
""
);
hash.update(
(importedModule &&
importedModule.used + JSON.stringify(importedModule.usedExports)) + ""
);
}
}

View File

@ -23,16 +23,20 @@ HarmonyInitDependency.Template = class HarmonyInitDependencyTemplate {
apply(dep, source, runtime, dependencyTemplates) {
const module = dep.originModule;
const list = [];
for(const dependency of module.dependencies) {
for (const dependency of module.dependencies) {
const template = dependencyTemplates.get(dependency.constructor);
if(template && typeof template.harmonyInit === "function" && typeof template.getHarmonyInitOrder === "function") {
if (
template &&
typeof template.harmonyInit === "function" &&
typeof template.getHarmonyInitOrder === "function"
) {
const order = template.getHarmonyInitOrder(dependency);
if(!isNaN(order)) {
if (!isNaN(order)) {
list.push({
order,
listOrder: list.length,
dependency,
template,
template
});
}
}
@ -40,12 +44,17 @@ HarmonyInitDependency.Template = class HarmonyInitDependencyTemplate {
list.sort((a, b) => {
const x = a.order - b.order;
if(x) return x;
if (x) return x;
return a.listOrder - b.listOrder;
});
for(const item of list) {
item.template.harmonyInit(item.dependency, source, runtime, dependencyTemplates);
for (const item of list) {
item.template.harmonyInit(
item.dependency,
source,
runtime,
dependencyTemplates
);
}
}
};

View File

@ -27,52 +27,120 @@ class HarmonyModulesPlugin {
}
apply(compiler) {
compiler.hooks.compilation.tap("HarmonyModulesPlugin", (compilation, {
normalModuleFactory
}) => {
compilation.dependencyFactories.set(HarmonyCompatibilityDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyCompatibilityDependency, new HarmonyCompatibilityDependency.Template());
compiler.hooks.compilation.tap(
"HarmonyModulesPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
HarmonyCompatibilityDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
HarmonyCompatibilityDependency,
new HarmonyCompatibilityDependency.Template()
);
compilation.dependencyFactories.set(HarmonyInitDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyInitDependency, new HarmonyInitDependency.Template());
compilation.dependencyFactories.set(
HarmonyInitDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
HarmonyInitDependency,
new HarmonyInitDependency.Template()
);
compilation.dependencyFactories.set(HarmonyImportSideEffectDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyImportSideEffectDependency, new HarmonyImportSideEffectDependency.Template());
compilation.dependencyFactories.set(
HarmonyImportSideEffectDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
HarmonyImportSideEffectDependency,
new HarmonyImportSideEffectDependency.Template()
);
compilation.dependencyFactories.set(HarmonyImportSpecifierDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyImportSpecifierDependency, new HarmonyImportSpecifierDependency.Template());
compilation.dependencyFactories.set(
HarmonyImportSpecifierDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
HarmonyImportSpecifierDependency,
new HarmonyImportSpecifierDependency.Template()
);
compilation.dependencyFactories.set(HarmonyExportHeaderDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyExportHeaderDependency, new HarmonyExportHeaderDependency.Template());
compilation.dependencyFactories.set(
HarmonyExportHeaderDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
HarmonyExportHeaderDependency,
new HarmonyExportHeaderDependency.Template()
);
compilation.dependencyFactories.set(HarmonyExportExpressionDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyExportExpressionDependency, new HarmonyExportExpressionDependency.Template());
compilation.dependencyFactories.set(
HarmonyExportExpressionDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
HarmonyExportExpressionDependency,
new HarmonyExportExpressionDependency.Template()
);
compilation.dependencyFactories.set(HarmonyExportSpecifierDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyExportSpecifierDependency, new HarmonyExportSpecifierDependency.Template());
compilation.dependencyFactories.set(
HarmonyExportSpecifierDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
HarmonyExportSpecifierDependency,
new HarmonyExportSpecifierDependency.Template()
);
compilation.dependencyFactories.set(HarmonyExportImportedSpecifierDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyExportImportedSpecifierDependency, new HarmonyExportImportedSpecifierDependency.Template());
compilation.dependencyFactories.set(
HarmonyExportImportedSpecifierDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
HarmonyExportImportedSpecifierDependency,
new HarmonyExportImportedSpecifierDependency.Template()
);
compilation.dependencyFactories.set(HarmonyAcceptDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyAcceptDependency, new HarmonyAcceptDependency.Template());
compilation.dependencyFactories.set(
HarmonyAcceptDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
HarmonyAcceptDependency,
new HarmonyAcceptDependency.Template()
);
compilation.dependencyFactories.set(HarmonyAcceptImportDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyAcceptImportDependency, new HarmonyAcceptImportDependency.Template());
compilation.dependencyFactories.set(
HarmonyAcceptImportDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
HarmonyAcceptImportDependency,
new HarmonyAcceptImportDependency.Template()
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.harmony !== "undefined" && !parserOptions.harmony)
return;
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.harmony !== "undefined" &&
!parserOptions.harmony
)
return;
new HarmonyDetectionParserPlugin().apply(parser);
new HarmonyImportDependencyParserPlugin(this.options).apply(parser);
new HarmonyExportDependencyParserPlugin(this.options).apply(parser);
new HarmonyTopLevelThisParserPlugin().apply(parser);
};
new HarmonyDetectionParserPlugin().apply(parser);
new HarmonyImportDependencyParserPlugin(this.options).apply(parser);
new HarmonyExportDependencyParserPlugin(this.options).apply(parser);
new HarmonyTopLevelThisParserPlugin().apply(parser);
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("HarmonyModulesPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/esm").tap("HarmonyModulesPlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("HarmonyModulesPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("HarmonyModulesPlugin", handler);
}
);
}
}
module.exports = HarmonyModulesPlugin;

View File

@ -8,17 +8,18 @@ const ConstDependency = require("./ConstDependency");
class HarmonyTopLevelThisParserPlugin {
apply(parser) {
parser.hooks.expression.for("this").tap("HarmonyTopLevelThisParserPlugin", node => {
if(!parser.scope.topLevelScope)
return;
const module = parser.state.module;
const isHarmony = !!(module.buildMeta && module.buildMeta.exportsType);
if(isHarmony) {
const dep = new ConstDependency("undefined", node.range, false);
dep.loc = node.loc;
parser.state.current.addDependency(dep);
}
});
parser.hooks.expression
.for("this")
.tap("HarmonyTopLevelThisParserPlugin", node => {
if (!parser.scope.topLevelScope) return;
const module = parser.state.module;
const isHarmony = !!(module.buildMeta && module.buildMeta.exportsType);
if (isHarmony) {
const dep = new ConstDependency("undefined", node.range, false);
dep.loc = node.loc;
parser.state.current.addDependency(dep);
}
});
}
}

View File

@ -16,7 +16,6 @@ class ImportContextDependency extends ContextDependency {
get type() {
return `import() context ${this.options.mode}`;
}
}
ImportContextDependency.Template = ContextDependencyTemplateAsRequireCall;

View File

@ -18,8 +18,10 @@ class ImportParserPlugin {
apply(parser) {
parser.hooks.importCall.tap("ImportParserPlugin", expr => {
if(expr.arguments.length !== 1)
throw new Error("Incorrect number of arguments provided to 'import(module: string) -> Promise'.");
if (expr.arguments.length !== 1)
throw new Error(
"Incorrect number of arguments provided to 'import(module: string) -> Promise'."
);
const param = parser.evaluateExpression(expr.arguments[0]);
@ -29,68 +31,143 @@ class ImportParserPlugin {
let exclude = null;
const importOptions = parser.getCommentOptions(expr.range);
if(importOptions) {
if(typeof importOptions.webpackChunkName !== "undefined") {
if(typeof importOptions.webpackChunkName !== "string")
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`));
else
chunkName = importOptions.webpackChunkName;
if (importOptions) {
if (typeof importOptions.webpackChunkName !== "undefined") {
if (typeof importOptions.webpackChunkName !== "string")
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackChunkName\` expected a string, but received: ${
importOptions.webpackChunkName
}.`
)
);
else chunkName = importOptions.webpackChunkName;
}
if(typeof importOptions.webpackMode !== "undefined") {
if(typeof importOptions.webpackMode !== "string")
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackMode\` expected a string, but received: ${importOptions.webpackMode}.`));
else
mode = importOptions.webpackMode;
if (typeof importOptions.webpackMode !== "undefined") {
if (typeof importOptions.webpackMode !== "string")
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected a string, but received: ${
importOptions.webpackMode
}.`
)
);
else mode = importOptions.webpackMode;
}
if(typeof importOptions.webpackInclude !== "undefined") {
if(!importOptions.webpackInclude || importOptions.webpackInclude.constructor.name !== "RegExp") {
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`));
if (typeof importOptions.webpackInclude !== "undefined") {
if (
!importOptions.webpackInclude ||
importOptions.webpackInclude.constructor.name !== "RegExp"
) {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackInclude\` expected a regular expression, but received: ${
importOptions.webpackInclude
}.`
)
);
} else {
include = new RegExp(importOptions.webpackInclude);
}
}
if(typeof importOptions.webpackExclude !== "undefined") {
if(!importOptions.webpackExclude || importOptions.webpackExclude.constructor.name !== "RegExp") {
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`));
if (typeof importOptions.webpackExclude !== "undefined") {
if (
!importOptions.webpackExclude ||
importOptions.webpackExclude.constructor.name !== "RegExp"
) {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackExclude\` expected a regular expression, but received: ${
importOptions.webpackExclude
}.`
)
);
} else {
exclude = new RegExp(importOptions.webpackExclude);
}
}
}
if(param.isString()) {
if(mode !== "lazy" && mode !== "eager" && mode !== "weak") {
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackMode\` expected 'lazy', 'eager' or 'weak', but received: ${mode}.`));
if (param.isString()) {
if (mode !== "lazy" && mode !== "eager" && mode !== "weak") {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected 'lazy', 'eager' or 'weak', but received: ${
mode
}.`
)
);
}
if(mode === "eager") {
const dep = new ImportEagerDependency(param.string, parser.state.module, expr.range);
if (mode === "eager") {
const dep = new ImportEagerDependency(
param.string,
parser.state.module,
expr.range
);
parser.state.current.addDependency(dep);
} else if(mode === "weak") {
const dep = new ImportWeakDependency(param.string, parser.state.module, expr.range);
} else if (mode === "weak") {
const dep = new ImportWeakDependency(
param.string,
parser.state.module,
expr.range
);
parser.state.current.addDependency(dep);
} else {
const depBlock = new ImportDependenciesBlock(param.string, expr.range, chunkName, parser.state.module, expr.loc, parser.state.module);
const depBlock = new ImportDependenciesBlock(
param.string,
expr.range,
chunkName,
parser.state.module,
expr.loc,
parser.state.module
);
parser.state.current.addBlock(depBlock);
}
return true;
} else {
if(mode !== "lazy" && mode !== "lazy-once" && mode !== "eager" && mode !== "weak") {
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`));
if (
mode !== "lazy" &&
mode !== "lazy-once" &&
mode !== "eager" &&
mode !== "weak"
) {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${
mode
}.`
)
);
mode = "lazy";
}
if(mode === "weak") {
if (mode === "weak") {
mode = "async-weak";
}
const dep = ContextDependencyHelpers.create(ImportContextDependency, expr.range, param, expr, this.options, {
chunkName,
include,
exclude,
mode,
namespaceObject: parser.state.module.buildMeta.strictHarmonyModule ? "strict" : true
});
if(!dep) return;
const dep = ContextDependencyHelpers.create(
ImportContextDependency,
expr.range,
param,
expr,
this.options,
{
chunkName,
include,
exclude,
mode,
namespaceObject: parser.state.module.buildMeta.strictHarmonyModule
? "strict"
: true
}
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);

View File

@ -17,33 +17,66 @@ class ImportPlugin {
apply(compiler) {
const options = this.options;
compiler.hooks.compilation.tap("ImportPlugin", (compilation, {
contextModuleFactory,
normalModuleFactory
}) => {
compilation.dependencyFactories.set(ImportDependency, normalModuleFactory);
compilation.dependencyTemplates.set(ImportDependency, new ImportDependency.Template());
compiler.hooks.compilation.tap(
"ImportPlugin",
(compilation, { contextModuleFactory, normalModuleFactory }) => {
compilation.dependencyFactories.set(
ImportDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ImportDependency,
new ImportDependency.Template()
);
compilation.dependencyFactories.set(ImportEagerDependency, normalModuleFactory);
compilation.dependencyTemplates.set(ImportEagerDependency, new ImportEagerDependency.Template());
compilation.dependencyFactories.set(
ImportEagerDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ImportEagerDependency,
new ImportEagerDependency.Template()
);
compilation.dependencyFactories.set(ImportWeakDependency, normalModuleFactory);
compilation.dependencyTemplates.set(ImportWeakDependency, new ImportWeakDependency.Template());
compilation.dependencyFactories.set(
ImportWeakDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ImportWeakDependency,
new ImportWeakDependency.Template()
);
compilation.dependencyFactories.set(ImportContextDependency, contextModuleFactory);
compilation.dependencyTemplates.set(ImportContextDependency, new ImportContextDependency.Template());
compilation.dependencyFactories.set(
ImportContextDependency,
contextModuleFactory
);
compilation.dependencyTemplates.set(
ImportContextDependency,
new ImportContextDependency.Template()
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.import !== "undefined" && !parserOptions.import)
return;
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.import !== "undefined" &&
!parserOptions.import
)
return;
new ImportParserPlugin(options).apply(parser);
};
new ImportParserPlugin(options).apply(parser);
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("ImportPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("ImportPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/esm").tap("ImportPlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("ImportPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("ImportPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("ImportPlugin", handler);
}
);
}
}
module.exports = ImportPlugin;

View File

@ -7,56 +7,83 @@
const LoaderDependency = require("./LoaderDependency");
class LoaderPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("LoaderPlugin", (compilation, {
normalModuleFactory
}) => {
compilation.dependencyFactories.set(LoaderDependency, normalModuleFactory);
});
compiler.hooks.compilation.tap(
"LoaderPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
LoaderDependency,
normalModuleFactory
);
}
);
compiler.hooks.compilation.tap("LoaderPlugin", (compilation) => {
compilation.hooks.normalModuleLoader.tap("LoaderPlugin", (loaderContext, module) => {
loaderContext.loadModule = (request, callback) => {
const dep = new LoaderDependency(request);
dep.loc = request;
const factory = compilation.dependencyFactories.get(dep.constructor);
if(factory === undefined)
return callback(new Error(`No module factory available for dependency type: ${dep.constructor.name}`));
compilation.addModuleDependencies(module, [{
factory,
dependencies: [dep]
}], true, "lm", false, err => {
if(err) return callback(err);
compiler.hooks.compilation.tap("LoaderPlugin", compilation => {
compilation.hooks.normalModuleLoader.tap(
"LoaderPlugin",
(loaderContext, module) => {
loaderContext.loadModule = (request, callback) => {
const dep = new LoaderDependency(request);
dep.loc = request;
const factory = compilation.dependencyFactories.get(
dep.constructor
);
if (factory === undefined)
return callback(
new Error(
`No module factory available for dependency type: ${
dep.constructor.name
}`
)
);
compilation.addModuleDependencies(
module,
[
{
factory,
dependencies: [dep]
}
],
true,
"lm",
false,
err => {
if (err) return callback(err);
if(!dep.module) return callback(new Error("Cannot load the module"));
if (!dep.module)
return callback(new Error("Cannot load the module"));
if(dep.module.error) return callback(dep.module.error);
if(!dep.module._source) throw new Error("The module created for a LoaderDependency must have a property _source");
let source, map;
const moduleSource = dep.module._source;
if(moduleSource.sourceAndMap) {
const sourceAndMap = moduleSource.sourceAndMap();
map = sourceAndMap.map;
source = sourceAndMap.source;
} else {
map = moduleSource.map();
source = moduleSource.source();
}
if(dep.module.buildInfo.fileDependencies) {
for(const d of dep.module.buildInfo.fileDependencies) {
loaderContext.addDependency(d);
if (dep.module.error) return callback(dep.module.error);
if (!dep.module._source)
throw new Error(
"The module created for a LoaderDependency must have a property _source"
);
let source, map;
const moduleSource = dep.module._source;
if (moduleSource.sourceAndMap) {
const sourceAndMap = moduleSource.sourceAndMap();
map = sourceAndMap.map;
source = sourceAndMap.source;
} else {
map = moduleSource.map();
source = moduleSource.source();
}
if (dep.module.buildInfo.fileDependencies) {
for (const d of dep.module.buildInfo.fileDependencies) {
loaderContext.addDependency(d);
}
}
if (dep.module.buildInfo.contextDependencies) {
for (const d of dep.module.buildInfo.contextDependencies) {
loaderContext.addContextDependency(d);
}
}
return callback(null, source, map, dep.module);
}
}
if(dep.module.buildInfo.contextDependencies) {
for(const d of dep.module.buildInfo.contextDependencies) {
loaderContext.addContextDependency(d);
}
}
return callback(null, source, map, dep.module);
});
};
});
);
};
}
);
});
}
}

View File

@ -16,8 +16,12 @@ class LocalModuleDependency extends NullDependency {
LocalModuleDependency.Template = class LocalModuleDependencyTemplate {
apply(dep, source) {
if(!dep.range) return;
source.replace(dep.range[0], dep.range[1] - 1, dep.localModule.variableName());
if (!dep.range) return;
source.replace(
dep.range[0],
dep.range[1] - 1,
dep.localModule.variableName()
);
}
};

View File

@ -8,37 +8,36 @@ const LocalModule = require("./LocalModule");
const LocalModulesHelpers = exports;
const lookup = (parent, mod) => {
if(mod.charAt(0) !== ".") return mod;
if (mod.charAt(0) !== ".") return mod;
var path = parent.split("/"),
segs = mod.split("/");
path.pop();
for(let i = 0; i < segs.length; i++) {
for (let i = 0; i < segs.length; i++) {
const seg = segs[i];
if(seg === "..") path.pop();
else if(seg !== ".") path.push(seg);
if (seg === "..") path.pop();
else if (seg !== ".") path.push(seg);
}
return path.join("/");
};
LocalModulesHelpers.addLocalModule = (state, name) => {
if(!state.localModules) state.localModules = [];
if (!state.localModules) state.localModules = [];
const m = new LocalModule(state.module, name, state.localModules.length);
state.localModules.push(m);
return m;
};
LocalModulesHelpers.getLocalModule = (state, name, namedModule) => {
if(!state.localModules) return null;
if(namedModule) {
if (!state.localModules) return null;
if (namedModule) {
// resolve dependency name relative to the defining named module
name = lookup(namedModule, name);
}
for(let i = 0; i < state.localModules.length; i++) {
if(state.localModules[i].name === name)
return state.localModules[i];
for (let i = 0; i < state.localModules.length; i++) {
if (state.localModules[i].name === name) return state.localModules[i];
}
return null;
};

View File

@ -5,9 +5,8 @@
"use strict";
class ModuleDependencyTemplateAsId {
apply(dep, source, runtime) {
if(!dep.range) return;
if (!dep.range) return;
const content = runtime.moduleId({
module: dep.module,
request: dep.request

View File

@ -5,9 +5,8 @@
"use strict";
class ModuleDependencyTemplateAsRequireId {
apply(dep, source, runtime) {
if(!dep.range) return;
if (!dep.range) return;
const content = runtime.moduleExports({
module: dep.module,
request: dep.request

View File

@ -8,48 +8,49 @@ const RequireContextDependency = require("./RequireContextDependency");
module.exports = class RequireContextDependencyParserPlugin {
apply(parser) {
parser.hooks.call.for("require.context").tap("RequireContextDependencyParserPlugin", expr => {
let regExp = /^\.\/.*$/;
let recursive = true;
let mode = "sync";
switch(expr.arguments.length) {
case 4:
{
parser.hooks.call
.for("require.context")
.tap("RequireContextDependencyParserPlugin", expr => {
let regExp = /^\.\/.*$/;
let recursive = true;
let mode = "sync";
switch (expr.arguments.length) {
case 4: {
const modeExpr = parser.evaluateExpression(expr.arguments[3]);
if(!modeExpr.isString()) return;
if (!modeExpr.isString()) return;
mode = modeExpr.string;
}
// falls through
case 3:
{
case 3: {
const regExpExpr = parser.evaluateExpression(expr.arguments[2]);
if(!regExpExpr.isRegExp()) return;
if (!regExpExpr.isRegExp()) return;
regExp = regExpExpr.regExp;
}
// falls through
case 2:
{
case 2: {
const recursiveExpr = parser.evaluateExpression(expr.arguments[1]);
if(!recursiveExpr.isBoolean()) return;
if (!recursiveExpr.isBoolean()) return;
recursive = recursiveExpr.bool;
}
// falls through
case 1:
{
case 1: {
const requestExpr = parser.evaluateExpression(expr.arguments[0]);
if(!requestExpr.isString()) return;
const dep = new RequireContextDependency({
request: requestExpr.string,
recursive,
regExp,
mode
}, expr.range);
if (!requestExpr.isString()) return;
const dep = new RequireContextDependency(
{
request: requestExpr.string,
recursive,
regExp,
mode
},
expr.range
);
dep.loc = expr.loc;
dep.optional = parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
}
});
}
});
}
};

View File

@ -11,9 +11,9 @@ const RequireContextDependencyParserPlugin = require("./RequireContextDependency
class RequireContextPlugin {
constructor(modulesDirectories, extensions, mainFiles) {
if(!Array.isArray(modulesDirectories))
if (!Array.isArray(modulesDirectories))
throw new Error("modulesDirectories must be an array");
if(!Array.isArray(extensions))
if (!Array.isArray(extensions))
throw new Error("extensions must be an array");
this.modulesDirectories = modulesDirectories;
this.extensions = extensions;
@ -21,75 +21,121 @@ class RequireContextPlugin {
}
apply(compiler) {
compiler.hooks.compilation.tap("RequireContextPlugin", (compilation, {
contextModuleFactory,
normalModuleFactory
}) => {
compilation.dependencyFactories.set(RequireContextDependency, contextModuleFactory);
compilation.dependencyTemplates.set(RequireContextDependency, new RequireContextDependency.Template());
compiler.hooks.compilation.tap(
"RequireContextPlugin",
(compilation, { contextModuleFactory, normalModuleFactory }) => {
compilation.dependencyFactories.set(
RequireContextDependency,
contextModuleFactory
);
compilation.dependencyTemplates.set(
RequireContextDependency,
new RequireContextDependency.Template()
);
compilation.dependencyFactories.set(ContextElementDependency, normalModuleFactory);
compilation.dependencyFactories.set(
ContextElementDependency,
normalModuleFactory
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.requireContext !== "undefined" && !parserOptions.requireContext)
return;
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.requireContext !== "undefined" &&
!parserOptions.requireContext
)
return;
new RequireContextDependencyParserPlugin().apply(parser);
};
new RequireContextDependencyParserPlugin().apply(parser);
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("RequireContextPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("RequireContextPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("RequireContextPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("RequireContextPlugin", handler);
contextModuleFactory.hooks.alternatives.tap("RequireContextPlugin", (items) => {
if(items.length === 0) return items;
return items.map((obj) => {
return this.extensions.filter((ext) => {
const l = obj.request.length;
return l > ext.length && obj.request.substr(l - ext.length, l) === ext;
}).map((ext) => {
const l = obj.request.length;
return {
context: obj.context,
request: obj.request.substr(0, l - ext.length)
};
}).concat(obj);
}).reduce((a, b) => a.concat(b), []);
});
contextModuleFactory.hooks.alternatives.tap("RequireContextPlugin", (items) => {
if(items.length === 0) return items;
return items.map((obj) => {
return this.mainFiles.filter((mainFile) => {
const l = obj.request.length;
return l > mainFile.length + 1 && obj.request.substr(l - mainFile.length - 1, l) === "/" + mainFile;
}).map((mainFile) => {
const l = obj.request.length;
return [{
context: obj.context,
request: obj.request.substr(0, l - mainFile.length)
}, {
context: obj.context,
request: obj.request.substr(0, l - mainFile.length - 1)
}];
}).reduce((a, b) => a.concat(b), []).concat(obj);
}).reduce((a, b) => a.concat(b), []);
});
contextModuleFactory.hooks.alternatives.tap("RequireContextPlugin", (items) => {
if(items.length === 0) return items;
return items.map((obj) => {
for(let i = 0; i < this.modulesDirectories.length; i++) {
const dir = this.modulesDirectories[i];
const idx = obj.request.indexOf("./" + dir + "/");
if(idx === 0) {
obj.request = obj.request.slice(dir.length + 3);
break;
}
contextModuleFactory.hooks.alternatives.tap(
"RequireContextPlugin",
items => {
if (items.length === 0) return items;
return items
.map(obj => {
return this.extensions
.filter(ext => {
const l = obj.request.length;
return (
l > ext.length &&
obj.request.substr(l - ext.length, l) === ext
);
})
.map(ext => {
const l = obj.request.length;
return {
context: obj.context,
request: obj.request.substr(0, l - ext.length)
};
})
.concat(obj);
})
.reduce((a, b) => a.concat(b), []);
}
return obj;
});
});
});
);
contextModuleFactory.hooks.alternatives.tap(
"RequireContextPlugin",
items => {
if (items.length === 0) return items;
return items
.map(obj => {
return this.mainFiles
.filter(mainFile => {
const l = obj.request.length;
return (
l > mainFile.length + 1 &&
obj.request.substr(l - mainFile.length - 1, l) ===
"/" + mainFile
);
})
.map(mainFile => {
const l = obj.request.length;
return [
{
context: obj.context,
request: obj.request.substr(0, l - mainFile.length)
},
{
context: obj.context,
request: obj.request.substr(0, l - mainFile.length - 1)
}
];
})
.reduce((a, b) => a.concat(b), [])
.concat(obj);
})
.reduce((a, b) => a.concat(b), []);
}
);
contextModuleFactory.hooks.alternatives.tap(
"RequireContextPlugin",
items => {
if (items.length === 0) return items;
return items.map(obj => {
for (let i = 0; i < this.modulesDirectories.length; i++) {
const dir = this.modulesDirectories[i];
const idx = obj.request.indexOf("./" + dir + "/");
if (idx === 0) {
obj.request = obj.request.slice(dir.length + 3);
break;
}
}
return obj;
});
}
);
}
);
}
}
module.exports = RequireContextPlugin;

View File

@ -7,11 +7,22 @@ const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const RequireEnsureDependency = require("./RequireEnsureDependency");
module.exports = class RequireEnsureDependenciesBlock extends AsyncDependenciesBlock {
constructor(expr, successExpression, errorExpression, chunkName, chunkNameRange, module, loc) {
constructor(
expr,
successExpression,
errorExpression,
chunkName,
chunkNameRange,
module,
loc
) {
super(chunkName, module, loc, null);
this.expr = expr;
const successBodyRange = successExpression && successExpression.body && successExpression.body.range;
if(successBodyRange) {
const successBodyRange =
successExpression &&
successExpression.body &&
successExpression.body.range;
if (successBodyRange) {
this.range = [successBodyRange[0] + 1, successBodyRange[1] - 1];
}
this.chunkNameRange = chunkNameRange;

View File

@ -10,59 +10,74 @@ const getFunctionExpression = require("./getFunctionExpression");
module.exports = class RequireEnsureDependenciesBlockParserPlugin {
apply(parser) {
parser.hooks.call.for("require.ensure").tap("RequireEnsureDependenciesBlockParserPlugin", expr => {
let chunkName = null;
let chunkNameRange = null;
let errorExpressionArg = null;
let errorExpression = null;
switch(expr.arguments.length) {
case 4:
{
parser.hooks.call
.for("require.ensure")
.tap("RequireEnsureDependenciesBlockParserPlugin", expr => {
let chunkName = null;
let chunkNameRange = null;
let errorExpressionArg = null;
let errorExpression = null;
switch (expr.arguments.length) {
case 4: {
const chunkNameExpr = parser.evaluateExpression(expr.arguments[3]);
if(!chunkNameExpr.isString()) return;
if (!chunkNameExpr.isString()) return;
chunkNameRange = chunkNameExpr.range;
chunkName = chunkNameExpr.string;
}
// falls through
case 3:
{
case 3: {
errorExpressionArg = expr.arguments[2];
errorExpression = getFunctionExpression(errorExpressionArg);
if(!errorExpression && !chunkName) {
const chunkNameExpr = parser.evaluateExpression(expr.arguments[2]);
if(!chunkNameExpr.isString()) return;
if (!errorExpression && !chunkName) {
const chunkNameExpr = parser.evaluateExpression(
expr.arguments[2]
);
if (!chunkNameExpr.isString()) return;
chunkNameRange = chunkNameExpr.range;
chunkName = chunkNameExpr.string;
}
}
// falls through
case 2:
{
const dependenciesExpr = parser.evaluateExpression(expr.arguments[0]);
const dependenciesItems = dependenciesExpr.isArray() ? dependenciesExpr.items : [dependenciesExpr];
case 2: {
const dependenciesExpr = parser.evaluateExpression(
expr.arguments[0]
);
const dependenciesItems = dependenciesExpr.isArray()
? dependenciesExpr.items
: [dependenciesExpr];
const successExpressionArg = expr.arguments[1];
const successExpression = getFunctionExpression(successExpressionArg);
const successExpression = getFunctionExpression(
successExpressionArg
);
if(successExpression) {
if (successExpression) {
parser.walkExpressions(successExpression.expressions);
}
if(errorExpression) {
if (errorExpression) {
parser.walkExpressions(errorExpression.expressions);
}
const dep = new RequireEnsureDependenciesBlock(expr,
const dep = new RequireEnsureDependenciesBlock(
expr,
successExpression ? successExpression.fn : successExpressionArg,
errorExpression ? errorExpression.fn : errorExpressionArg,
chunkName, chunkNameRange, parser.state.module, expr.loc);
chunkName,
chunkNameRange,
parser.state.module,
expr.loc
);
const old = parser.state.current;
parser.state.current = dep;
try {
let failed = false;
parser.inScope([], () => {
for(const ee of dependenciesItems) {
if(ee.isString()) {
const edep = new RequireEnsureItemDependency(ee.string, ee.range);
for (const ee of dependenciesItems) {
if (ee.isString()) {
const edep = new RequireEnsureItemDependency(
ee.string,
ee.range
);
edep.loc = dep.loc;
dep.addDependency(edep);
} else {
@ -70,33 +85,31 @@ module.exports = class RequireEnsureDependenciesBlockParserPlugin {
}
}
});
if(failed) {
if (failed) {
return;
}
if(successExpression) {
if(successExpression.fn.body.type === "BlockStatement")
if (successExpression) {
if (successExpression.fn.body.type === "BlockStatement")
parser.walkStatement(successExpression.fn.body);
else
parser.walkExpression(successExpression.fn.body);
else parser.walkExpression(successExpression.fn.body);
}
old.addBlock(dep);
} finally {
parser.state.current = old;
}
if(!successExpression) {
if (!successExpression) {
parser.walkExpression(successExpressionArg);
}
if(errorExpression) {
if(errorExpression.fn.body.type === "BlockStatement")
if (errorExpression) {
if (errorExpression.fn.body.type === "BlockStatement")
parser.walkStatement(errorExpression.fn.body);
else
parser.walkExpression(errorExpression.fn.body);
} else if(errorExpressionArg) {
else parser.walkExpression(errorExpression.fn.body);
} else if (errorExpressionArg) {
parser.walkExpression(errorExpressionArg);
}
return true;
}
}
});
}
});
}
};

View File

@ -23,16 +23,34 @@ RequireEnsureDependency.Template = class RequireEnsureDependencyTemplate {
block: depBlock,
message: "require.ensure"
});
const errorCallbackExists = depBlock.expr.arguments.length === 4 || (!depBlock.chunkName && depBlock.expr.arguments.length === 3);
const errorCallbackExists =
depBlock.expr.arguments.length === 4 ||
(!depBlock.chunkName && depBlock.expr.arguments.length === 3);
const startBlock = `${promise}.then((`;
const middleBlock = ").bind(null, __webpack_require__)).catch(";
const endBlock = `).bind(null, __webpack_require__)).catch(${runtime.onError()})`;
source.replace(depBlock.expr.range[0], depBlock.expr.arguments[1].range[0] - 1, startBlock);
if(errorCallbackExists) {
source.replace(depBlock.expr.arguments[1].range[1], depBlock.expr.arguments[2].range[0] - 1, middleBlock);
source.replace(depBlock.expr.arguments[2].range[1], depBlock.expr.range[1] - 1, ")");
source.replace(
depBlock.expr.range[0],
depBlock.expr.arguments[1].range[0] - 1,
startBlock
);
if (errorCallbackExists) {
source.replace(
depBlock.expr.arguments[1].range[1],
depBlock.expr.arguments[2].range[0] - 1,
middleBlock
);
source.replace(
depBlock.expr.arguments[2].range[1],
depBlock.expr.range[1] - 1,
")"
);
} else {
source.replace(depBlock.expr.arguments[1].range[1], depBlock.expr.range[1] - 1, endBlock);
source.replace(
depBlock.expr.arguments[1].range[1],
depBlock.expr.range[1] - 1,
endBlock
);
}
}
};

View File

@ -14,29 +14,61 @@ const RequireEnsureDependenciesBlockParserPlugin = require("./RequireEnsureDepen
const ParserHelpers = require("../ParserHelpers");
class RequireEnsurePlugin {
apply(compiler) {
compiler.hooks.compilation.tap("RequireEnsurePlugin", (compilation, {
normalModuleFactory
}) => {
compilation.dependencyFactories.set(RequireEnsureItemDependency, normalModuleFactory);
compilation.dependencyTemplates.set(RequireEnsureItemDependency, new RequireEnsureItemDependency.Template());
compiler.hooks.compilation.tap(
"RequireEnsurePlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
RequireEnsureItemDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
RequireEnsureItemDependency,
new RequireEnsureItemDependency.Template()
);
compilation.dependencyFactories.set(RequireEnsureDependency, new NullFactory());
compilation.dependencyTemplates.set(RequireEnsureDependency, new RequireEnsureDependency.Template());
compilation.dependencyFactories.set(
RequireEnsureDependency,
new NullFactory()
);
compilation.dependencyTemplates.set(
RequireEnsureDependency,
new RequireEnsureDependency.Template()
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.requireEnsure !== "undefined" && !parserOptions.requireEnsure)
return;
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.requireEnsure !== "undefined" &&
!parserOptions.requireEnsure
)
return;
new RequireEnsureDependenciesBlockParserPlugin().apply(parser);
parser.hooks.evaluateTypeof.for("require.ensure").tap("RequireEnsurePlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.typeof.for("require.ensure").tap("RequireEnsurePlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("function")));
};
new RequireEnsureDependenciesBlockParserPlugin().apply(parser);
parser.hooks.evaluateTypeof
.for("require.ensure")
.tap(
"RequireEnsurePlugin",
ParserHelpers.evaluateToString("function")
);
parser.hooks.typeof
.for("require.ensure")
.tap(
"RequireEnsurePlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("function")
)
);
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("RequireEnsurePlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("RequireEnsurePlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("RequireEnsurePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("RequireEnsurePlugin", handler);
}
);
}
}
module.exports = RequireEnsurePlugin;

View File

@ -8,7 +8,7 @@ const NullDependency = require("./NullDependency");
class RequireHeaderDependency extends NullDependency {
constructor(range) {
super();
if(!Array.isArray(range)) throw new Error("range must be valid");
if (!Array.isArray(range)) throw new Error("range must be valid");
this.range = range;
}
}

View File

@ -14,7 +14,7 @@ class RequireIncludeDependency extends ModuleDependency {
}
getReference() {
if(!this.module) return null;
if (!this.module) return null;
return {
module: this.module,
importedNames: [] // This doesn't use any export
@ -28,7 +28,11 @@ class RequireIncludeDependency extends ModuleDependency {
RequireIncludeDependency.Template = class RequireIncludeDependencyTemplate {
apply(dep, source, runtime) {
const comment = runtime.outputOptions.pathinfo ? Template.toComment(`require.include ${runtime.requestShortener.shorten(dep.request)}`) : "";
const comment = runtime.outputOptions.pathinfo
? Template.toComment(
`require.include ${runtime.requestShortener.shorten(dep.request)}`
)
: "";
source.replace(dep.range[0], dep.range[1] - 1, `undefined${comment}`);
}
};

View File

@ -8,14 +8,16 @@ const RequireIncludeDependency = require("./RequireIncludeDependency");
module.exports = class RequireIncludeDependencyParserPlugin {
apply(parser) {
parser.hooks.call.for("require.include").tap("RequireIncludeDependencyParserPlugin", expr => {
if(expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if(!param.isString()) return;
const dep = new RequireIncludeDependency(param.string, expr.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
});
parser.hooks.call
.for("require.include")
.tap("RequireIncludeDependencyParserPlugin", expr => {
if (expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if (!param.isString()) return;
const dep = new RequireIncludeDependency(param.string, expr.range);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
});
}
};

View File

@ -11,24 +11,51 @@ const ParserHelpers = require("../ParserHelpers");
class RequireIncludePlugin {
apply(compiler) {
compiler.hooks.compilation.tap("RequireIncludePlugin", (compilation, {
normalModuleFactory
}) => {
compilation.dependencyFactories.set(RequireIncludeDependency, normalModuleFactory);
compilation.dependencyTemplates.set(RequireIncludeDependency, new RequireIncludeDependency.Template());
compiler.hooks.compilation.tap(
"RequireIncludePlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
RequireIncludeDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
RequireIncludeDependency,
new RequireIncludeDependency.Template()
);
const handler = (parser, parserOptions) => {
if(typeof parserOptions.requireInclude !== "undefined" && !parserOptions.requireInclude)
return;
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.requireInclude !== "undefined" &&
!parserOptions.requireInclude
)
return;
new RequireIncludeDependencyParserPlugin().apply(parser);
parser.hooks.evaluateTypeof.for("require.include").tap("RequireIncludePlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.typeof.for("require.include").tap("RequireIncludePlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("function")));
};
new RequireIncludeDependencyParserPlugin().apply(parser);
parser.hooks.evaluateTypeof
.for("require.include")
.tap(
"RequireIncludePlugin",
ParserHelpers.evaluateToString("function")
);
parser.hooks.typeof
.for("require.include")
.tap(
"RequireIncludePlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("function")
)
);
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("RequireIncludePlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("RequireIncludePlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("RequireIncludePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("RequireIncludePlugin", handler);
}
);
}
}
module.exports = RequireIncludePlugin;

View File

@ -16,7 +16,6 @@ class RequireResolveContextDependency extends ContextDependency {
get type() {
return "amd require context";
}
}
RequireResolveContextDependency.Template = ContextDependencyTemplateAsId;

View File

@ -18,12 +18,12 @@ class RequireResolveDependencyParserPlugin {
const options = this.options;
const process = (expr, weak) => {
if(expr.arguments.length !== 1) return;
if (expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if(param.isConditional()) {
for(const option of param.options) {
if (param.isConditional()) {
for (const option of param.options) {
const result = processItem(expr, option, weak);
if(result === undefined) {
if (result === undefined) {
processContext(expr, option, weak);
}
}
@ -33,7 +33,7 @@ class RequireResolveDependencyParserPlugin {
return true;
} else {
const result = processItem(expr, param, weak);
if(result === undefined) {
if (result === undefined) {
processContext(expr, param, weak);
}
const dep = new RequireResolveHeaderDependency(expr.callee.range);
@ -43,7 +43,7 @@ class RequireResolveDependencyParserPlugin {
}
};
const processItem = (expr, param, weak) => {
if(param.isString()) {
if (param.isString()) {
const dep = new RequireResolveDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
@ -53,22 +53,33 @@ class RequireResolveDependencyParserPlugin {
}
};
const processContext = (expr, param, weak) => {
const dep = ContextDependencyHelpers.create(RequireResolveContextDependency, param.range, param, expr, options, {
mode: weak ? "weak" : "sync"
});
if(!dep) return;
const dep = ContextDependencyHelpers.create(
RequireResolveContextDependency,
param.range,
param,
expr,
options,
{
mode: weak ? "weak" : "sync"
}
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.call.for("require.resolve").tap("RequireResolveDependencyParserPlugin", (expr) => {
return process(expr, false);
});
parser.hooks.call.for("require.resolveWeak").tap("RequireResolveDependencyParserPlugin", (expr) => {
return process(expr, true);
});
parser.hooks.call
.for("require.resolve")
.tap("RequireResolveDependencyParserPlugin", expr => {
return process(expr, false);
});
parser.hooks.call
.for("require.resolveWeak")
.tap("RequireResolveDependencyParserPlugin", expr => {
return process(expr, true);
});
}
}
module.exports = RequireResolveDependencyParserPlugin;

View File

@ -8,7 +8,7 @@ const NullDependency = require("./NullDependency");
class RequireResolveHeaderDependency extends NullDependency {
constructor(range) {
super();
if(!Array.isArray(range)) throw new Error("range must be valid");
if (!Array.isArray(range)) throw new Error("range must be valid");
this.range = range;
}
}

View File

@ -13,49 +13,96 @@ class SystemPlugin {
}
apply(compiler) {
compiler.hooks.compilation.tap("SystemPlugin", (compilation, {
normalModuleFactory
}) => {
const handler = (parser, parserOptions) => {
if(typeof parserOptions.system !== "undefined" && !parserOptions.system)
return;
compiler.hooks.compilation.tap(
"SystemPlugin",
(compilation, { normalModuleFactory }) => {
const handler = (parser, parserOptions) => {
if (
typeof parserOptions.system !== "undefined" &&
!parserOptions.system
)
return;
const shouldWarn = typeof parserOptions.system === "undefined";
const shouldWarn = typeof parserOptions.system === "undefined";
const setNotSupported = name => {
parser.hooks.evaluateTypeof.for(name).tap("SystemPlugin", ParserHelpers.evaluateToString("undefined"));
parser.hooks.expression.for(name).tap("SystemPlugin",
ParserHelpers.expressionIsUnsupported(parser, name + " is not supported by webpack.")
);
const setNotSupported = name => {
parser.hooks.evaluateTypeof
.for(name)
.tap("SystemPlugin", ParserHelpers.evaluateToString("undefined"));
parser.hooks.expression
.for(name)
.tap(
"SystemPlugin",
ParserHelpers.expressionIsUnsupported(
parser,
name + " is not supported by webpack."
)
);
};
parser.hooks.typeof
.for("System.import")
.tap(
"SystemPlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("function")
)
);
parser.hooks.evaluateTypeof
.for("System.import")
.tap("SystemPlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.typeof
.for("System")
.tap(
"SystemPlugin",
ParserHelpers.toConstantDependency(
parser,
JSON.stringify("object")
)
);
parser.hooks.evaluateTypeof
.for("System")
.tap("SystemPlugin", ParserHelpers.evaluateToString("object"));
setNotSupported("System.set");
setNotSupported("System.get");
setNotSupported("System.register");
parser.hooks.expression.for("System").tap("SystemPlugin", () => {
const systemPolyfillRequire = ParserHelpers.requireFileAsExpression(
parser.state.module.context,
require.resolve("../../buildin/system.js")
);
return ParserHelpers.addParsedVariableToModule(
parser,
"System",
systemPolyfillRequire
);
});
parser.hooks.call.for("System.import").tap("SystemPlugin", expr => {
if (shouldWarn) {
parser.state.module.warnings.push(
new SystemImportDeprecationWarning(
parser.state.module,
expr.loc
)
);
}
return parser.hooks.importCall.call(expr);
});
};
parser.hooks.typeof.for("System.import").tap("SystemPlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("function")));
parser.hooks.evaluateTypeof.for("System.import").tap("SystemPlugin", ParserHelpers.evaluateToString("function"));
parser.hooks.typeof.for("System").tap("SystemPlugin", ParserHelpers.toConstantDependency(parser, JSON.stringify("object")));
parser.hooks.evaluateTypeof.for("System").tap("SystemPlugin", ParserHelpers.evaluateToString("object"));
setNotSupported("System.set");
setNotSupported("System.get");
setNotSupported("System.register");
parser.hooks.expression.for("System").tap("SystemPlugin", () => {
const systemPolyfillRequire = ParserHelpers.requireFileAsExpression(
parser.state.module.context, require.resolve("../../buildin/system.js"));
return ParserHelpers.addParsedVariableToModule(parser, "System", systemPolyfillRequire);
});
parser.hooks.call.for("System.import").tap("SystemPlugin", expr => {
if(shouldWarn) {
parser.state.module.warnings.push(new SystemImportDeprecationWarning(parser.state.module, expr.loc));
}
return parser.hooks.importCall.call(expr);
});
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("SystemPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("SystemPlugin", handler);
});
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("SystemPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("SystemPlugin", handler);
}
);
}
}
@ -64,7 +111,8 @@ class SystemImportDeprecationWarning extends WebpackError {
super();
this.name = "SystemImportDeprecationWarning";
this.message = "System.import() is deprecated and will be removed soon. Use import() instead.\n" +
this.message =
"System.import() is deprecated and will be removed soon. Use import() instead.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/";
this.origin = this.module = module;

View File

@ -16,7 +16,11 @@ class UnsupportedDependency extends NullDependency {
UnsupportedDependency.Template = class UnsupportedDependencyTemplate {
apply(dep, source, runtime) {
source.replace(dep.range[0], dep.range[1], webpackMissingModule(dep.request));
source.replace(
dep.range[0],
dep.range[1],
webpackMissingModule(dep.request)
);
}
};

View File

@ -12,7 +12,7 @@ class WebAssemblyImportDependency extends ModuleDependency {
}
getReference() {
if(!this.module) return null;
if (!this.module) return null;
return {
module: this.module,
importedNames: [this.name]

View File

@ -3,16 +3,20 @@
Author Tobias Koppers @sokra
*/
"use strict";
const toErrorCode = err => `var e = new Error(${JSON.stringify(err)}); e.code = 'MODULE_NOT_FOUND';`;
const toErrorCode = err =>
`var e = new Error(${JSON.stringify(err)}); e.code = 'MODULE_NOT_FOUND';`;
exports.module = request => `!(function webpackMissingModule() { ${exports.moduleCode(request)} }())`;
exports.module = request =>
`!(function webpackMissingModule() { ${exports.moduleCode(request)} }())`;
exports.promise = (request) => {
exports.promise = request => {
const errorCode = toErrorCode(`Cannot find module "${request}"`);
return `Promise.reject(function webpackMissingModule() { ${errorCode} return e; }())`;
return `Promise.reject(function webpackMissingModule() { ${
errorCode
} return e; }())`;
};
exports.moduleCode = (request) => {
exports.moduleCode = request => {
const errorCode = toErrorCode(`Cannot find module "${request}"`);
return `${errorCode} throw e;`;
};

View File

@ -4,7 +4,10 @@
*/
module.exports = expr => {
// <FunctionExpression>
if(expr.type === "FunctionExpression" || expr.type === "ArrowFunctionExpression") {
if (
expr.type === "FunctionExpression" ||
expr.type === "ArrowFunctionExpression"
) {
return {
fn: expr,
expressions: [],
@ -13,19 +16,22 @@ module.exports = expr => {
}
// <FunctionExpression>.bind(<Expression>)
if(expr.type === "CallExpression" &&
if (
expr.type === "CallExpression" &&
expr.callee.type === "MemberExpression" &&
expr.callee.object.type === "FunctionExpression" &&
expr.callee.property.type === "Identifier" &&
expr.callee.property.name === "bind" &&
expr.arguments.length === 1) {
expr.arguments.length === 1
) {
return {
fn: expr.callee.object,
expressions: [expr.arguments[0]]
};
}
// (function(_this) {return <FunctionExpression>})(this) (Coffeescript)
if(expr.type === "CallExpression" &&
if (
expr.type === "CallExpression" &&
expr.callee.type === "FunctionExpression" &&
expr.callee.body.type === "BlockStatement" &&
expr.arguments.length === 1 &&
@ -34,7 +40,8 @@ module.exports = expr => {
expr.callee.body.body.length === 1 &&
expr.callee.body.body[0].type === "ReturnStatement" &&
expr.callee.body.body[0].argument &&
expr.callee.body.body[0].argument.type === "FunctionExpression") {
expr.callee.body.body[0].argument.type === "FunctionExpression"
) {
return {
fn: expr.callee.body.body[0].argument,
expressions: [],

View File

@ -8,15 +8,19 @@
const ConcatSource = require("webpack-sources").ConcatSource;
class NodeChunkTemplatePlugin {
apply(chunkTemplate) {
chunkTemplate.hooks.render.tap("NodeChunkTemplatePlugin", (modules, chunk) => {
const source = new ConcatSource();
source.add(`exports.ids = ${JSON.stringify(chunk.ids)};\nexports.modules = `);
source.add(modules);
source.add(";");
return source;
});
chunkTemplate.hooks.render.tap(
"NodeChunkTemplatePlugin",
(modules, chunk) => {
const source = new ConcatSource();
source.add(
`exports.ids = ${JSON.stringify(chunk.ids)};\nexports.modules = `
);
source.add(modules);
source.add(";");
return source;
}
);
chunkTemplate.hooks.hash.tap("NodeChunkTemplatePlugin", hash => {
hash.update("node");
hash.update("3");

View File

@ -11,13 +11,17 @@ const CachedInputFileSystem = require("enhanced-resolve/lib/CachedInputFileSyste
class NodeEnvironmentPlugin {
apply(compiler) {
compiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000);
compiler.inputFileSystem = new CachedInputFileSystem(
new NodeJsInputFileSystem(),
60000
);
const inputFileSystem = compiler.inputFileSystem;
compiler.outputFileSystem = new NodeOutputFileSystem();
compiler.watchFileSystem = new NodeWatchFileSystem(compiler.inputFileSystem);
compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", (compiler) => {
if(compiler.inputFileSystem === inputFileSystem)
inputFileSystem.purge();
compiler.watchFileSystem = new NodeWatchFileSystem(
compiler.inputFileSystem
);
compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => {
if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge();
});
}
}

View File

@ -7,21 +7,30 @@
const ConcatSource = require("webpack-sources").ConcatSource;
class NodeHotUpdateChunkTemplatePlugin {
apply(hotUpdateChunkTemplate) {
hotUpdateChunkTemplate.hooks.render.tap("NodeHotUpdateChunkTemplatePlugin", (modulesSource, modules, removedModules, hash, id) => {
const source = new ConcatSource();
source.add("exports.id = " + JSON.stringify(id) + ";\nexports.modules = ");
source.add(modulesSource);
source.add(";");
return source;
});
hotUpdateChunkTemplate.hooks.hash.tap("NodeHotUpdateChunkTemplatePlugin", hash => {
hash.update("NodeHotUpdateChunkTemplatePlugin");
hash.update("3");
hash.update(hotUpdateChunkTemplate.outputOptions.hotUpdateFunction + "");
hash.update(hotUpdateChunkTemplate.outputOptions.library + "");
});
hotUpdateChunkTemplate.hooks.render.tap(
"NodeHotUpdateChunkTemplatePlugin",
(modulesSource, modules, removedModules, hash, id) => {
const source = new ConcatSource();
source.add(
"exports.id = " + JSON.stringify(id) + ";\nexports.modules = "
);
source.add(modulesSource);
source.add(";");
return source;
}
);
hotUpdateChunkTemplate.hooks.hash.tap(
"NodeHotUpdateChunkTemplatePlugin",
hash => {
hash.update("NodeHotUpdateChunkTemplatePlugin");
hash.update("3");
hash.update(
hotUpdateChunkTemplate.outputOptions.hotUpdateFunction + ""
);
hash.update(hotUpdateChunkTemplate.outputOptions.library + "");
}
);
}
}
module.exports = NodeHotUpdateChunkTemplatePlugin;

View File

@ -4,21 +4,24 @@
*/
/*global installedChunks $hotChunkFilename$ hotAddUpdateChunk $hotMainFilename$ */
module.exports = function() {
function hotDownloadUpdateChunk(chunkId) { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotDownloadUpdateChunk(chunkId) {
var chunk = require("./" + $hotChunkFilename$);
hotAddUpdateChunk(chunk.id, chunk.modules);
}
function hotDownloadManifest() { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotDownloadManifest() {
try {
var update = require("./" + $hotMainFilename$);
} catch(e) {
} catch (e) {
return Promise.resolve();
}
return Promise.resolve(update);
}
function hotDisposeChunk(chunkId) { //eslint-disable-line no-unused-vars
//eslint-disable-next-line no-unused-vars
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
};

View File

@ -4,29 +4,32 @@
*/
/*global installedChunks $hotChunkFilename$ $require$ hotAddUpdateChunk $hotMainFilename$ */
module.exports = function() {
function hotDownloadUpdateChunk(chunkId) { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotDownloadUpdateChunk(chunkId) {
var filename = require("path").join(__dirname, $hotChunkFilename$);
require("fs").readFile(filename, "utf-8", function(err, content) {
if(err) {
if($require$.onError)
return $require$.oe(err);
else
throw err;
if (err) {
if ($require$.onError) return $require$.oe(err);
else throw err;
}
var chunk = {};
require("vm").runInThisContext("(function(exports) {" + content + "\n})", filename)(chunk);
require("vm").runInThisContext(
"(function(exports) {" + content + "\n})",
filename
)(chunk);
hotAddUpdateChunk(chunk.id, chunk.modules);
});
}
function hotDownloadManifest() { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotDownloadManifest() {
var filename = require("path").join(__dirname, $hotMainFilename$);
return new Promise(function(resolve, reject) {
require("fs").readFile(filename, "utf-8", function(err, content) {
if(err) return resolve();
if (err) return resolve();
try {
var update = JSON.parse(content);
} catch(e) {
} catch (e) {
return reject(e);
}
resolve(update);
@ -34,7 +37,8 @@ module.exports = function() {
});
}
function hotDisposeChunk(chunkId) { //eslint-disable-line no-unused-vars
//eslint-disable-next-line no-unused-vars
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
};

View File

@ -13,183 +13,256 @@ module.exports = class NodeMainTemplatePlugin {
apply(mainTemplate) {
const needChunkOnDemandLoadingCode = chunk => {
for(const chunkGroup of chunk.groupsIterable) {
if(chunkGroup.getNumberOfChildren() > 0) return true;
for (const chunkGroup of chunk.groupsIterable) {
if (chunkGroup.getNumberOfChildren() > 0) return true;
}
return false;
};
const asyncChunkLoading = this.asyncChunkLoading;
mainTemplate.hooks.localVars.tap("NodeMainTemplatePlugin", (source, chunk) => {
if(needChunkOnDemandLoadingCode(chunk)) {
return Template.asString([
source,
"",
"// object to store loaded chunks",
"// \"0\" means \"already loaded\"",
"var installedChunks = {",
Template.indent(chunk.ids.map((id) => `${id}: 0`).join(",\n")),
"};"
]);
mainTemplate.hooks.localVars.tap(
"NodeMainTemplatePlugin",
(source, chunk) => {
if (needChunkOnDemandLoadingCode(chunk)) {
return Template.asString([
source,
"",
"// object to store loaded chunks",
'// "0" means "already loaded"',
"var installedChunks = {",
Template.indent(chunk.ids.map(id => `${id}: 0`).join(",\n")),
"};"
]);
}
return source;
}
return source;
});
mainTemplate.hooks.requireExtensions.tap("NodeMainTemplatePlugin", (source, chunk) => {
if(needChunkOnDemandLoadingCode(chunk)) {
return Template.asString([
source,
"",
"// uncatched error handler for webpack runtime",
`${mainTemplate.requireFn}.oe = function(err) {`,
Template.indent([
"process.nextTick(function() {",
Template.indent("throw err; // catch this error by using import().catch()"),
"});"
]),
"};"
]);
}
return source;
});
mainTemplate.hooks.requireEnsure.tap("NodeMainTemplatePlugin", (source, chunk, hash) => {
const chunkFilename = mainTemplate.outputOptions.chunkFilename;
const chunkMaps = chunk.getChunkMaps();
const insertMoreModules = [
"var moreModules = chunk.modules, chunkIds = chunk.ids;",
"for(var moduleId in moreModules) {",
Template.indent(mainTemplate.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")),
"}"
];
if(asyncChunkLoading) {
return Template.asString([
source,
"",
"// ReadFile + VM.run chunk loading for javascript",
"",
"var installedChunkData = installedChunks[chunkId];",
"if(installedChunkData !== 0) { // 0 means \"already installed\".",
Template.indent([
"// array of [resolve, reject, promise] means \"currently loading\"",
"if(installedChunkData) {",
);
mainTemplate.hooks.requireExtensions.tap(
"NodeMainTemplatePlugin",
(source, chunk) => {
if (needChunkOnDemandLoadingCode(chunk)) {
return Template.asString([
source,
"",
"// uncatched error handler for webpack runtime",
`${mainTemplate.requireFn}.oe = function(err) {`,
Template.indent([
"promises.push(installedChunkData[2]);"
"process.nextTick(function() {",
Template.indent(
"throw err; // catch this error by using import().catch()"
),
"});"
]),
"} else {",
"};"
]);
}
return source;
}
);
mainTemplate.hooks.requireEnsure.tap(
"NodeMainTemplatePlugin",
(source, chunk, hash) => {
const chunkFilename = mainTemplate.outputOptions.chunkFilename;
const chunkMaps = chunk.getChunkMaps();
const insertMoreModules = [
"var moreModules = chunk.modules, chunkIds = chunk.ids;",
"for(var moduleId in moreModules) {",
Template.indent(
mainTemplate.renderAddModule(
hash,
chunk,
"moduleId",
"moreModules[moduleId]"
)
),
"}"
];
if (asyncChunkLoading) {
return Template.asString([
source,
"",
"// ReadFile + VM.run chunk loading for javascript",
"",
"var installedChunkData = installedChunks[chunkId];",
'if(installedChunkData !== 0) { // 0 means "already installed".',
Template.indent([
"// load the chunk and return promise to it",
"var promise = new Promise(function(resolve, reject) {",
'// array of [resolve, reject, promise] means "currently loading"',
"if(installedChunkData) {",
Template.indent(["promises.push(installedChunkData[2]);"]),
"} else {",
Template.indent([
"installedChunkData = installedChunks[chunkId] = [resolve, reject];",
"var filename = __dirname + " + mainTemplate.getAssetPath(JSON.stringify(`/${chunkFilename}`), {
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: (length) => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: "\" + chunkId + \"",
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength: (length) => {
const shortChunkHashMap = {};
for(const chunkId of Object.keys(chunkMaps.hash)) {
if(typeof chunkMaps.hash[chunkId] === "string")
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length);
}
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`
}
}) + ";",
"require('fs').readFile(filename, 'utf-8', function(err, content) {",
"// load the chunk and return promise to it",
"var promise = new Promise(function(resolve, reject) {",
Template.indent([
"if(err) return reject(err);",
"var chunk = {};",
"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
"(chunk, require, require('path').dirname(filename), filename);"
].concat(insertMoreModules).concat([
"var callbacks = [];",
"for(var i = 0; i < chunkIds.length; i++) {",
Template.indent([
"if(installedChunks[chunkIds[i]])",
Template.indent([
"callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);"
]),
"installedChunks[chunkIds[i]] = 0;"
]),
"}",
"for(i = 0; i < callbacks.length; i++)",
Template.indent("callbacks[i]();")
])),
"});"
"installedChunkData = installedChunks[chunkId] = [resolve, reject];",
"var filename = __dirname + " +
mainTemplate.getAssetPath(
JSON.stringify(`/${chunkFilename}`),
{
hash: `" + ${mainTemplate.renderCurrentHashCode(
hash
)} + "`,
hashWithLength: length =>
`" + ${mainTemplate.renderCurrentHashCode(
hash,
length
)} + "`,
chunk: {
id: '" + chunkId + "',
hash: `" + ${JSON.stringify(
chunkMaps.hash
)}[chunkId] + "`,
hashWithLength: length => {
const shortChunkHashMap = {};
for (const chunkId of Object.keys(chunkMaps.hash)) {
if (typeof chunkMaps.hash[chunkId] === "string")
shortChunkHashMap[chunkId] = chunkMaps.hash[
chunkId
].substr(0, length);
}
return `" + ${JSON.stringify(
shortChunkHashMap
)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(
chunkMaps.name
)}[chunkId]||chunkId) + "`
}
}
) +
";",
"require('fs').readFile(filename, 'utf-8', function(err, content) {",
Template.indent(
[
"if(err) return reject(err);",
"var chunk = {};",
"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
"(chunk, require, require('path').dirname(filename), filename);"
]
.concat(insertMoreModules)
.concat([
"var callbacks = [];",
"for(var i = 0; i < chunkIds.length; i++) {",
Template.indent([
"if(installedChunks[chunkIds[i]])",
Template.indent([
"callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);"
]),
"installedChunks[chunkIds[i]] = 0;"
]),
"}",
"for(i = 0; i < callbacks.length; i++)",
Template.indent("callbacks[i]();")
])
),
"});"
]),
"});",
"promises.push(installedChunkData[2] = promise);"
]),
"});",
"promises.push(installedChunkData[2] = promise);"
"}"
]),
"}"
]),
"}"
]);
} else {
const request = mainTemplate.getAssetPath(JSON.stringify(`./${chunkFilename}`), {
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: (length) => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: "\" + chunkId + \"",
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength: (length) => {
const shortChunkHashMap = {};
for(const chunkId of Object.keys(chunkMaps.hash)) {
if(typeof chunkMaps.hash[chunkId] === "string")
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length);
]);
} else {
const request = mainTemplate.getAssetPath(
JSON.stringify(`./${chunkFilename}`),
{
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: length =>
`" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: '" + chunkId + "',
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength: length => {
const shortChunkHashMap = {};
for (const chunkId of Object.keys(chunkMaps.hash)) {
if (typeof chunkMaps.hash[chunkId] === "string")
shortChunkHashMap[chunkId] = chunkMaps.hash[
chunkId
].substr(0, length);
}
return `" + ${JSON.stringify(
shortChunkHashMap
)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(
chunkMaps.name
)}[chunkId]||chunkId) + "`
}
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`
}
});
return Template.asString([
source,
"",
"// require() chunk loading for javascript",
"",
"// \"0\" is the signal for \"already loaded\"",
"if(installedChunks[chunkId] !== 0) {",
Template.indent([
`var chunk = require(${request});`
].concat(insertMoreModules).concat([
"for(var i = 0; i < chunkIds.length; i++)",
Template.indent("installedChunks[chunkIds[i]] = 0;")
])),
"}",
]);
}
});
mainTemplate.hooks.hotBootstrap.tap("NodeMainTemplatePlugin", (source, chunk, hash) => {
const hotUpdateChunkFilename = mainTemplate.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename = mainTemplate.outputOptions.hotUpdateMainFilename;
const chunkMaps = chunk.getChunkMaps();
const currentHotUpdateChunkFilename = mainTemplate.getAssetPath(JSON.stringify(hotUpdateChunkFilename), {
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: (length) => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: "\" + chunkId + \"",
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength: (length) => {
const shortChunkHashMap = {};
for(const chunkId of Object.keys(chunkMaps.hash)) {
if(typeof chunkMaps.hash[chunkId] === "string")
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length);
}
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`
);
return Template.asString([
source,
"",
"// require() chunk loading for javascript",
"",
'// "0" is the signal for "already loaded"',
"if(installedChunks[chunkId] !== 0) {",
Template.indent(
[`var chunk = require(${request});`]
.concat(insertMoreModules)
.concat([
"for(var i = 0; i < chunkIds.length; i++)",
Template.indent("installedChunks[chunkIds[i]] = 0;")
])
),
"}"
]);
}
});
const currentHotUpdateMainFilename = mainTemplate.getAssetPath(JSON.stringify(hotUpdateMainFilename), {
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: (length) => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`
});
return Template.getFunctionContent(asyncChunkLoading ? require("./NodeMainTemplateAsync.runtime.js") : require("./NodeMainTemplate.runtime.js"))
.replace(/\$require\$/g, mainTemplate.requireFn)
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename);
});
}
);
mainTemplate.hooks.hotBootstrap.tap(
"NodeMainTemplatePlugin",
(source, chunk, hash) => {
const hotUpdateChunkFilename =
mainTemplate.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename =
mainTemplate.outputOptions.hotUpdateMainFilename;
const chunkMaps = chunk.getChunkMaps();
const currentHotUpdateChunkFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateChunkFilename),
{
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: length =>
`" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: '" + chunkId + "',
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength: length => {
const shortChunkHashMap = {};
for (const chunkId of Object.keys(chunkMaps.hash)) {
if (typeof chunkMaps.hash[chunkId] === "string")
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(
0,
length
);
}
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(
chunkMaps.name
)}[chunkId]||chunkId) + "`
}
}
);
const currentHotUpdateMainFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateMainFilename),
{
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: length =>
`" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`
}
);
return Template.getFunctionContent(
asyncChunkLoading
? require("./NodeMainTemplateAsync.runtime.js")
: require("./NodeMainTemplate.runtime.js")
)
.replace(/\$require\$/g, mainTemplate.requireFn)
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename);
}
);
mainTemplate.hooks.hash.tap("NodeMainTemplatePlugin", hash => {
hash.update("node");
hash.update("3");

View File

@ -13,16 +13,20 @@ module.exports = class NodeSourcePlugin {
}
apply(compiler) {
const options = this.options;
if(options === false) // allow single kill switch to turn off this plugin
if (options === false)
// allow single kill switch to turn off this plugin
return;
const getPathToModule = (module, type) => {
if(type === true || (type === undefined && nodeLibsBrowser[module])) {
if(!nodeLibsBrowser[module]) throw new Error(`No browser version for node.js core module ${module} available`);
if (type === true || (type === undefined && nodeLibsBrowser[module])) {
if (!nodeLibsBrowser[module])
throw new Error(
`No browser version for node.js core module ${module} available`
);
return nodeLibsBrowser[module];
} else if(type === "mock") {
} else if (type === "mock") {
return require.resolve(`node-libs-browser/mock/${module}`);
} else if(type === "empty") {
} else if (type === "empty") {
return require.resolve("node-libs-browser/mock/empty");
} else return module;
};
@ -30,61 +34,105 @@ module.exports = class NodeSourcePlugin {
const addExpression = (parser, name, module, type, suffix) => {
suffix = suffix || "";
parser.hooks.expression.for(name).tap("NodeSourcePlugin", () => {
if(parser.state.module && parser.state.module.resource === getPathToModule(module, type)) return;
const mockModule = ParserHelpers.requireFileAsExpression(parser.state.module.context, getPathToModule(module, type));
return ParserHelpers.addParsedVariableToModule(parser, name, mockModule + suffix);
if (
parser.state.module &&
parser.state.module.resource === getPathToModule(module, type)
)
return;
const mockModule = ParserHelpers.requireFileAsExpression(
parser.state.module.context,
getPathToModule(module, type)
);
return ParserHelpers.addParsedVariableToModule(
parser,
name,
mockModule + suffix
);
});
};
compiler.hooks.compilation.tap("NodeSourcePlugin", (compilation, {
normalModuleFactory
}) => {
const handler = (parser, parserOptions) => {
if(parserOptions.node === false)
return;
compiler.hooks.compilation.tap(
"NodeSourcePlugin",
(compilation, { normalModuleFactory }) => {
const handler = (parser, parserOptions) => {
if (parserOptions.node === false) return;
let localOptions = options;
if(parserOptions.node)
localOptions = Object.assign({}, localOptions, parserOptions.node);
let localOptions = options;
if (parserOptions.node)
localOptions = Object.assign({}, localOptions, parserOptions.node);
if(localOptions.global) {
parser.hooks.expression.for("global").tap("NodeSourcePlugin", () => {
const retrieveGlobalModule = ParserHelpers.requireFileAsExpression(parser.state.module.context, require.resolve("../../buildin/global.js"));
return ParserHelpers.addParsedVariableToModule(parser, "global", retrieveGlobalModule);
});
}
if(localOptions.process) {
const processType = localOptions.process;
addExpression(parser, "process", "process", processType);
}
if(localOptions.console) {
const consoleType = localOptions.console;
addExpression(parser, "console", "console", consoleType);
}
const bufferType = localOptions.Buffer;
if(bufferType) {
addExpression(parser, "Buffer", "buffer", bufferType, ".Buffer");
}
if(localOptions.setImmediate) {
const setImmediateType = localOptions.setImmediate;
addExpression(parser, "setImmediate", "timers", setImmediateType, ".setImmediate");
addExpression(parser, "clearImmediate", "timers", setImmediateType, ".clearImmediate");
}
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("NodeSourcePlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("NodeSourcePlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/esm").tap("NodeSourcePlugin", handler);
});
compiler.hooks.afterResolvers.tap("NodeSourcePlugin", (compiler) => {
for(const lib of Object.keys(nodeLibsBrowser)) {
if(options[lib] !== false) {
compiler.resolverFactory.hooks.resolver.for("normal").tap("NodeSourcePlugin", resolver => {
new AliasPlugin("described-resolve", {
name: lib,
onlyModule: true,
alias: getPathToModule(lib, options[lib])
}, "resolve").apply(resolver);
});
if (localOptions.global) {
parser.hooks.expression
.for("global")
.tap("NodeSourcePlugin", () => {
const retrieveGlobalModule = ParserHelpers.requireFileAsExpression(
parser.state.module.context,
require.resolve("../../buildin/global.js")
);
return ParserHelpers.addParsedVariableToModule(
parser,
"global",
retrieveGlobalModule
);
});
}
if (localOptions.process) {
const processType = localOptions.process;
addExpression(parser, "process", "process", processType);
}
if (localOptions.console) {
const consoleType = localOptions.console;
addExpression(parser, "console", "console", consoleType);
}
const bufferType = localOptions.Buffer;
if (bufferType) {
addExpression(parser, "Buffer", "buffer", bufferType, ".Buffer");
}
if (localOptions.setImmediate) {
const setImmediateType = localOptions.setImmediate;
addExpression(
parser,
"setImmediate",
"timers",
setImmediateType,
".setImmediate"
);
addExpression(
parser,
"clearImmediate",
"timers",
setImmediateType,
".clearImmediate"
);
}
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("NodeSourcePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("NodeSourcePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("NodeSourcePlugin", handler);
}
);
compiler.hooks.afterResolvers.tap("NodeSourcePlugin", compiler => {
for (const lib of Object.keys(nodeLibsBrowser)) {
if (options[lib] !== false) {
compiler.resolverFactory.hooks.resolver
.for("normal")
.tap("NodeSourcePlugin", resolver => {
new AliasPlugin(
"described-resolve",
{
name: lib,
onlyModule: true,
alias: getPathToModule(lib, options[lib])
},
"resolve"
).apply(resolver);
});
}
}
});

View File

@ -6,7 +6,8 @@
const ExternalsPlugin = require("../ExternalsPlugin");
const builtins = require("module").builtinModules || Object.keys(process.binding("natives"));
const builtins =
require("module").builtinModules || Object.keys(process.binding("natives"));
class NodeTargetPlugin {
apply(compiler) {

View File

@ -16,10 +16,14 @@ class NodeTemplatePlugin {
}
apply(compiler) {
compiler.hooks.thisCompilation.tap("NodeTemplatePlugin", (compilation) => {
new NodeMainTemplatePlugin(this.asyncChunkLoading).apply(compilation.mainTemplate);
compiler.hooks.thisCompilation.tap("NodeTemplatePlugin", compilation => {
new NodeMainTemplatePlugin(this.asyncChunkLoading).apply(
compilation.mainTemplate
);
new NodeChunkTemplatePlugin().apply(compilation.chunkTemplate);
new NodeHotUpdateChunkTemplatePlugin().apply(compilation.hotUpdateChunkTemplate);
new NodeHotUpdateChunkTemplatePlugin().apply(
compilation.hotUpdateChunkTemplate
);
});
}
}

View File

@ -17,66 +17,63 @@ class NodeWatchFileSystem {
}
watch(files, dirs, missing, startTime, options, callback, callbackUndelayed) {
if(!Array.isArray(files))
throw new Error("Invalid arguments: 'files'");
if(!Array.isArray(dirs))
throw new Error("Invalid arguments: 'dirs'");
if(!Array.isArray(missing))
if (!Array.isArray(files)) throw new Error("Invalid arguments: 'files'");
if (!Array.isArray(dirs)) throw new Error("Invalid arguments: 'dirs'");
if (!Array.isArray(missing))
throw new Error("Invalid arguments: 'missing'");
if(typeof callback !== "function")
if (typeof callback !== "function")
throw new Error("Invalid arguments: 'callback'");
if(typeof startTime !== "number" && startTime)
if (typeof startTime !== "number" && startTime)
throw new Error("Invalid arguments: 'startTime'");
if(typeof options !== "object")
if (typeof options !== "object")
throw new Error("Invalid arguments: 'options'");
if(typeof callbackUndelayed !== "function" && callbackUndelayed)
if (typeof callbackUndelayed !== "function" && callbackUndelayed)
throw new Error("Invalid arguments: 'callbackUndelayed'");
const oldWatcher = this.watcher;
this.watcher = new Watchpack(options);
if(callbackUndelayed)
this.watcher.once("change", callbackUndelayed);
if (callbackUndelayed) this.watcher.once("change", callbackUndelayed);
this.watcher.once("aggregated", (changes, removals) => {
changes = changes.concat(removals);
if(this.inputFileSystem && this.inputFileSystem.purge) {
if (this.inputFileSystem && this.inputFileSystem.purge) {
this.inputFileSystem.purge(changes);
}
const times = objectToMap(this.watcher.getTimes());
callback(null,
callback(
null,
changes.filter(file => files.includes(file)).sort(),
changes.filter(file => dirs.includes(file)).sort(),
changes.filter(file => missing.includes(file)).sort(), times, times);
changes.filter(file => missing.includes(file)).sort(),
times,
times
);
});
this.watcher.watch(files.concat(missing), dirs.concat(missing), startTime);
if(oldWatcher) {
if (oldWatcher) {
oldWatcher.close();
}
return {
close: () => {
if(this.watcher) {
if (this.watcher) {
this.watcher.close();
this.watcher = null;
}
},
pause: () => {
if(this.watcher) {
if (this.watcher) {
this.watcher.pause();
}
},
getFileTimestamps: () => {
if(this.watcher)
return objectToMap(this.watcher.getTimes());
else
return new Map();
if (this.watcher) return objectToMap(this.watcher.getTimes());
else return new Map();
},
getContextTimestamps: () => {
if(this.watcher)
return objectToMap(this.watcher.getTimes());
else
return new Map();
if (this.watcher) return objectToMap(this.watcher.getTimes());
else return new Map();
}
};
}

View File

@ -7,79 +7,109 @@
const Template = require("../Template");
class ReadFileCompileWasmMainTemplatePlugin {
apply(mainTemplate) {
mainTemplate.hooks.localVars.tap("ReadFileCompileWasmMainTemplatePlugin", (source, chunk) => {
return Template.asString([
source,
"",
"// object to store loaded and loading wasm modules",
"var installedWasmModules = {};",
]);
});
mainTemplate.hooks.requireEnsure.tap("ReadFileCompileWasmMainTemplatePlugin", (source, chunk, hash) => {
const webassemblyModuleFilename = mainTemplate.outputOptions.webassemblyModuleFilename;
const chunkModuleMaps = chunk.getChunkModuleMaps(m => m.type.startsWith("webassembly"));
if(Object.keys(chunkModuleMaps.id).length === 0) return source;
const wasmModuleSrcPath = mainTemplate.getAssetPath(JSON.stringify(webassemblyModuleFilename), {
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
module: {
id: "\" + wasmModuleId + \"",
hash: `" + ${JSON.stringify(chunkModuleMaps.hash)}[wasmModuleId] + "`,
hashWithLength(length) {
const shortChunkHashMap = Object.create(null);
for(const wasmModuleId of Object.keys(chunkModuleMaps.hash)) {
if(typeof chunkModuleMaps.hash[wasmModuleId] === "string")
shortChunkHashMap[wasmModuleId] = chunkModuleMaps.hash[wasmModuleId].substr(0, length);
}
return `" + ${JSON.stringify(shortChunkHashMap)}[wasmModuleId] + "`;
}
}
});
return Template.asString([
source,
"",
"// ReadFile + compile chunk loading for webassembly",
"",
`var wasmModules = ${JSON.stringify(chunkModuleMaps.id)}[chunkId] || [];`,
"",
"wasmModules.forEach(function(wasmModuleId) {",
Template.indent([
"var installedWasmModuleData = installedWasmModules[wasmModuleId];",
mainTemplate.hooks.localVars.tap(
"ReadFileCompileWasmMainTemplatePlugin",
(source, chunk) => {
return Template.asString([
source,
"",
"// a Promise means \"currently loading\" or \"already loaded\".",
"promises.push(installedWasmModuleData ||",
"// object to store loaded and loading wasm modules",
"var installedWasmModules = {};"
]);
}
);
mainTemplate.hooks.requireEnsure.tap(
"ReadFileCompileWasmMainTemplatePlugin",
(source, chunk, hash) => {
const webassemblyModuleFilename =
mainTemplate.outputOptions.webassemblyModuleFilename;
const chunkModuleMaps = chunk.getChunkModuleMaps(m =>
m.type.startsWith("webassembly")
);
if (Object.keys(chunkModuleMaps.id).length === 0) return source;
const wasmModuleSrcPath = mainTemplate.getAssetPath(
JSON.stringify(webassemblyModuleFilename),
{
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
hashWithLength: length =>
`" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
module: {
id: '" + wasmModuleId + "',
hash: `" + ${JSON.stringify(
chunkModuleMaps.hash
)}[wasmModuleId] + "`,
hashWithLength(length) {
const shortChunkHashMap = Object.create(null);
for (const wasmModuleId of Object.keys(chunkModuleMaps.hash)) {
if (typeof chunkModuleMaps.hash[wasmModuleId] === "string")
shortChunkHashMap[wasmModuleId] = chunkModuleMaps.hash[
wasmModuleId
].substr(0, length);
}
return `" + ${JSON.stringify(
shortChunkHashMap
)}[wasmModuleId] + "`;
}
}
}
);
return Template.asString([
source,
"",
"// ReadFile + compile chunk loading for webassembly",
"",
`var wasmModules = ${JSON.stringify(
chunkModuleMaps.id
)}[chunkId] || [];`,
"",
"wasmModules.forEach(function(wasmModuleId) {",
Template.indent([
"(installedWasmModules[wasmModuleId] = new Promise(function(resolve, reject) {",
"var installedWasmModuleData = installedWasmModules[wasmModuleId];",
"",
'// a Promise means "currently loading" or "already loaded".',
"promises.push(installedWasmModuleData ||",
Template.indent([
`require('fs').readFile(require('path').resolve(__dirname, ${wasmModuleSrcPath}), function(err, buffer) {`,
"(installedWasmModules[wasmModuleId] = new Promise(function(resolve, reject) {",
Template.indent([
"if(err) return reject(err);",
"resolve(WebAssembly.compile(buffer));"
`require('fs').readFile(require('path').resolve(__dirname, ${
wasmModuleSrcPath
}), function(err, buffer) {`,
Template.indent([
"if(err) return reject(err);",
"resolve(WebAssembly.compile(buffer));"
]),
"});"
]),
"});"
`}).then(function(module) { ${
mainTemplate.requireFn
}.w[wasmModuleId] = module; }))`
]),
`}).then(function(module) { ${mainTemplate.requireFn}.w[wasmModuleId] = module; }))`
");"
]),
");",
]),
"});",
]);
});
mainTemplate.hooks.requireExtensions.tap("ReadFileCompileWasmMainTemplatePlugin", (source, chunk) => {
return Template.asString([
source,
"",
"// object with all compiled WebAssmbly.Modules",
`${mainTemplate.requireFn}.w = {};`
]);
});
mainTemplate.hooks.hash.tap("ReadFileCompileWasmMainTemplatePlugin", hash => {
hash.update("ReadFileCompileWasmMainTemplatePlugin");
hash.update("1");
hash.update(`${mainTemplate.outputOptions.webassemblyModuleFilename}`);
});
"});"
]);
}
);
mainTemplate.hooks.requireExtensions.tap(
"ReadFileCompileWasmMainTemplatePlugin",
(source, chunk) => {
return Template.asString([
source,
"",
"// object with all compiled WebAssmbly.Modules",
`${mainTemplate.requireFn}.w = {};`
]);
}
);
mainTemplate.hooks.hash.tap(
"ReadFileCompileWasmMainTemplatePlugin",
hash => {
hash.update("ReadFileCompileWasmMainTemplatePlugin");
hash.update("1");
hash.update(`${mainTemplate.outputOptions.webassemblyModuleFilename}`);
}
);
}
}
module.exports = ReadFileCompileWasmMainTemplatePlugin;

View File

@ -9,10 +9,17 @@ const WasmModuleTemplatePlugin = require("../wasm/WasmModuleTemplatePlugin");
class ReadFileCompileWasmTemplatePlugin {
apply(compiler) {
compiler.hooks.thisCompilation.tap("ReadFileCompileWasmTemplatePlugin", (compilation) => {
new ReadFileCompileWasmMainTemplatePlugin().apply(compilation.mainTemplate);
new WasmModuleTemplatePlugin().apply(compilation.moduleTemplates.javascript);
});
compiler.hooks.thisCompilation.tap(
"ReadFileCompileWasmTemplatePlugin",
compilation => {
new ReadFileCompileWasmMainTemplatePlugin().apply(
compilation.mainTemplate
);
new WasmModuleTemplatePlugin().apply(
compilation.moduleTemplates.javascript
);
}
);
}
}

View File

@ -6,8 +6,13 @@
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/");
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 || {};
}
@ -16,60 +21,66 @@ class AggressiveMergingPlugin {
const options = this.options;
const minSizeReduce = options.minSizeReduce || 1.5;
compiler.hooks.thisCompilation.tap("AggressiveMergingPlugin", (compilation) => {
compilation.hooks.optimizeChunksAdvanced.tap("AggressiveMergingPlugin", (chunks) => {
let combinations = [];
chunks.forEach((a, idx) => {
if(a.canBeInitial()) return;
for(let i = 0; i < idx; i++) {
const b = chunks[i];
if(b.canBeInitial()) continue;
combinations.push({
a,
b,
improvement: undefined
compiler.hooks.thisCompilation.tap(
"AggressiveMergingPlugin",
compilation => {
compilation.hooks.optimizeChunksAdvanced.tap(
"AggressiveMergingPlugin",
chunks => {
let combinations = [];
chunks.forEach((a, idx) => {
if (a.canBeInitial()) return;
for (let i = 0; i < idx; i++) {
const b = chunks[i];
if (b.canBeInitial()) continue;
combinations.push({
a,
b,
improvement: undefined
});
}
});
for (const pair of combinations) {
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 {
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 (pair.b.integrate(pair.a, "aggressive-merge")) {
chunks.splice(chunks.indexOf(pair.a), 1);
return true;
}
}
});
for(const pair of combinations) {
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 {
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(pair.b.integrate(pair.a, "aggressive-merge")) {
chunks.splice(chunks.indexOf(pair.a), 1);
return true;
}
});
});
);
}
);
}
}

View File

@ -26,219 +26,256 @@ class AggressiveSplittingPlugin {
validateOptions(schema, options || {}, "Aggressive Splitting Plugin");
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;
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.hooks.thisCompilation.tap("AggressiveSplittingPlugin", (compilation) => {
let needAdditionalSeal = false;
let newSplits;
let fromAggressiveSplittingSet;
let chunkSplitDataMap;
compilation.hooks.optimize.tap("AggressiveSplittingPlugin", () => {
newSplits = [];
fromAggressiveSplittingSet = new Set();
chunkSplitDataMap = new Map();
});
compilation.hooks.optimizeChunksAdvanced.tap("AggressiveSplittingPlugin", (chunks) => {
// Precompute stuff
const nameToModuleMap = new Map();
const moduleToNameMap = new Map();
for(const m of compilation.modules) {
const name = identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache);
nameToModuleMap.set(name, m);
moduleToNameMap.set(m, name);
}
// Check used chunk ids
const usedIds = new Set();
for(const chunk of chunks) {
usedIds.add(chunk.id);
}
const recordedSplits = compilation.records && compilation.records.aggressiveSplits || [];
const usedSplits = newSplits ? recordedSplits.concat(newSplits) : recordedSplits;
const minSize = this.options.minSize;
const maxSize = this.options.maxSize;
const applySplit = (splitData) => {
// Cannot split if id is already taken
if(splitData.id !== undefined && usedIds.has(splitData.id)) return false;
// Get module objects from names
const selectedModules = splitData.modules.map(name => nameToModuleMap.get(name));
// Does the modules exist at all?
if(!selectedModules.every(Boolean)) return false;
// Check if size matches (faster than waiting for hash)
const size = selectedModules.reduce((sum, m) => sum + m.size(), 0);
if(size !== splitData.size) return false;
// get chunks with all modules
const selectedChunks = intersect(selectedModules.map(m => new Set(m.chunksIterable)));
// No relevant chunks found
if(selectedChunks.size === 0) return false;
// The found chunk is already the split or similar
if(selectedChunks.size === 1 && Array.from(selectedChunks)[0].getNumberOfModules() === selectedModules.length) {
const chunk = Array.from(selectedChunks)[0];
if(fromAggressiveSplittingSet.has(chunk)) return false;
fromAggressiveSplittingSet.add(chunk);
chunkSplitDataMap.set(chunk, splitData);
return true;
}
// split the chunk into two parts
const newChunk = compilation.addChunk();
newChunk.chunkReason = "aggressive splitted";
for(const chunk of selectedChunks) {
selectedModules.forEach(moveModuleBetween(chunk, newChunk));
chunk.split(newChunk);
chunk.name = null;
}
fromAggressiveSplittingSet.add(newChunk);
chunkSplitDataMap.set(newChunk, splitData);
if(splitData.id !== null && splitData.id !== undefined) {
newChunk.id = splitData.id;
}
return true;
};
// try to restore to recorded splitting
let changed = false;
for(let j = 0; j < usedSplits.length; j++) {
const splitData = usedSplits[j];
if(applySplit(splitData))
changed = true;
}
// for any chunk which isn't splitted yet, split it and create a new entry
// start with the biggest chunk
const sortedChunks = chunks.slice().sort((a, b) => {
const diff1 = b.modulesSize() - a.modulesSize();
if(diff1) return diff1;
const diff2 = a.getNumberOfModules() - b.getNumberOfModules();
if(diff2) return diff2;
const modulesA = Array.from(a.modulesIterable);
const modulesB = Array.from(b.modulesIterable);
modulesA.sort();
modulesB.sort();
const aI = modulesA[Symbol.iterator]();
const bI = modulesB[Symbol.iterator]();
while(true) { // eslint-disable-line
const aItem = aI.next();
const bItem = bI.next();
if(aItem.done) return 0;
const aModuleIdentifier = aItem.value.identifier();
const bModuleIdentifier = bItem.value.identifier();
if(aModuleIdentifier > bModuleIdentifier) return -1;
if(aModuleIdentifier < bModuleIdentifier) return 1;
}
compiler.hooks.thisCompilation.tap(
"AggressiveSplittingPlugin",
compilation => {
let needAdditionalSeal = false;
let newSplits;
let fromAggressiveSplittingSet;
let chunkSplitDataMap;
compilation.hooks.optimize.tap("AggressiveSplittingPlugin", () => {
newSplits = [];
fromAggressiveSplittingSet = new Set();
chunkSplitDataMap = new Map();
});
for(const chunk of sortedChunks) {
if(fromAggressiveSplittingSet.has(chunk)) continue;
const size = chunk.modulesSize();
if(size > maxSize && chunk.getNumberOfModules() > 1) {
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;
});
const selectedModules = [];
let selectedModulesSize = 0;
for(let k = 0; k < modules.length; k++) {
const module = modules[k];
const newSize = selectedModulesSize + module.size();
if(newSize > maxSize && selectedModulesSize >= minSize) break;
selectedModulesSize = newSize;
selectedModules.push(module);
compilation.hooks.optimizeChunksAdvanced.tap(
"AggressiveSplittingPlugin",
chunks => {
// Precompute stuff
const nameToModuleMap = new Map();
const moduleToNameMap = new Map();
for (const m of compilation.modules) {
const name = identifierUtils.makePathsRelative(
compiler.context,
m.identifier(),
compilation.cache
);
nameToModuleMap.set(name, m);
moduleToNameMap.set(m, name);
}
if(selectedModules.length === 0) continue;
const splitData = {
modules: selectedModules.map(m => moduleToNameMap.get(m)).sort(),
size: selectedModulesSize
// Check used chunk ids
const usedIds = new Set();
for (const chunk of chunks) {
usedIds.add(chunk.id);
}
const recordedSplits =
(compilation.records && compilation.records.aggressiveSplits) ||
[];
const usedSplits = newSplits
? recordedSplits.concat(newSplits)
: recordedSplits;
const minSize = this.options.minSize;
const maxSize = this.options.maxSize;
const applySplit = splitData => {
// Cannot split if id is already taken
if (splitData.id !== undefined && usedIds.has(splitData.id))
return false;
// Get module objects from names
const selectedModules = splitData.modules.map(name =>
nameToModuleMap.get(name)
);
// Does the modules exist at all?
if (!selectedModules.every(Boolean)) return false;
// Check if size matches (faster than waiting for hash)
const size = selectedModules.reduce(
(sum, m) => sum + m.size(),
0
);
if (size !== splitData.size) return false;
// get chunks with all modules
const selectedChunks = intersect(
selectedModules.map(m => new Set(m.chunksIterable))
);
// No relevant chunks found
if (selectedChunks.size === 0) return false;
// The found chunk is already the split or similar
if (
selectedChunks.size === 1 &&
Array.from(selectedChunks)[0].getNumberOfModules() ===
selectedModules.length
) {
const chunk = Array.from(selectedChunks)[0];
if (fromAggressiveSplittingSet.has(chunk)) return false;
fromAggressiveSplittingSet.add(chunk);
chunkSplitDataMap.set(chunk, splitData);
return true;
}
// split the chunk into two parts
const newChunk = compilation.addChunk();
newChunk.chunkReason = "aggressive splitted";
for (const chunk of selectedChunks) {
selectedModules.forEach(moveModuleBetween(chunk, newChunk));
chunk.split(newChunk);
chunk.name = null;
}
fromAggressiveSplittingSet.add(newChunk);
chunkSplitDataMap.set(newChunk, splitData);
if (splitData.id !== null && splitData.id !== undefined) {
newChunk.id = splitData.id;
}
return true;
};
if(applySplit(splitData)) {
newSplits = (newSplits || []).concat(splitData);
changed = true;
// try to restore to recorded splitting
let changed = false;
for (let j = 0; j < usedSplits.length; j++) {
const splitData = usedSplits[j];
if (applySplit(splitData)) changed = true;
}
// for any chunk which isn't splitted yet, split it and create a new entry
// start with the biggest chunk
const sortedChunks = chunks.slice().sort((a, b) => {
const diff1 = b.modulesSize() - a.modulesSize();
if (diff1) return diff1;
const diff2 = a.getNumberOfModules() - b.getNumberOfModules();
if (diff2) return diff2;
const modulesA = Array.from(a.modulesIterable);
const modulesB = Array.from(b.modulesIterable);
modulesA.sort();
modulesB.sort();
const aI = modulesA[Symbol.iterator]();
const bI = modulesB[Symbol.iterator]();
// eslint-disable-next-line no-constant-condition
while (true) {
const aItem = aI.next();
const bItem = bI.next();
if (aItem.done) return 0;
const aModuleIdentifier = aItem.value.identifier();
const bModuleIdentifier = bItem.value.identifier();
if (aModuleIdentifier > bModuleIdentifier) return -1;
if (aModuleIdentifier < bModuleIdentifier) return 1;
}
});
for (const chunk of sortedChunks) {
if (fromAggressiveSplittingSet.has(chunk)) continue;
const size = chunk.modulesSize();
if (size > maxSize && chunk.getNumberOfModules() > 1) {
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;
});
const selectedModules = [];
let selectedModulesSize = 0;
for (let k = 0; k < modules.length; k++) {
const module = modules[k];
const newSize = selectedModulesSize + module.size();
if (newSize > maxSize && selectedModulesSize >= minSize)
break;
selectedModulesSize = newSize;
selectedModules.push(module);
}
if (selectedModules.length === 0) continue;
const splitData = {
modules: selectedModules
.map(m => moduleToNameMap.get(m))
.sort(),
size: selectedModulesSize
};
if (applySplit(splitData)) {
newSplits = (newSplits || []).concat(splitData);
changed = true;
}
}
}
if (changed) return true;
}
);
compilation.hooks.recordHash.tap(
"AggressiveSplittingPlugin",
records => {
// 4. save made splittings to records
const allSplits = new Set();
const invalidSplits = new Set();
// Check if some splittings are invalid
// We remove invalid splittings and try again
for (const chunk of compilation.chunks) {
const splitData = chunkSplitDataMap.get(chunk);
if (splitData !== undefined) {
if (splitData.hash && chunk.hash !== splitData.hash) {
// Split was successful, but hash doesn't equal
// We can throw away the split since it's useless now
invalidSplits.add(splitData);
}
}
}
if (invalidSplits.size > 0) {
records.aggressiveSplits = records.aggressiveSplits.filter(
splitData => !invalidSplits.has(splitData)
);
needAdditionalSeal = true;
} else {
// set hash and id values on all (new) splittings
for (const chunk of compilation.chunks) {
const splitData = chunkSplitDataMap.get(chunk);
if (splitData !== undefined) {
splitData.hash = chunk.hash;
splitData.id = chunk.id;
allSplits.add(splitData);
// set flag for stats
chunk.recorded = true;
}
}
// Also add all unused historial splits (after the used ones)
// They can still be used in some future compilation
const recordedSplits =
compilation.records && compilation.records.aggressiveSplits;
if (recordedSplits) {
for (const splitData of recordedSplits) {
if (!invalidSplits.has(splitData)) allSplits.add(splitData);
}
}
// record all splits
records.aggressiveSplits = Array.from(allSplits);
needAdditionalSeal = false;
}
}
}
if(changed)
return true;
});
compilation.hooks.recordHash.tap("AggressiveSplittingPlugin", (records) => {
// 4. save made splittings to records
const allSplits = new Set();
const invalidSplits = new Set();
// Check if some splittings are invalid
// We remove invalid splittings and try again
for(const chunk of compilation.chunks) {
const splitData = chunkSplitDataMap.get(chunk);
if(splitData !== undefined) {
if(splitData.hash && chunk.hash !== splitData.hash) {
// Split was successful, but hash doesn't equal
// We can throw away the split since it's useless now
invalidSplits.add(splitData);
);
compilation.hooks.needAdditionalSeal.tap(
"AggressiveSplittingPlugin",
() => {
if (needAdditionalSeal) {
needAdditionalSeal = false;
return true;
}
}
}
if(invalidSplits.size > 0) {
records.aggressiveSplits = records.aggressiveSplits.filter(splitData => !invalidSplits.has(splitData));
needAdditionalSeal = true;
} else {
// set hash and id values on all (new) splittings
for(const chunk of compilation.chunks) {
const splitData = chunkSplitDataMap.get(chunk);
if(splitData !== undefined) {
splitData.hash = chunk.hash;
splitData.id = chunk.id;
allSplits.add(splitData);
// set flag for stats
chunk.recorded = true;
}
}
// Also add all unused historial splits (after the used ones)
// They can still be used in some future compilation
const recordedSplits = compilation.records && compilation.records.aggressiveSplits;
if(recordedSplits) {
for(const splitData of recordedSplits) {
if(!invalidSplits.has(splitData))
allSplits.add(splitData);
}
}
// record all splits
records.aggressiveSplits = Array.from(allSplits);
needAdditionalSeal = false;
}
});
compilation.hooks.needAdditionalSeal.tap("AggressiveSplittingPlugin", () => {
if(needAdditionalSeal) {
needAdditionalSeal = false;
return true;
}
});
});
);
}
);
}
}
module.exports = AggressiveSplittingPlugin;

View File

@ -9,15 +9,20 @@ class ChunkModuleIdRangePlugin {
}
apply(compiler) {
const options = this.options;
compiler.hooks.compilation.tap("ChunkModuleIdRangePlugin", (compilation) => {
compilation.hooks.moduleIds.tap("ChunkModuleIdRangePlugin", (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");
compiler.hooks.compilation.tap("ChunkModuleIdRangePlugin", compilation => {
compilation.hooks.moduleIds.tap("ChunkModuleIdRangePlugin", 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) {
if (options.order) {
chunkModules = chunk.modules.slice();
switch(options.order) {
switch (options.order) {
case "index":
chunkModules.sort((a, b) => {
return a.index - b.index;
@ -29,22 +34,22 @@ class ChunkModuleIdRangePlugin {
});
break;
default:
throw new Error("ChunkModuleIdRangePlugin: unexpected value of order");
throw new Error(
"ChunkModuleIdRangePlugin: unexpected value of order"
);
}
} else {
chunkModules = modules.filter((m) => {
chunkModules = modules.filter(m => {
return m.chunks.includes(chunk);
});
}
for(let i = 0; i < chunkModules.length; i++) {
for (let i = 0; i < chunkModules.length; i++) {
const m = chunkModules[i];
if(m.id === null) {
if (m.id === null) {
m.id = currentId++;
}
if(options.end && currentId > options.end)
break;
if (options.end && currentId > options.end) break;
}
});
});

File diff suppressed because it is too large Load Diff

View File

@ -7,54 +7,64 @@
const GraphHelpers = require("../GraphHelpers");
class EnsureChunkConditionsPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("EnsureChunkConditionsPlugin", (compilation) => {
const handler = (chunks) => {
let changed = false;
for(const module of compilation.modules) {
if(!module.chunkCondition) continue;
const sourceChunks = new Set();
const chunkGroups = new Set();
for(const chunk of module.chunksIterable) {
if(!module.chunkCondition(chunk)) {
sourceChunks.add(chunk);
for(const group of chunk.groupsIterable) {
compiler.hooks.compilation.tap(
"EnsureChunkConditionsPlugin",
compilation => {
const handler = chunks => {
let changed = false;
for (const module of compilation.modules) {
if (!module.chunkCondition) continue;
const sourceChunks = new Set();
const chunkGroups = new Set();
for (const chunk of module.chunksIterable) {
if (!module.chunkCondition(chunk)) {
sourceChunks.add(chunk);
for (const group of chunk.groupsIterable) {
chunkGroups.add(group);
}
}
}
if (sourceChunks.size === 0) continue;
const targetChunks = new Set();
chunkGroupLoop: for (const chunkGroup of chunkGroups) {
// Can module be placed in a chunk of this group?
for (const chunk of chunkGroup.chunks) {
if (module.chunkCondition(chunk)) {
targetChunks.add(chunk);
continue chunkGroupLoop;
}
}
// We reached the entrypoint: fail
if (chunkGroup.isInitial()) {
throw new Error(
"Cannot fullfill chunk condition of " + module.identifier()
);
}
// Try placing in all parents
for (const group of chunkGroup.parentsIterable) {
chunkGroups.add(group);
}
}
}
if(sourceChunks.size === 0) continue;
const targetChunks = new Set();
chunkGroupLoop: for(const chunkGroup of chunkGroups) {
// Can module be placed in a chunk of this group?
for(const chunk of chunkGroup.chunks) {
if(module.chunkCondition(chunk)) {
targetChunks.add(chunk);
continue chunkGroupLoop;
}
for (const sourceChunk of sourceChunks) {
GraphHelpers.disconnectChunkAndModule(sourceChunk, module);
}
// We reached the entrypoint: fail
if(chunkGroup.isInitial()) {
throw new Error("Cannot fullfill chunk condition of " + module.identifier());
}
// Try placing in all parents
for(const group of chunkGroup.parentsIterable) {
chunkGroups.add(group);
for (const targetChunk of targetChunks) {
GraphHelpers.connectChunkAndModule(targetChunk, module);
}
}
for(const sourceChunk of sourceChunks) {
GraphHelpers.disconnectChunkAndModule(sourceChunk, module);
}
for(const targetChunk of targetChunks) {
GraphHelpers.connectChunkAndModule(targetChunk, module);
}
}
if(changed) return true;
};
compilation.hooks.optimizeChunksBasic.tap("EnsureChunkConditionsPlugin", handler);
compilation.hooks.optimizeExtractedChunksBasic.tap("EnsureChunkConditionsPlugin", handler);
});
if (changed) return true;
};
compilation.hooks.optimizeChunksBasic.tap(
"EnsureChunkConditionsPlugin",
handler
);
compilation.hooks.optimizeExtractedChunksBasic.tap(
"EnsureChunkConditionsPlugin",
handler
);
}
);
}
}
module.exports = EnsureChunkConditionsPlugin;

View File

@ -5,30 +5,33 @@
"use strict";
class FlagIncludedChunksPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("FlagIncludedChunksPlugin", (compilation) => {
compilation.hooks.optimizeChunkIds.tap("FlagIncludedChunksPlugin", (chunks) => {
for(const chunkA of chunks) {
loopB: for(const chunkB of chunks) {
// as we iterate the same iterables twice
// skip if we find ourselves
if(chunkA === chunkB) continue loopB;
compiler.hooks.compilation.tap("FlagIncludedChunksPlugin", compilation => {
compilation.hooks.optimizeChunkIds.tap(
"FlagIncludedChunksPlugin",
chunks => {
for (const chunkA of chunks) {
loopB: for (const chunkB of chunks) {
// as we iterate the same iterables twice
// skip if we find ourselves
if (chunkA === chunkB) continue loopB;
// 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()) continue loopB;
// 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())
continue loopB;
if(chunkB.getNumberOfModules() === 0) continue loopB;
if (chunkB.getNumberOfModules() === 0) continue loopB;
// is chunkB in chunkA?
for(const m of chunkB.modulesIterable) {
if(!chunkA.containsModule(m)) continue loopB;
// is chunkB in chunkA?
for (const m of chunkB.modulesIterable) {
if (!chunkA.containsModule(m)) continue loopB;
}
chunkA.ids.push(chunkB.id);
}
chunkA.ids.push(chunkB.id);
}
}
});
);
});
}
}

View File

@ -14,44 +14,52 @@ class LimitChunkCountPlugin {
}
apply(compiler) {
const options = this.options;
compiler.hooks.compilation.tap("LimitChunkCountPlugin", (compilation) => {
compilation.hooks.optimizeChunksAdvanced.tap("LimitChunkCountPlugin", (chunks) => {
const maxChunks = options.maxChunks;
if(!maxChunks) return;
if(maxChunks < 1) return;
if(chunks.length <= maxChunks) return;
compiler.hooks.compilation.tap("LimitChunkCountPlugin", compilation => {
compilation.hooks.optimizeChunksAdvanced.tap(
"LimitChunkCountPlugin",
chunks => {
const maxChunks = options.maxChunks;
if (!maxChunks) return;
if (maxChunks < 1) return;
if (chunks.length <= maxChunks) return;
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]);
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;
}
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;
}
});
);
});
}
}

View File

@ -5,60 +5,71 @@
"use strict";
class MergeDuplicateChunksPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("MergeDuplicateChunksPlugin", (compilation) => {
compilation.hooks.optimizeChunksBasic.tap("MergeDuplicateChunksPlugin", (chunks) => {
// remember already tested chunks for performance
const notDuplicates = new Set();
compiler.hooks.compilation.tap(
"MergeDuplicateChunksPlugin",
compilation => {
compilation.hooks.optimizeChunksBasic.tap(
"MergeDuplicateChunksPlugin",
chunks => {
// remember already tested chunks for performance
const notDuplicates = new Set();
// for each chunk
for(const chunk of chunks) {
// track a Set of all chunk that could be duplicates
let possibleDuplicates;
for(const module of chunk.modulesIterable) {
if(possibleDuplicates === undefined) {
// when possibleDuplicates is not yet set,
// create a new Set from chunks of the current module
// including only chunks with the same number of modules
for(const dup of module.chunksIterable) {
if(dup !== chunk && chunk.getNumberOfModules() === dup.getNumberOfModules() && !notDuplicates.has(dup)) {
// delay allocating the new Set until here, reduce memory pressure
if(possibleDuplicates === undefined)
possibleDuplicates = new Set();
possibleDuplicates.add(dup);
// for each chunk
for (const chunk of chunks) {
// track a Set of all chunk that could be duplicates
let possibleDuplicates;
for (const module of chunk.modulesIterable) {
if (possibleDuplicates === undefined) {
// when possibleDuplicates is not yet set,
// create a new Set from chunks of the current module
// including only chunks with the same number of modules
for (const dup of module.chunksIterable) {
if (
dup !== chunk &&
chunk.getNumberOfModules() === dup.getNumberOfModules() &&
!notDuplicates.has(dup)
) {
// delay allocating the new Set until here, reduce memory pressure
if (possibleDuplicates === undefined)
possibleDuplicates = new Set();
possibleDuplicates.add(dup);
}
}
// when no chunk is possible we can break here
if (possibleDuplicates === undefined) break;
} else {
// validate existing possible duplicates
for (const dup of possibleDuplicates) {
// remove possible duplicate when module is not contained
if (!dup.containsModule(module))
possibleDuplicates.delete(dup);
}
// when all chunks has been removed we can break here
if (possibleDuplicates.size === 0) break;
}
}
// when no chunk is possible we can break here
if(possibleDuplicates === undefined) break;
} else {
// validate existing possible duplicates
for(const dup of possibleDuplicates) {
// remove possible duplicate when module is not contained
if(!dup.containsModule(module))
possibleDuplicates.delete(dup);
// when we found duplicates
if (
possibleDuplicates !== undefined &&
possibleDuplicates.size > 0
) {
for (const otherChunk of possibleDuplicates) {
if (otherChunk.hasRuntime() !== chunk.hasRuntime()) continue;
// merge them
if (chunk.integrate(otherChunk, "duplicate"))
chunks.splice(chunks.indexOf(otherChunk), 1);
}
}
// when all chunks has been removed we can break here
if(possibleDuplicates.size === 0) break;
// don't check already processed chunks twice
notDuplicates.add(chunk);
}
}
// when we found duplicates
if(possibleDuplicates !== undefined && possibleDuplicates.size > 0) {
for(const otherChunk of possibleDuplicates) {
if(otherChunk.hasRuntime() !== chunk.hasRuntime()) continue;
// merge them
if(chunk.integrate(otherChunk, "duplicate"))
chunks.splice(chunks.indexOf(otherChunk), 1);
}
}
// don't check already processed chunks twice
notDuplicates.add(chunk);
}
});
});
);
}
);
}
}
module.exports = MergeDuplicateChunksPlugin;

View File

@ -16,50 +16,61 @@ class MinChunkSizePlugin {
apply(compiler) {
const options = this.options;
const minChunkSize = options.minChunkSize;
compiler.hooks.compilation.tap("MinChunkSizePlugin", (compilation) => {
compilation.hooks.optimizeChunksAdvanced.tap("MinChunkSizePlugin", (chunks) => {
const equalOptions = {
chunkOverhead: 1,
entryChunkMultiplicator: 1
};
compiler.hooks.compilation.tap("MinChunkSizePlugin", compilation => {
compilation.hooks.optimizeChunksAdvanced.tap(
"MinChunkSizePlugin",
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];
});
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;
if (sortedSizeFilteredExtendedPairCombinations.length === 0) return;
const pair = sortedSizeFilteredExtendedPairCombinations[0];
const pair = sortedSizeFilteredExtendedPairCombinations[0];
pair[2].integrate(pair[3], "min-size");
chunks.splice(chunks.indexOf(pair[3]), 1);
return true;
});
pair[2].integrate(pair[3], "min-size");
chunks.splice(chunks.indexOf(pair[3]), 1);
return true;
}
);
});
}
}

View File

@ -17,240 +17,333 @@ const formatBailoutReason = msg => {
class ModuleConcatenationPlugin {
constructor(options) {
if(typeof options !== "object") options = {};
if (typeof options !== "object") options = {};
this.options = options;
}
apply(compiler) {
compiler.hooks.compilation.tap("ModuleConcatenationPlugin", (compilation, {
normalModuleFactory
}) => {
const handler = (parser, parserOptions) => {
parser.hooks.call.for("eval").tap("ModuleConcatenationPlugin", () => {
// Because of variable renaming we can't use modules with eval.
parser.state.module.buildMeta.moduleConcatenationBailout = "eval()";
});
};
compiler.hooks.compilation.tap(
"ModuleConcatenationPlugin",
(compilation, { normalModuleFactory }) => {
const handler = (parser, parserOptions) => {
parser.hooks.call.for("eval").tap("ModuleConcatenationPlugin", () => {
// Because of variable renaming we can't use modules with eval.
parser.state.module.buildMeta.moduleConcatenationBailout = "eval()";
});
};
normalModuleFactory.hooks.parser.for("javascript/auto").tap("ModuleConcatenationPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/dynamic").tap("ModuleConcatenationPlugin", handler);
normalModuleFactory.hooks.parser.for("javascript/esm").tap("ModuleConcatenationPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("ModuleConcatenationPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("ModuleConcatenationPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("ModuleConcatenationPlugin", handler);
const bailoutReasonMap = new Map();
const bailoutReasonMap = new Map();
const setBailoutReason = (module, reason) => {
bailoutReasonMap.set(module, reason);
module.optimizationBailout.push(typeof reason === "function" ? (rs) => formatBailoutReason(reason(rs)) : formatBailoutReason(reason));
};
const setBailoutReason = (module, reason) => {
bailoutReasonMap.set(module, reason);
module.optimizationBailout.push(
typeof reason === "function"
? rs => formatBailoutReason(reason(rs))
: formatBailoutReason(reason)
);
};
const getBailoutReason = (module, requestShortener) => {
const reason = bailoutReasonMap.get(module);
if(typeof reason === "function") return reason(requestShortener);
return reason;
};
const getBailoutReason = (module, requestShortener) => {
const reason = bailoutReasonMap.get(module);
if (typeof reason === "function") return reason(requestShortener);
return reason;
};
compilation.hooks.optimizeChunkModules.tap("ModuleConcatenationPlugin", (chunks, modules) => {
const relevantModules = [];
const possibleInners = new Set();
for(const module of modules) {
// Only harmony modules are valid for optimization
if(!module.buildMeta || module.buildMeta.exportsType !== "namespace" || !module.dependencies.some(d => d instanceof HarmonyCompatibilityDependency)) {
setBailoutReason(module, "Module is not an ECMAScript module");
continue;
}
compilation.hooks.optimizeChunkModules.tap(
"ModuleConcatenationPlugin",
(chunks, modules) => {
const relevantModules = [];
const possibleInners = new Set();
for (const module of modules) {
// Only harmony modules are valid for optimization
if (
!module.buildMeta ||
module.buildMeta.exportsType !== "namespace" ||
!module.dependencies.some(
d => d instanceof HarmonyCompatibilityDependency
)
) {
setBailoutReason(module, "Module is not an ECMAScript module");
continue;
}
// Some expressions are not compatible with module concatenation
// because they may produce unexpected results. The plugin bails out
// if some were detected upfront.
if(module.buildMeta && module.buildMeta.moduleConcatenationBailout) {
setBailoutReason(module, `Module uses ${module.buildMeta.moduleConcatenationBailout}`);
continue;
}
// Some expressions are not compatible with module concatenation
// because they may produce unexpected results. The plugin bails out
// if some were detected upfront.
if (
module.buildMeta &&
module.buildMeta.moduleConcatenationBailout
) {
setBailoutReason(
module,
`Module uses ${module.buildMeta.moduleConcatenationBailout}`
);
continue;
}
// Exports must be known (and not dynamic)
if(!Array.isArray(module.buildMeta.providedExports)) {
setBailoutReason(module, "Module exports are unknown");
continue;
}
// Exports must be known (and not dynamic)
if (!Array.isArray(module.buildMeta.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;
}
// 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;
}
// 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);
relevantModules.push(module);
// Module must not be the entry points
if(module.isEntryModule()) {
setBailoutReason(module, "Module is an entry point");
continue;
}
// Module must not be the entry points
if (module.isEntryModule()) {
setBailoutReason(module, "Module is an entry point");
continue;
}
// Module must be in any chunk (we don't want to do useless work)
if(module.getNumberOfChunks() === 0) {
setBailoutReason(module, "Module is not in any chunk");
continue;
}
// Module must be in any chunk (we don't want to do useless work)
if (module.getNumberOfChunks() === 0) {
setBailoutReason(module, "Module is not in any chunk");
continue;
}
// Module must only be used by Harmony Imports
const nonHarmonyReasons = module.reasons.filter(reason => !reason.dependency || !(reason.dependency instanceof HarmonyImportDependency));
if(nonHarmonyReasons.length > 0) {
const importingModules = new Set(nonHarmonyReasons.map(r => r.module).filter(Boolean));
const importingExplanations = new Set(nonHarmonyReasons.map(r => r.explanation).filter(Boolean));
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();
const explanations = Array.from(importingExplanations).sort();
if(names.length > 0 && explanations.length === 0)
return `Module is referenced from these modules with unsupported syntax: ${names.join(", ")}`;
else if(names.length === 0 && explanations.length > 0)
return `Module is referenced by: ${explanations.join(", ")}`;
else if(names.length > 0 && explanations.length > 0)
return `Module is referenced from these modules with unsupported syntax: ${names.join(", ")} and by: ${explanations.join(", ")}`;
else
return "Module is referenced in a unsupported way";
// Module must only be used by Harmony Imports
const nonHarmonyReasons = module.reasons.filter(
reason =>
!reason.dependency ||
!(reason.dependency instanceof HarmonyImportDependency)
);
if (nonHarmonyReasons.length > 0) {
const importingModules = new Set(
nonHarmonyReasons.map(r => r.module).filter(Boolean)
);
const importingExplanations = new Set(
nonHarmonyReasons.map(r => r.explanation).filter(Boolean)
);
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();
const explanations = Array.from(importingExplanations).sort();
if (names.length > 0 && explanations.length === 0)
return `Module is referenced from these modules with unsupported syntax: ${names.join(
", "
)}`;
else if (names.length === 0 && explanations.length > 0)
return `Module is referenced by: ${explanations.join(
", "
)}`;
else if (names.length > 0 && explanations.length > 0)
return `Module is referenced from these modules with unsupported syntax: ${names.join(
", "
)} and by: ${explanations.join(", ")}`;
else return "Module is referenced in a unsupported way";
});
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;
});
continue;
}
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;
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);
// create a configuration with the root
const currentConfiguration = new ConcatConfiguration(currentRoot);
// cache failures to add modules
const failureCache = new Map();
// 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.getModules()) {
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 modules = concatConfiguration.getModules();
const newModule = new ConcatenatedModule(concatConfiguration.rootModule, modules);
for(const warning of concatConfiguration.getWarningsSorted()) {
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 modules) {
usedModules.add(m);
for(const chunk of chunks) {
chunk.removeModule(m);
}
}
for(const chunk of chunks) {
chunk.addModule(newModule);
newModule.addChunk(chunk);
if(chunk.entryModule === concatConfiguration.rootModule)
chunk.entryModule = newModule;
}
compilation.modules.push(newModule);
for(const reason of newModule.reasons) {
reason.dependency.module = newModule;
}
for(const dep of newModule.dependencies) {
if(dep.module) {
for(const reason of dep.module.reasons) {
if(reason.dependency === dep)
reason.module = newModule;
// 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.getModules()) {
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 modules = concatConfiguration.getModules();
const newModule = new ConcatenatedModule(
concatConfiguration.rootModule,
modules
);
for (const warning of concatConfiguration.getWarningsSorted()) {
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 modules) {
usedModules.add(m);
for (const chunk of chunks) {
chunk.removeModule(m);
}
}
for (const chunk of chunks) {
chunk.addModule(newModule);
newModule.addChunk(chunk);
if (chunk.entryModule === concatConfiguration.rootModule)
chunk.entryModule = newModule;
}
compilation.modules.push(newModule);
for (const reason of newModule.reasons) {
reason.dependency.module = newModule;
}
for (const dep of newModule.dependencies) {
if (dep.module) {
for (const reason of dep.module.reasons) {
if (reason.dependency === dep) reason.module = newModule;
}
}
}
}
compilation.modules = compilation.modules.filter(
m => !usedModules.has(m)
);
}
}
compilation.modules = compilation.modules.filter(m => !usedModules.has(m));
});
});
);
}
);
}
getImports(module) {
return Array.from(new Set(module.dependencies
return Array.from(
new Set(
module.dependencies
// Only harmony Dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module)
// Only harmony Dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module)
// Get reference info for this dependency
.map(dep => dep.getReference())
// Get reference info for this dependency
.map(dep => dep.getReference())
// Reference is valid and has a module
.filter(ref => ref && ref.module)
// Reference is valid and has a module
.filter(ref => ref && ref.module)
// Dependencies are simple enough to concat them
.filter(ref => Array.isArray(ref.importedNames) || Array.isArray(ref.module.buildMeta.providedExports))
// Dependencies are simple enough to concat them
.filter(
ref =>
Array.isArray(ref.importedNames) ||
Array.isArray(ref.module.buildMeta.providedExports)
)
// Take the imported module
.map(ref => ref.module)
));
// Take the imported module
.map(ref => ref.module)
)
);
}
tryToAdd(config, module, possibleModules, failureCache) {
const cacheEntry = failureCache.get(module);
if(cacheEntry) {
if (cacheEntry) {
return cacheEntry;
}
// Already added?
if(config.has(module)) {
if (config.has(module)) {
return null;
}
// Not possible to add?
if(!possibleModules.has(module)) {
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)) {
if (!config.rootModule.hasEqualsChunks(module)) {
failureCache.set(module, module); // cache failures for performance
return module;
}
@ -262,22 +355,35 @@ class ModuleConcatenationPlugin {
testConfig.add(module);
// Every module which depends on the added module must be in the configuration too.
for(const reason of module.reasons) {
for (const reason of module.reasons) {
// Modules that are not used can be ignored
if(reason.module.factoryMeta.sideEffectFree && reason.module.used === false) continue;
if (
reason.module.factoryMeta.sideEffectFree &&
reason.module.used === false
)
continue;
const problem = this.tryToAdd(testConfig, reason.module, possibleModules, failureCache);
if(problem) {
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) {
for (const imp of this.getImports(module)) {
const problem = this.tryToAdd(
testConfig,
imp,
possibleModules,
failureCache
);
if (problem) {
config.addWarning(module, problem);
}
}
@ -291,7 +397,7 @@ class ModuleConcatenationPlugin {
class ConcatConfiguration {
constructor(rootModule, cloneFrom) {
this.rootModule = rootModule;
if(cloneFrom) {
if (cloneFrom) {
this.modules = cloneFrom.modules.createChild(5);
this.warnings = cloneFrom.warnings.createChild(5);
} else {
@ -318,13 +424,15 @@ class ConcatConfiguration {
}
getWarningsSorted() {
return new Map(this.warnings.asPairArray().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;
}));
return new Map(
this.warnings.asPairArray().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;
})
);
}
getModules() {

View File

@ -6,106 +6,119 @@
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/");
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.hooks.compilation.tap("OccurrenceOrderPlugin", (compilation) => {
compilation.hooks.optimizeModuleOrder.tap("OccurrenceOrderPlugin", (modules) => {
const occursInInitialChunksMap = new Map();
const occursInAllChunksMap = new Map();
compiler.hooks.compilation.tap("OccurrenceOrderPlugin", compilation => {
compilation.hooks.optimizeModuleOrder.tap(
"OccurrenceOrderPlugin",
modules => {
const occursInInitialChunksMap = new Map();
const occursInAllChunksMap = new Map();
const initialChunkChunkMap = new Map();
const entryCountMap = new Map();
for(const m of modules) {
let initial = 0;
let entry = 0;
for(const c of m.chunksIterable) {
if(c.canBeInitial()) initial++;
if(c.entryModule === m) entry++;
const initialChunkChunkMap = new Map();
const entryCountMap = new Map();
for (const m of modules) {
let initial = 0;
let entry = 0;
for (const c of m.chunksIterable) {
if (c.canBeInitial()) initial++;
if (c.entryModule === m) entry++;
}
initialChunkChunkMap.set(m, initial);
entryCountMap.set(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;
let factor = 1;
if(typeof r.dependency.getNumberOfIdOccurrences === "function")
factor = r.dependency.getNumberOfIdOccurrences();
if(factor === 0) return sum;
return sum + factor * r.module.getNumberOfChunks();
};
const countOccursInEntry = (sum, r) => {
if (!r.module) return sum;
return sum + initialChunkChunkMap.get(r.module);
};
const countOccurs = (sum, r) => {
if (!r.module) return sum;
let factor = 1;
if (typeof r.dependency.getNumberOfIdOccurrences === "function")
factor = r.dependency.getNumberOfIdOccurrences();
if (factor === 0) return sum;
return sum + factor * r.module.getNumberOfChunks();
};
if(preferEntry) {
for(const m of modules) {
const result = m.reasons.reduce(countOccursInEntry, 0) + initialChunkChunkMap.get(m) + entryCountMap.get(m);
occursInInitialChunksMap.set(m, result);
}
}
const originalOrder = new Map();
let i = 0;
for(const m of modules) {
const result = m.reasons.reduce(countOccurs, 0) + m.getNumberOfChunks() + entryCountMap.get(m);
occursInAllChunksMap.set(m, result);
originalOrder.set(m, i++);
}
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;
const orgA = originalOrder.get(a);
const orgB = originalOrder.get(b);
return orgB - orgA;
});
});
compilation.hooks.optimizeChunkOrder.tap("OccurrenceOrderPlugin", (chunks) => {
const occursInInitialChunksMap = new Map();
const originalOrder = new Map();
let i = 0;
for(const c of chunks) {
let occurs = 0;
for(const chunkGroup of c.groupsIterable) {
for(const parent of chunkGroup.parentsIterable) {
if(parent.isInitial())
occurs++;
if (preferEntry) {
for (const m of modules) {
const result =
m.reasons.reduce(countOccursInEntry, 0) +
initialChunkChunkMap.get(m) +
entryCountMap.get(m);
occursInInitialChunksMap.set(m, result);
}
}
occursInInitialChunksMap.set(c, occurs);
originalOrder.set(c, i++);
}
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 = a.getNumberOfGroups();
const bOccurs = b.getNumberOfGroups();
if(aOccurs > bOccurs) return -1;
if(aOccurs < bOccurs) return 1;
const orgA = originalOrder.get(a);
const orgB = originalOrder.get(b);
return orgB - orgA;
});
});
const originalOrder = new Map();
let i = 0;
for (const m of modules) {
const result =
m.reasons.reduce(countOccurs, 0) +
m.getNumberOfChunks() +
entryCountMap.get(m);
occursInAllChunksMap.set(m, result);
originalOrder.set(m, i++);
}
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;
const orgA = originalOrder.get(a);
const orgB = originalOrder.get(b);
return orgB - orgA;
});
}
);
compilation.hooks.optimizeChunkOrder.tap(
"OccurrenceOrderPlugin",
chunks => {
const occursInInitialChunksMap = new Map();
const originalOrder = new Map();
let i = 0;
for (const c of chunks) {
let occurs = 0;
for (const chunkGroup of c.groupsIterable) {
for (const parent of chunkGroup.parentsIterable) {
if (parent.isInitial()) occurs++;
}
}
occursInInitialChunksMap.set(c, occurs);
originalOrder.set(c, i++);
}
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 = a.getNumberOfGroups();
const bOccurs = b.getNumberOfGroups();
if (aOccurs > bOccurs) return -1;
if (aOccurs < bOccurs) return 1;
const orgA = originalOrder.get(a);
const orgB = originalOrder.get(b);
return orgB - orgA;
});
}
);
});
}
}

View File

@ -6,16 +6,26 @@
class RemoveEmptyChunksPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("RemoveEmptyChunksPlugin", (compilation) => {
const handler = (chunks) => {
chunks.filter((chunk) => chunk.isEmpty() && !chunk.hasRuntime() && !chunk.hasEntryModule())
.forEach((chunk) => {
compiler.hooks.compilation.tap("RemoveEmptyChunksPlugin", compilation => {
const handler = chunks => {
chunks
.filter(
chunk =>
chunk.isEmpty() && !chunk.hasRuntime() && !chunk.hasEntryModule()
)
.forEach(chunk => {
chunk.remove("empty");
chunks.splice(chunks.indexOf(chunk), 1);
});
};
compilation.hooks.optimizeChunksBasic.tap("RemoveEmptyChunksPlugin", handler);
compilation.hooks.optimizeExtractedChunksBasic.tap("RemoveEmptyChunksPlugin", handler);
compilation.hooks.optimizeChunksBasic.tap(
"RemoveEmptyChunksPlugin",
handler
);
compilation.hooks.optimizeExtractedChunksBasic.tap(
"RemoveEmptyChunksPlugin",
handler
);
});
}
}

View File

@ -11,11 +11,11 @@ const getParentChunksWithModule = (currentChunk, module) => {
const chunks = [];
const stack = new Set(currentChunk.parentsIterable);
for(const chunk of stack) {
if(chunk.containsModule(module)) {
for (const chunk of stack) {
if (chunk.containsModule(module)) {
chunks.push(chunk);
} else {
for(const parent of chunk.parentsIterable) {
for (const parent of chunk.parentsIterable) {
stack.add(parent);
}
}
@ -26,38 +26,40 @@ const getParentChunksWithModule = (currentChunk, module) => {
class RemoveParentModulesPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("RemoveParentModulesPlugin", (compilation) => {
compiler.hooks.compilation.tap("RemoveParentModulesPlugin", compilation => {
const handler = (chunks, chunkGroups) => {
const queue = new Queue();
const availableModulesMap = new Map();
for(const chunkGroup of compilation.entrypoints.values()) {
for (const chunkGroup of compilation.entrypoints.values()) {
// initialize available modules for chunks without parents
availableModulesMap.set(chunkGroup, new Set());
for(const child of chunkGroup.childrenIterable)
queue.enqueue(child);
for (const child of chunkGroup.childrenIterable) queue.enqueue(child);
}
while(queue.length > 0) {
while (queue.length > 0) {
const chunkGroup = queue.dequeue();
let availableModules = availableModulesMap.get(chunkGroup);
let changed = false;
for(const parent of chunkGroup.parentsIterable) {
for (const parent of chunkGroup.parentsIterable) {
const availableModulesInParent = availableModulesMap.get(parent);
if(availableModulesInParent !== undefined) {
if (availableModulesInParent !== undefined) {
// If we know the available modules in parent: process these
if(availableModules === undefined) {
if (availableModules === undefined) {
// if we have not own info yet: create new entry
availableModules = new Set(availableModulesInParent);
for(const chunk of parent.chunks) {
for(const m of chunk.modulesIterable)
for (const chunk of parent.chunks) {
for (const m of chunk.modulesIterable)
availableModules.add(m);
}
availableModulesMap.set(chunkGroup, availableModules);
changed = true;
} else {
for(const m of availableModules) {
if(!parent.containsModule(m) && !availableModulesInParent.has(m)) {
for (const m of availableModules) {
if (
!parent.containsModule(m) &&
!availableModulesInParent.has(m)
) {
availableModules.delete(m);
changed = true;
}
@ -65,37 +67,47 @@ class RemoveParentModulesPlugin {
}
}
}
if(changed) {
if (changed) {
// if something changed: enqueue our children
for(const child of chunkGroup.childrenIterable)
for (const child of chunkGroup.childrenIterable)
queue.enqueue(child);
}
}
// now we have available modules for every chunk
for(const chunk of chunks) {
const availableModulesSets = Array.from(chunk.groupsIterable, chunkGroup => availableModulesMap.get(chunkGroup));
if(availableModulesSets.some(s => s === undefined)) continue; // No info about this chunk group
for (const chunk of chunks) {
const availableModulesSets = Array.from(
chunk.groupsIterable,
chunkGroup => availableModulesMap.get(chunkGroup)
);
if (availableModulesSets.some(s => s === undefined)) continue; // No info about this chunk group
const availableModules = intersect(availableModulesSets);
const numberOfModules = chunk.getNumberOfModules();
const toRemove = new Set();
if(numberOfModules < availableModules.size) {
for(const m of chunk.modulesIterable)
if(availableModules.has(m))
toRemove.add(m);
if (numberOfModules < availableModules.size) {
for (const m of chunk.modulesIterable)
if (availableModules.has(m)) toRemove.add(m);
} else {
for(const m of availableModules)
if(chunk.containsModule(m))
toRemove.add(m);
for (const m of availableModules)
if (chunk.containsModule(m)) toRemove.add(m);
}
for(const module of toRemove) {
module.rewriteChunkInReasons(chunk, getParentChunksWithModule(chunk, module));
for (const module of toRemove) {
module.rewriteChunkInReasons(
chunk,
getParentChunksWithModule(chunk, module)
);
chunk.removeModule(module);
}
}
};
compilation.hooks.optimizeChunksBasic.tap("RemoveParentModulesPlugin", handler);
compilation.hooks.optimizeExtractedChunksBasic.tap("RemoveParentModulesPlugin", handler);
compilation.hooks.optimizeChunksBasic.tap(
"RemoveParentModulesPlugin",
handler
);
compilation.hooks.optimizeExtractedChunksBasic.tap(
"RemoveParentModulesPlugin",
handler
);
});
}
}

View File

@ -6,19 +6,22 @@
module.exports = class RuntimeChunkPlugin {
constructor(options) {
this.options = Object.assign({
name: entrypoint => `runtime~${entrypoint.name}`
}, options);
this.options = Object.assign(
{
name: entrypoint => `runtime~${entrypoint.name}`
},
options
);
}
apply(compiler) {
compiler.hooks.thisCompilation.tap("RuntimeChunkPlugin", compilation => {
compilation.hooks.optimizeChunksAdvanced.tap("RuntimeChunkPlugin", () => {
for(const entrypoint of compilation.entrypoints.values()) {
for (const entrypoint of compilation.entrypoints.values()) {
const chunk = entrypoint.getRuntimeChunk();
if(chunk.getNumberOfModules() > 0) {
if (chunk.getNumberOfModules() > 0) {
let name = this.options.name;
if(typeof name === "function") {
if (typeof name === "function") {
name = name(entrypoint);
}
const newChunk = compilation.addChunk(name);

View File

@ -11,13 +11,20 @@ const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportS
class SideEffectsFlagPlugin {
apply(compiler) {
compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", (nmf) => {
compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => {
nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {
const resolveData = data.resourceResolveData;
if(resolveData && resolveData.descriptionFileData && resolveData.relativePath) {
if (
resolveData &&
resolveData.descriptionFileData &&
resolveData.relativePath
) {
const sideEffects = resolveData.descriptionFileData.sideEffects;
const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects(resolveData.relativePath, sideEffects);
if(!hasSideEffects) {
const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects(
resolveData.relativePath,
sideEffects
);
if (!hasSideEffects) {
module.factoryMeta.sideEffectFree = true;
}
}
@ -25,115 +32,124 @@ class SideEffectsFlagPlugin {
return module;
});
nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {
if(data.settings.sideEffects === false)
if (data.settings.sideEffects === false)
module.factoryMeta.sideEffectFree = true;
else if(data.settings.sideEffects === true)
else if (data.settings.sideEffects === true)
module.factoryMeta.sideEffectFree = false;
});
});
compiler.hooks.compilation.tap("SideEffectsFlagPlugin", (compilation) => {
compilation.hooks.optimizeDependencies.tap("SideEffectsFlagPlugin", (modules) => {
const reexportMaps = new Map();
compiler.hooks.compilation.tap("SideEffectsFlagPlugin", compilation => {
compilation.hooks.optimizeDependencies.tap(
"SideEffectsFlagPlugin",
modules => {
const reexportMaps = new Map();
// Capture reexports of sideEffectFree modules
for(const module of modules) {
const removeDependencies = [];
for(const dep of module.dependencies) {
if(dep instanceof HarmonyImportSideEffectDependency) {
if(dep.module && dep.module.factoryMeta.sideEffectFree) {
removeDependencies.push(dep);
}
} else if(dep instanceof HarmonyExportImportedSpecifierDependency) {
if(module.factoryMeta.sideEffectFree) {
const mode = dep.getMode(true);
if(mode.type === "safe-reexport") {
let map = reexportMaps.get(module);
if(!map) {
reexportMaps.set(module, map = new Map());
}
for(const pair of mode.map) {
map.set(pair[0], {
module: mode.module,
exportName: pair[1]
});
// Capture reexports of sideEffectFree modules
for (const module of modules) {
const removeDependencies = [];
for (const dep of module.dependencies) {
if (dep instanceof HarmonyImportSideEffectDependency) {
if (dep.module && dep.module.factoryMeta.sideEffectFree) {
removeDependencies.push(dep);
}
} else if (
dep instanceof HarmonyExportImportedSpecifierDependency
) {
if (module.factoryMeta.sideEffectFree) {
const mode = dep.getMode(true);
if (mode.type === "safe-reexport") {
let map = reexportMaps.get(module);
if (!map) {
reexportMaps.set(module, (map = new Map()));
}
for (const pair of mode.map) {
map.set(pair[0], {
module: mode.module,
exportName: pair[1]
});
}
}
}
}
}
}
for(const dep of removeDependencies) {
module.removeDependency(dep);
dep.module.reasons = dep.module.reasons.filter(r => r.dependency !== dep);
}
}
// Flatten reexports
for(const map of reexportMaps.values()) {
for(const pair of map) {
let mapping = pair[1];
while(mapping) {
const innerMap = reexportMaps.get(mapping.module);
if(!innerMap) break;
const newMapping = innerMap.get(mapping.exportName);
if(newMapping) {
map.set(pair[0], newMapping);
}
mapping = newMapping;
for (const dep of removeDependencies) {
module.removeDependency(dep);
dep.module.reasons = dep.module.reasons.filter(
r => r.dependency !== dep
);
}
}
}
// Update imports along the reexports from sideEffectFree modules
const updates = [];
for(const pair of reexportMaps) {
const module = pair[0];
const map = pair[1];
for(const reason of module.reasons) {
const dep = reason.dependency;
if(dep instanceof HarmonyImportSpecifierDependency) {
const mapping = map.get(dep.id);
if(mapping) {
updates.push({
dep,
mapping,
module,
reason
});
// Flatten reexports
for (const map of reexportMaps.values()) {
for (const pair of map) {
let mapping = pair[1];
while (mapping) {
const innerMap = reexportMaps.get(mapping.module);
if (!innerMap) break;
const newMapping = innerMap.get(mapping.exportName);
if (newMapping) {
map.set(pair[0], newMapping);
}
mapping = newMapping;
}
}
}
}
// Execute updates
for(const update of updates) {
const dep = update.dep;
const mapping = update.mapping;
const module = update.module;
const reason = update.reason;
dep.module = mapping.module;
dep.id = mapping.exportName;
module.removeReason(reason.module, dep);
mapping.module.addReason(reason.module, dep);
// Update imports along the reexports from sideEffectFree modules
const updates = [];
for (const pair of reexportMaps) {
const module = pair[0];
const map = pair[1];
for (const reason of module.reasons) {
const dep = reason.dependency;
if (dep instanceof HarmonyImportSpecifierDependency) {
const mapping = map.get(dep.id);
if (mapping) {
updates.push({
dep,
mapping,
module,
reason
});
}
}
}
}
// Execute updates
for (const update of updates) {
const dep = update.dep;
const mapping = update.mapping;
const module = update.module;
const reason = update.reason;
dep.module = mapping.module;
dep.id = mapping.exportName;
module.removeReason(reason.module, dep);
mapping.module.addReason(reason.module, dep);
}
}
});
);
});
}
static moduleHasSideEffects(moduleName, flagValue) {
switch(typeof flagValue) {
switch (typeof flagValue) {
case "undefined":
return true;
case "boolean":
return flagValue;
case "string":
if(process.platform === "win32") {
if (process.platform === "win32") {
flagValue = flagValue.replace(/\\/g, "/");
}
return mm.isMatch(moduleName, flagValue, {
matchBase: true
});
case "object":
return flagValue.some(glob => SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob));
return flagValue.some(glob =>
SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob)
);
}
}
}

View File

@ -9,19 +9,23 @@ const SortableSet = require("../util/SortableSet");
const GraphHelpers = require("../GraphHelpers");
const isSubset = require("../util/SetHelpers").isSubset;
const hashFilename = (name) => {
return crypto.createHash("md4").update(name).digest("hex").slice(0, 8);
const hashFilename = name => {
return crypto
.createHash("md4")
.update(name)
.digest("hex")
.slice(0, 8);
};
const sortByIdentifier = (a, b) => {
if(a.identifier() > b.identifier()) return 1;
if(a.identifier() < b.identifier()) return -1;
if (a.identifier() > b.identifier()) return 1;
if (a.identifier() < b.identifier()) return -1;
return 0;
};
const getRequests = chunk => {
let requests = 0;
for(const chunkGroup of chunk.groupsIterable) {
for (const chunkGroup of chunk.groupsIterable) {
requests = Math.max(requests, chunkGroup.chunks.length);
}
return requests;
@ -29,14 +33,13 @@ const getRequests = chunk => {
const getModulesSize = modules => {
let sum = 0;
for(const m of modules)
sum += m.size();
for (const m of modules) sum += m.size();
return sum;
};
const isOverlap = (a, b) => {
for(const item of a.keys()) {
if(b.has(item)) return true;
for (const item of a.keys()) {
if (b.has(item)) return true;
}
return false;
};
@ -44,33 +47,34 @@ const isOverlap = (a, b) => {
const compareEntries = (a, b) => {
// 1. by priority
const diffPriority = a.cacheGroup.priority - b.cacheGroup.priority;
if(diffPriority) return diffPriority;
if (diffPriority) return diffPriority;
// 2. by number of chunks
const diffCount = a.chunks.size - b.chunks.size;
if(diffCount) return diffCount;
if (diffCount) return diffCount;
// 3. by size reduction
const aSizeReduce = a.size * (a.chunks.size - 1);
const bSizeReduce = b.size * (b.chunks.size - 1);
const diffSizeReduce = aSizeReduce - bSizeReduce;
if(diffSizeReduce) return diffSizeReduce;
if (diffSizeReduce) return diffSizeReduce;
// 4. by number of modules (to be able to compare by identifier)
const modulesA = a.modules;
const modulesB = b.modules;
const diff = modulesA.size - modulesB.size;
if(diff) return diff;
if (diff) return diff;
// 5. by module identifiers
modulesA.sort();
modulesB.sort();
const aI = modulesA[Symbol.iterator]();
const bI = modulesB[Symbol.iterator]();
while(true) { // eslint-disable-line
// eslint-disable-next-line no-constant-condition
while (true) {
const aItem = aI.next();
const bItem = bI.next();
if(aItem.done) return 0;
if (aItem.done) return 0;
const aModuleIdentifier = aItem.value.identifier();
const bModuleIdentifier = bItem.value.identifier();
if(aModuleIdentifier > bModuleIdentifier) return -1;
if(aModuleIdentifier < bModuleIdentifier) return 1;
if (aModuleIdentifier > bModuleIdentifier) return -1;
if (aModuleIdentifier < bModuleIdentifier) return 1;
}
};
@ -87,69 +91,74 @@ module.exports = class SplitChunksPlugin {
maxAsyncRequests: options.maxAsyncRequests || 1,
maxInitialRequests: options.maxInitialRequests || 1,
getName: SplitChunksPlugin.normalizeName(options.name) || (() => {}),
getCacheGroups: SplitChunksPlugin.normalizeCacheGroups(options.cacheGroups),
getCacheGroups: SplitChunksPlugin.normalizeCacheGroups(
options.cacheGroups
)
};
}
static normalizeName(option) {
if(option === true) {
if (option === true) {
const fn = (module, chunks, cacheGroup) => {
const names = chunks.map(c => c.name);
if(!names.every(Boolean)) return;
if (!names.every(Boolean)) return;
names.sort();
let name = (cacheGroup && cacheGroup !== "default" ? cacheGroup + "~" : "") + names.join("~");
let name =
(cacheGroup && cacheGroup !== "default" ? cacheGroup + "~" : "") +
names.join("~");
// Filenames and paths can't be too long otherwise an
// ENAMETOOLONG error is raised. If the generated name if too
// long, it is truncated and a hash is appended. The limit has
// been set to 100 to prevent `[name].[chunkhash].[ext]` from
// generating a 256+ character string.
if(name.length > 100) {
if (name.length > 100) {
name = name.slice(0, 100) + "~" + hashFilename(name);
}
return name;
};
return fn;
}
if(typeof option === "string") {
if (typeof option === "string") {
const fn = () => {
return option;
};
return fn;
}
if(typeof option === "function")
return option;
if (typeof option === "function") return option;
}
static normalizeCacheGroups(cacheGroups) {
if(typeof cacheGroups === "function") {
if (typeof cacheGroups === "function") {
return cacheGroups;
}
if(cacheGroups && typeof cacheGroups === "object") {
if (cacheGroups && typeof cacheGroups === "object") {
const fn = (module, chunks) => {
let results;
for(const key of Object.keys(cacheGroups)) {
for (const key of Object.keys(cacheGroups)) {
let option = cacheGroups[key];
if(option === false)
continue;
if(option instanceof RegExp || typeof option === "string") {
if (option === false) continue;
if (option instanceof RegExp || typeof option === "string") {
option = {
test: option
};
}
if(typeof option === "function") {
if (typeof option === "function") {
let result = option(module);
if(result) {
if(results === undefined) results = [];
for(const r of (Array.isArray(result) ? result : [result])) {
const result = Object.assign({
key,
}, r);
if(result.name) result.getName = () => result.name;
if (result) {
if (results === undefined) results = [];
for (const r of Array.isArray(result) ? result : [result]) {
const result = Object.assign(
{
key
},
r
);
if (result.name) result.getName = () => result.name;
results.push(result);
}
}
} else if(SplitChunksPlugin.checkTest(option.test, module, chunks)) {
if(results === undefined) results = [];
} else if (SplitChunksPlugin.checkTest(option.test, module, chunks)) {
if (results === undefined) results = [];
results.push({
key: key,
priority: option.priority,
@ -173,23 +182,19 @@ module.exports = class SplitChunksPlugin {
}
static checkTest(test, module, chunks) {
if(test === undefined)
return true;
if(typeof test === "function")
return test(module, chunks);
if(typeof test === "boolean")
return test;
const names = chunks.map(c => c.name).concat(module.nameForCondition ? [module.nameForCondition()] : []).filter(Boolean);
if(typeof test === "string") {
for(const name of names)
if(name.startsWith(test))
return true;
if (test === undefined) return true;
if (typeof test === "function") return test(module, chunks);
if (typeof test === "boolean") return test;
const names = chunks
.map(c => c.name)
.concat(module.nameForCondition ? [module.nameForCondition()] : [])
.filter(Boolean);
if (typeof test === "string") {
for (const name of names) if (name.startsWith(test)) return true;
return false;
}
if(test instanceof RegExp) {
for(const name of names)
if(test.test(name))
return true;
if (test instanceof RegExp) {
for (const name of names) if (test.test(name)) return true;
return false;
}
return false;
@ -201,213 +206,269 @@ module.exports = class SplitChunksPlugin {
compilation.hooks.unseal.tap("SplitChunksPlugin", () => {
alreadyOptimized = false;
});
compilation.hooks.optimizeChunksAdvanced.tap("SplitChunksPlugin", chunks => {
if(alreadyOptimized) return;
alreadyOptimized = true;
// Give each selected chunk an index (to create strings from chunks)
const indexMap = new Map();
let index = 1;
for(const chunk of chunks) {
indexMap.set(chunk, index++);
}
const getKey = chunks => {
return Array.from(chunks, c => indexMap.get(c)).sort().join();
};
// Create a list of possible combinations
const chunkSetsInGraph = new Map(); // Map<string, Set<Chunk>>
for(const module of compilation.modules) {
const chunkIndices = getKey(module.chunksIterable);
chunkSetsInGraph.set(chunkIndices, new Set(module.chunksIterable));
}
const combinations = new Map(); // Map<string, Set<Chunk>[]>
for(const [key, chunksSet] of chunkSetsInGraph) {
var array = [];
for(const set of chunkSetsInGraph.values()) {
if(isSubset(chunksSet, set)) {
array.push(set);
compilation.hooks.optimizeChunksAdvanced.tap(
"SplitChunksPlugin",
chunks => {
if (alreadyOptimized) return;
alreadyOptimized = true;
// Give each selected chunk an index (to create strings from chunks)
const indexMap = new Map();
let index = 1;
for (const chunk of chunks) {
indexMap.set(chunk, index++);
}
const getKey = chunks => {
return Array.from(chunks, c => indexMap.get(c))
.sort()
.join();
};
// Create a list of possible combinations
const chunkSetsInGraph = new Map(); // Map<string, Set<Chunk>>
for (const module of compilation.modules) {
const chunkIndices = getKey(module.chunksIterable);
chunkSetsInGraph.set(chunkIndices, new Set(module.chunksIterable));
}
const combinations = new Map(); // Map<string, Set<Chunk>[]>
for (const [key, chunksSet] of chunkSetsInGraph) {
var array = [];
for (const set of chunkSetsInGraph.values()) {
if (isSubset(chunksSet, set)) {
array.push(set);
}
}
combinations.set(key, array);
}
// Map a list of chunks to a list of modules
// For the key the chunk "index" is used, the value is a SortableSet of modules
const chunksInfoMap = new Map();
// Walk through all modules
for (const module of compilation.modules) {
// Get array of chunks
const chunks = module.getChunks();
// Get cache group
let cacheGroups = this.options.getCacheGroups(module, chunks);
if (!Array.isArray(cacheGroups)) continue;
for (const cacheGroupSource of cacheGroups) {
const cacheGroup = {
key: cacheGroupSource.key,
priority: cacheGroupSource.priority || 0,
chunks: cacheGroupSource.chunks || this.options.chunks,
minSize:
cacheGroupSource.minSize !== undefined
? cacheGroupSource.minSize
: cacheGroupSource.enforce ? 0 : this.options.minSize,
minChunks:
cacheGroupSource.minChunks !== undefined
? cacheGroupSource.minChunks
: cacheGroupSource.enforce ? 1 : this.options.minChunks,
maxAsyncRequests:
cacheGroupSource.maxAsyncRequests !== undefined
? cacheGroupSource.maxAsyncRequests
: cacheGroupSource.enforce
? Infinity
: this.options.maxAsyncRequests,
maxInitialRequests:
cacheGroupSource.maxInitialRequests !== undefined
? cacheGroupSource.maxInitialRequests
: cacheGroupSource.enforce
? Infinity
: this.options.maxInitialRequests,
getName:
cacheGroupSource.getName !== undefined
? cacheGroupSource.getName
: this.options.getName,
reuseExistingChunk: cacheGroupSource.reuseExistingChunk
};
// For all combination of chunk selection
for (const chunkCombination of combinations.get(getKey(chunks))) {
// Get indices of chunks in which this module occurs
const chunkIndices = Array.from(chunkCombination, chunk =>
indexMap.get(chunk)
);
// Break if minimum number of chunks is not reached
if (chunkIndices.length < cacheGroup.minChunks) continue;
// Select chunks by configuration
const selectedChunks =
cacheGroup.chunks === "initial"
? Array.from(chunkCombination).filter(chunk =>
chunk.canBeInitial()
)
: cacheGroup.chunks === "async"
? Array.from(chunkCombination).filter(
chunk => !chunk.canBeInitial()
)
: Array.from(chunkCombination);
// Determine name for split chunk
const name = cacheGroup.getName(
module,
selectedChunks,
cacheGroup.key
);
// Create key for maps
// When it has a name we use the name as key
// Elsewise we create the key from chunks and cache group key
// This automatically merges equal names
const chunksKey = getKey(selectedChunks);
const key =
(name && `name:${name}`) ||
`chunks:${chunksKey} key:${cacheGroup.key}`;
// Add module to maps
let info = chunksInfoMap.get(key);
if (info === undefined) {
chunksInfoMap.set(
key,
(info = {
modules: new SortableSet(undefined, sortByIdentifier),
cacheGroup,
name,
chunks: new Map(),
reusedableChunks: new Set(),
chunksKeys: new Set()
})
);
}
info.modules.add(module);
if (!info.chunksKeys.has(chunksKey)) {
info.chunksKeys.add(chunksKey);
for (const chunk of selectedChunks) {
info.chunks.set(chunk, chunk.getNumberOfModules());
}
}
}
}
}
combinations.set(key, array);
}
// Map a list of chunks to a list of modules
// For the key the chunk "index" is used, the value is a SortableSet of modules
const chunksInfoMap = new Map();
// Walk through all modules
for(const module of compilation.modules) {
// Get array of chunks
const chunks = module.getChunks();
// Get cache group
let cacheGroups = this.options.getCacheGroups(module, chunks);
if(!Array.isArray(cacheGroups)) continue;
for(const cacheGroupSource of cacheGroups) {
const cacheGroup = {
key: cacheGroupSource.key,
priority: cacheGroupSource.priority || 0,
chunks: cacheGroupSource.chunks || this.options.chunks,
minSize: cacheGroupSource.minSize !== undefined ? cacheGroupSource.minSize : cacheGroupSource.enforce ? 0 : this.options.minSize,
minChunks: cacheGroupSource.minChunks !== undefined ? cacheGroupSource.minChunks : cacheGroupSource.enforce ? 1 : this.options.minChunks,
maxAsyncRequests: cacheGroupSource.maxAsyncRequests !== undefined ? cacheGroupSource.maxAsyncRequests : cacheGroupSource.enforce ? Infinity : this.options.maxAsyncRequests,
maxInitialRequests: cacheGroupSource.maxInitialRequests !== undefined ? cacheGroupSource.maxInitialRequests : cacheGroupSource.enforce ? Infinity : this.options.maxInitialRequests,
getName: cacheGroupSource.getName !== undefined ? cacheGroupSource.getName : this.options.getName,
reuseExistingChunk: cacheGroupSource.reuseExistingChunk
};
// For all combination of chunk selection
for(const chunkCombination of combinations.get(getKey(chunks))) {
// Get indices of chunks in which this module occurs
const chunkIndices = Array.from(chunkCombination, chunk => indexMap.get(chunk));
// Break if minimum number of chunks is not reached
if(chunkIndices.length < cacheGroup.minChunks)
for (const [key, info] of chunksInfoMap) {
// Get size of module lists
info.size = getModulesSize(info.modules);
if (info.size < info.cacheGroup.minSize) {
chunksInfoMap.delete(key);
}
}
let changed = false;
while (chunksInfoMap.size > 0) {
// Find best matching entry
let bestEntryKey;
let bestEntry;
for (const pair of chunksInfoMap) {
const key = pair[0];
const info = pair[1];
if (bestEntry === undefined) {
bestEntry = info;
bestEntryKey = key;
} else if (compareEntries(bestEntry, info) < 0) {
bestEntry = info;
bestEntryKey = key;
}
}
const item = bestEntry;
chunksInfoMap.delete(bestEntryKey);
let chunkName = item.name;
// Variable for the new chunk (lazy created)
let newChunk;
// When no chunk name, check if we can reuse a chunk instead of creating a new one
let isReused = false;
if (item.cacheGroup.reuseExistingChunk) {
for (const pair of item.chunks) {
if (pair[1] === item.modules.size) {
const chunk = pair[0];
if (chunk.hasEntryModule()) continue;
if (!newChunk || !newChunk.name) newChunk = chunk;
else if (
chunk.name &&
chunk.name.length < newChunk.name.length
)
newChunk = chunk;
else if (
chunk.name &&
chunk.name.length === newChunk.name.length &&
chunk.name < newChunk.name
)
newChunk = chunk;
chunkName = undefined;
isReused = true;
}
}
}
// Walk through all chunks
for (const chunk of item.chunks.keys()) {
// skip if we address ourself
if (chunk.name === chunkName || chunk === newChunk) continue;
// respect max requests when not enforced
const maxRequests = chunk.isOnlyInitial()
? item.cacheGroup.maxInitialRequests
: chunk.canBeInitial()
? Math.min(
item.cacheGroup.maxInitialRequests,
item.cacheGroup.maxAsyncRequests
)
: item.cacheGroup.maxAsyncRequests;
if (isFinite(maxRequests) && getRequests(chunk) >= maxRequests)
continue;
// Select chunks by configuration
const selectedChunks = cacheGroup.chunks === "initial" ? Array.from(chunkCombination).filter(chunk => chunk.canBeInitial()) :
cacheGroup.chunks === "async" ? Array.from(chunkCombination).filter(chunk => !chunk.canBeInitial()) :
Array.from(chunkCombination);
// Determine name for split chunk
const name = cacheGroup.getName(module, selectedChunks, cacheGroup.key);
// Create key for maps
// When it has a name we use the name as key
// Elsewise we create the key from chunks and cache group key
// This automatically merges equal names
const chunksKey = getKey(selectedChunks);
const key = name && `name:${name}` ||
`chunks:${chunksKey} key:${cacheGroup.key}`;
// Add module to maps
let info = chunksInfoMap.get(key);
if(info === undefined) {
chunksInfoMap.set(key, info = {
modules: new SortableSet(undefined, sortByIdentifier),
cacheGroup,
name,
chunks: new Map(),
reusedableChunks: new Set(),
chunksKeys: new Set()
});
if (newChunk === undefined) {
// Create the new chunk
newChunk = compilation.addChunk(chunkName);
}
info.modules.add(module);
if(!info.chunksKeys.has(chunksKey)) {
info.chunksKeys.add(chunksKey);
for(const chunk of selectedChunks) {
info.chunks.set(chunk, chunk.getNumberOfModules());
// Add graph connections for splitted chunk
chunk.split(newChunk);
// Remove all selected modules from the chunk
for (const module of item.modules) {
chunk.removeModule(module);
module.rewriteChunkInReasons(chunk, [newChunk]);
}
}
// If we successfully created a new chunk or reused one
if (newChunk) {
// Add a note to the chunk
newChunk.chunkReason = isReused
? "reused as split chunk"
: "split chunk";
if (item.cacheGroup.key) {
newChunk.chunkReason += ` (cache group: ${
item.cacheGroup.key
})`;
}
if (chunkName) {
newChunk.chunkReason += ` (name: ${chunkName})`;
// If the choosen name is already an entry point we remove the entry point
const entrypoint = compilation.entrypoints.get(chunkName);
if (entrypoint) {
compilation.entrypoints.delete(chunkName);
entrypoint.remove();
newChunk.entryModule = undefined;
}
}
}
}
}
for(const [key, info] of chunksInfoMap) {
// Get size of module lists
info.size = getModulesSize(info.modules);
if(info.size < info.cacheGroup.minSize) {
chunksInfoMap.delete(key);
}
}
let changed = false;
while(chunksInfoMap.size > 0) {
// Find best matching entry
let bestEntryKey;
let bestEntry;
for(const pair of chunksInfoMap) {
const key = pair[0];
const info = pair[1];
if(bestEntry === undefined) {
bestEntry = info;
bestEntryKey = key;
} else if(compareEntries(bestEntry, info) < 0) {
bestEntry = info;
bestEntryKey = key;
}
}
const item = bestEntry;
chunksInfoMap.delete(bestEntryKey);
let chunkName = item.name;
// Variable for the new chunk (lazy created)
let newChunk;
// When no chunk name, check if we can reuse a chunk instead of creating a new one
let isReused = false;
if(item.cacheGroup.reuseExistingChunk) {
for(const pair of item.chunks) {
if(pair[1] === item.modules.size) {
const chunk = pair[0];
if(chunk.hasEntryModule()) continue;
if(!newChunk || !newChunk.name)
newChunk = chunk;
else if(chunk.name && chunk.name.length < newChunk.name.length)
newChunk = chunk;
else if(chunk.name && chunk.name.length === newChunk.name.length && chunk.name < newChunk.name)
newChunk = chunk;
chunkName = undefined;
isReused = true;
}
}
}
// Walk through all chunks
for(const chunk of item.chunks.keys()) {
// skip if we address ourself
if(chunk.name === chunkName || chunk === newChunk) continue;
// respect max requests when not enforced
const maxRequests = chunk.isOnlyInitial() ? item.cacheGroup.maxInitialRequests :
chunk.canBeInitial() ? Math.min(item.cacheGroup.maxInitialRequests, item.cacheGroup.maxAsyncRequests) :
item.cacheGroup.maxAsyncRequests;
if(isFinite(maxRequests) && getRequests(chunk) >= maxRequests) continue;
if(newChunk === undefined) {
// Create the new chunk
newChunk = compilation.addChunk(chunkName);
}
// Add graph connections for splitted chunk
chunk.split(newChunk);
// Remove all selected modules from the chunk
for(const module of item.modules) {
chunk.removeModule(module);
module.rewriteChunkInReasons(chunk, [newChunk]);
}
}
// If we successfully created a new chunk or reused one
if(newChunk) {
// Add a note to the chunk
newChunk.chunkReason = isReused ? "reused as split chunk" : "split chunk";
if(item.cacheGroup.key) {
newChunk.chunkReason += ` (cache group: ${item.cacheGroup.key})`;
}
if(chunkName) {
newChunk.chunkReason += ` (name: ${chunkName})`;
// If the choosen name is already an entry point we remove the entry point
const entrypoint = compilation.entrypoints.get(chunkName);
if(entrypoint) {
compilation.entrypoints.delete(chunkName);
entrypoint.remove();
newChunk.entryModule = undefined;
}
}
if(!isReused) {
// Add all modules to the new chunk
for(const module of item.modules) {
GraphHelpers.connectChunkAndModule(newChunk, module);
}
}
// remove all modules from other entries and update size
for(const [key, info] of chunksInfoMap) {
if(isOverlap(info.chunks, item.chunks)) {
const oldSize = info.modules.size;
for(const module of item.modules) {
info.modules.delete(module);
if (!isReused) {
// Add all modules to the new chunk
for (const module of item.modules) {
GraphHelpers.connectChunkAndModule(newChunk, module);
}
if(info.modules.size === 0) {
chunksInfoMap.delete(key);
continue;
}
if(info.modules.size !== oldSize) {
info.size = getModulesSize(info.modules);
if(info.size < info.cacheGroup.minSize)
}
// remove all modules from other entries and update size
for (const [key, info] of chunksInfoMap) {
if (isOverlap(info.chunks, item.chunks)) {
const oldSize = info.modules.size;
for (const module of item.modules) {
info.modules.delete(module);
}
if (info.modules.size === 0) {
chunksInfoMap.delete(key);
continue;
}
if (info.modules.size !== oldSize) {
info.size = getModulesSize(info.modules);
if (info.size < info.cacheGroup.minSize)
chunksInfoMap.delete(key);
}
}
}
changed = true;
}
changed = true;
}
if (changed) return true;
}
if(changed) return true;
});
);
});
}
};

View File

@ -13,8 +13,15 @@ module.exports = class AssetsOverSizeLimitWarning extends WebpackError {
this.name = "AssetsOverSizeLimitWarning";
this.assets = assetsOverSizeLimit;
const assetLists = this.assets.map(asset => `\n ${asset.name} (${SizeFormatHelpers.formatSize(asset.size)})`).join("");
this.message = `asset size limit: The following asset(s) exceed the recommended size limit (${SizeFormatHelpers.formatSize(assetLimit)}).
const assetLists = this.assets
.map(
asset =>
`\n ${asset.name} (${SizeFormatHelpers.formatSize(asset.size)})`
)
.join("");
this.message = `asset size limit: The following asset(s) exceed the recommended size limit (${SizeFormatHelpers.formatSize(
assetLimit
)}).
This can impact web performance.
Assets: ${assetLists}`;

View File

@ -13,14 +13,17 @@ module.exports = class EntrypointsOverSizeLimitWarning extends WebpackError {
this.name = "EntrypointsOverSizeLimitWarning";
this.entrypoints = entrypoints;
const entrypointList = this.entrypoints.map(entrypoint => `\n ${
entrypoint.name
} (${
SizeFormatHelpers.formatSize(entrypoint.size)
})\n${
entrypoint.files.map(asset => ` ${asset}`).join("\n")
}`).join("");
this.message = `entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (${SizeFormatHelpers.formatSize(entrypointLimit)}). This can impact web performance.
const entrypointList = this.entrypoints
.map(
entrypoint =>
`\n ${entrypoint.name} (${SizeFormatHelpers.formatSize(
entrypoint.size
)})\n${entrypoint.files.map(asset => ` ${asset}`).join("\n")}`
)
.join("");
this.message = `entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (${SizeFormatHelpers.formatSize(
entrypointLimit
)}). This can impact web performance.
Entrypoints:${entrypointList}\n`;
Error.captureStackTrace(this, this.constructor);

View File

@ -11,7 +11,8 @@ module.exports = class NoAsyncChunksWarning extends WebpackError {
super();
this.name = "NoAsyncChunksWarning";
this.message = "webpack performance recommendations: \n" +
this.message =
"webpack performance recommendations: \n" +
"You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/";

View File

@ -18,15 +18,14 @@ module.exports = class SizeLimitsPlugin {
const entrypointSizeLimit = this.maxEntrypointSize;
const assetSizeLimit = this.maxAssetSize;
const hints = this.hints;
const assetFilter = this.assetFilter || (asset => !(asset.endsWith(".map")));
const assetFilter = this.assetFilter || (asset => !asset.endsWith(".map"));
compiler.hooks.afterEmit.tap("SizeLimitsPlugin", (compilation) => {
compiler.hooks.afterEmit.tap("SizeLimitsPlugin", compilation => {
const warnings = [];
const getEntrypointSize = entrypoint =>
entrypoint.getFiles()
.reduce((currentSize, file) => {
if(assetFilter(file) && compilation.assets[file]) {
entrypoint.getFiles().reduce((currentSize, file) => {
if (assetFilter(file) && compilation.assets[file]) {
return currentSize + compilation.assets[file].size();
}
@ -34,29 +33,29 @@ module.exports = class SizeLimitsPlugin {
}, 0);
const assetsOverSizeLimit = [];
for(const assetName of Object.keys(compilation.assets)) {
if(!assetFilter(assetName)) {
for (const assetName of Object.keys(compilation.assets)) {
if (!assetFilter(assetName)) {
continue;
}
const asset = compilation.assets[assetName];
const size = asset.size();
if(size > assetSizeLimit) {
if (size > assetSizeLimit) {
assetsOverSizeLimit.push({
name: assetName,
size: size,
size: size
});
asset.isOverSizeLimit = true;
}
}
const entrypointsOverLimit = [];
for(const pair of compilation.entrypoints) {
for (const pair of compilation.entrypoints) {
const name = pair[0];
const entry = pair[1];
const size = getEntrypointSize(entry, compilation);
if(size > entrypointSizeLimit) {
if (size > entrypointSizeLimit) {
entrypointsOverLimit.push({
name: name,
size: size,
@ -66,32 +65,35 @@ module.exports = class SizeLimitsPlugin {
}
}
if(hints) {
if (hints) {
// 1. Individual Chunk: Size < 250kb
// 2. Collective Initial Chunks [entrypoint] (Each Set?): Size < 250kb
// 3. No Async Chunks
// if !1, then 2, if !2 return
if(assetsOverSizeLimit.length > 0) {
if (assetsOverSizeLimit.length > 0) {
warnings.push(
new AssetsOverSizeLimitWarning(
assetsOverSizeLimit,
assetSizeLimit));
new AssetsOverSizeLimitWarning(assetsOverSizeLimit, assetSizeLimit)
);
}
if(entrypointsOverLimit.length > 0) {
if (entrypointsOverLimit.length > 0) {
warnings.push(
new EntrypointsOverSizeLimitWarning(
entrypointsOverLimit,
entrypointSizeLimit));
entrypointSizeLimit
)
);
}
if(warnings.length > 0) {
const hasAsyncChunks = compilation.chunks.filter(chunk => !chunk.canBeInitial()).length > 0;
if (warnings.length > 0) {
const hasAsyncChunks =
compilation.chunks.filter(chunk => !chunk.canBeInitial()).length >
0;
if(!hasAsyncChunks) {
if (!hasAsyncChunks) {
warnings.push(new NoAsyncChunksWarning());
}
if(hints === "error") {
if (hints === "error") {
compilation.errors.push(...warnings);
} else {
compilation.warnings.push(...warnings);

View File

@ -16,7 +16,7 @@ module.exports = class Queue {
dequeue() {
const result = this.iterator.next();
if(result.done) return undefined;
if (result.done) return undefined;
this.set.delete(result.value);
return result.value;
}

View File

@ -11,7 +11,7 @@ class Semaphore {
}
acquire(callback) {
if(this.available > 0) {
if (this.available > 0) {
this.available--;
callback();
} else {
@ -20,7 +20,7 @@ class Semaphore {
}
release() {
if(this.waiters.length > 0) {
if (this.waiters.length > 0) {
const callback = this.waiters.pop();
process.nextTick(callback);
} else {

View File

@ -1,23 +1,23 @@
"use strict";
exports.intersect = sets => {
if(sets.length === 0) return new Set();
if(sets.length === 1) return new Set(sets[0]);
if (sets.length === 0) return new Set();
if (sets.length === 1) return new Set(sets[0]);
let minSize = Infinity;
let minIndex = -1;
for(let i = 0; i < sets.length; i++) {
for (let i = 0; i < sets.length; i++) {
const size = sets[i].size;
if(size < minSize) {
if (size < minSize) {
minIndex = i;
minSize = size;
}
}
const current = new Set(sets[minIndex]);
for(let i = 0; i < sets.length; i++) {
if(i === minIndex) continue;
for (let i = 0; i < sets.length; i++) {
if (i === minIndex) continue;
const set = sets[i];
for(const item of current) {
if(!set.has(item)) {
for (const item of current) {
if (!set.has(item)) {
current.delete(item);
}
}
@ -26,9 +26,9 @@ exports.intersect = sets => {
};
exports.isSubset = (bigSet, smallSet) => {
if(bigSet.size < smallSet.size) return false;
for(const item of smallSet) {
if(!bigSet.has(item)) return false;
if (bigSet.size < smallSet.size) return false;
for (const item of smallSet) {
if (!bigSet.has(item)) return false;
}
return true;
};

View File

@ -1,7 +1,6 @@
"use strict";
class SortableSet extends Set {
constructor(initialIterable, defaultSort) {
super(initialIterable);
this._sortFn = defaultSort;
@ -39,14 +38,14 @@ class SortableSet extends Set {
* @returns {void}
*/
sortWith(sortFn) {
if(this.size === 0 || sortFn === this._lastActiveSortFn) {
if (this.size === 0 || sortFn === this._lastActiveSortFn) {
// already sorted - nothing to do
return;
}
const sortedArray = Array.from(this).sort(sortFn);
super.clear();
for(let i = 0; i < sortedArray.length; i += 1) {
for (let i = 0; i < sortedArray.length; i += 1) {
super.add(sortedArray[i]);
}
this._lastActiveSortFn = sortFn;
@ -65,11 +64,11 @@ class SortableSet extends Set {
* @returns {any} - returns result of fn(this), cached until set changes
*/
getFromCache(fn) {
if(this._cache === undefined) {
if (this._cache === undefined) {
this._cache = new Map();
} else {
const data = this._cache.get(fn);
if(data !== undefined) {
if (data !== undefined) {
return data;
}
}
@ -83,11 +82,11 @@ class SortableSet extends Set {
* @returns {any} - returns result of fn(this), cached until set changes
*/
getFromUnorderedCache(fn) {
if(this._cacheOrderIndependent === undefined) {
if (this._cacheOrderIndependent === undefined) {
this._cacheOrderIndependent = new Map();
} else {
const data = this._cacheOrderIndependent.get(fn);
if(data !== undefined) {
if (data !== undefined) {
return data;
}
}
@ -97,12 +96,11 @@ class SortableSet extends Set {
}
_invalidateCache() {
if(this._cache !== undefined)
this._cache.clear();
if (this._cache !== undefined) this._cache.clear();
}
_invalidateOrderedCache() {
if(this._cacheOrderIndependent !== undefined)
if (this._cacheOrderIndependent !== undefined)
this._cacheOrderIndependent.clear();
}
}

Some files were not shown because too many files have changed in this diff Show More