nav tabs on admin dashboard

This commit is contained in:
2019-03-07 00:20:34 -06:00
parent f73d6ae228
commit e4f473f376
11661 changed files with 216240 additions and 1544253 deletions

View File

@@ -9,36 +9,75 @@ const ParserHelpers = require("./ParserHelpers");
const NullFactory = require("./NullFactory");
/* eslint-disable camelcase */
const REPLACEMENTS = {
__webpack_require__: "__webpack_require__", // eslint-disable-line camelcase
__webpack_public_path__: "__webpack_require__.p", // eslint-disable-line camelcase
__webpack_modules__: "__webpack_require__.m", // eslint-disable-line camelcase
__webpack_chunk_load__: "__webpack_require__.e", // eslint-disable-line camelcase
__non_webpack_require__: "require", // eslint-disable-line camelcase
__webpack_nonce__: "__webpack_require__.nc", // eslint-disable-line camelcase
"require.onError": "__webpack_require__.oe" // eslint-disable-line camelcase
__webpack_require__: "__webpack_require__",
__webpack_public_path__: "__webpack_require__.p",
__webpack_modules__: "__webpack_require__.m",
__webpack_chunk_load__: "__webpack_require__.e",
__non_webpack_require__: "require",
__webpack_nonce__: "__webpack_require__.nc",
"require.onError": "__webpack_require__.oe"
};
const NO_WEBPACK_REQUIRE = {
__non_webpack_require__: true
};
const REPLACEMENT_TYPES = {
__webpack_public_path__: "string", // eslint-disable-line camelcase
__webpack_require__: "function", // eslint-disable-line camelcase
__webpack_modules__: "object", // eslint-disable-line camelcase
__webpack_chunk_load__: "function", // eslint-disable-line camelcase
__webpack_nonce__: "string" // eslint-disable-line camelcase
__webpack_public_path__: "string",
__webpack_require__: "function",
__webpack_modules__: "object",
__webpack_chunk_load__: "function",
__webpack_nonce__: "string"
};
/* eslint-enable camelcase */
class APIPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
compiler.hooks.compilation.tap(
"APIPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
params.normalModuleFactory.plugin("parser", parser => {
Object.keys(REPLACEMENTS).forEach(key => {
parser.plugin(`expression ${key}`, ParserHelpers.toConstantDependency(REPLACEMENTS[key]));
parser.plugin(`evaluate typeof ${key}`, ParserHelpers.evaluateToString(REPLACEMENT_TYPES[key]));
});
});
});
const handler = parser => {
Object.keys(REPLACEMENTS).forEach(key => {
parser.hooks.expression
.for(key)
.tap(
"APIPlugin",
NO_WEBPACK_REQUIRE[key]
? ParserHelpers.toConstantDependency(
parser,
REPLACEMENTS[key]
)
: ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
REPLACEMENTS[key]
)
);
const type = REPLACEMENT_TYPES[key];
if (type) {
parser.hooks.evaluateTypeof
.for(key)
.tap("APIPlugin", ParserHelpers.evaluateToString(type));
}
});
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("APIPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("APIPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("APIPlugin", handler);
}
);
}
}

View File

@@ -5,50 +5,100 @@
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const { ConcatSource } = require("webpack-sources");
const Template = require("./Template");
/** @typedef {import("./Compilation")} Compilation */
/**
* @typedef {Object} AmdMainTemplatePluginOptions
* @param {string=} name the library name
* @property {boolean=} requireAsWrapper
*/
class AmdMainTemplatePlugin {
constructor(name) {
this.name = name;
/**
* @param {AmdMainTemplatePluginOptions} options the plugin options
*/
constructor(options) {
if (!options || typeof options === "string") {
this.name = options;
this.requireAsWrapper = false;
} else {
this.name = options.name;
this.requireAsWrapper = options.requireAsWrapper;
}
}
/**
* @param {Compilation} compilation the compilation instance
* @returns {void}
*/
apply(compilation) {
const mainTemplate = compilation.mainTemplate;
const { mainTemplate, chunkTemplate } = compilation;
compilation.templatesPlugin("render-with-entry", (source, chunk, hash) => {
const externals = chunk.getModules().filter((m) => m.external);
const externalsDepsArray = JSON.stringify(externals.map((m) =>
typeof m.request === "object" ? m.request.amd : m.request
));
const externalsArguments = externals.map((m) =>
Template.toIdentifier(`__WEBPACK_EXTERNAL_MODULE_${m.id}__`)
).join(", ");
const onRenderWithEntry = (source, chunk, hash) => {
const externals = chunk.getModules().filter(m => m.external);
const externalsDepsArray = JSON.stringify(
externals.map(m =>
typeof m.request === "object" ? m.request.amd : m.request
)
);
const externalsArguments = externals
.map(
m => `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(`${m.id}`)}__`
)
.join(", ");
if(this.name) {
const name = mainTemplate.applyPluginsWaterfall("asset-path", this.name, {
if (this.requireAsWrapper) {
return new ConcatSource(
`require(${externalsDepsArray}, function(${externalsArguments}) { return `,
source,
"});"
);
} else if (this.name) {
const name = mainTemplate.getAssetPath(this.name, {
hash,
chunk
});
return new ConcatSource(
`define(${JSON.stringify(name)}, ${externalsDepsArray}, function(${externalsArguments}) { return `, source, "});"
`define(${JSON.stringify(
name
)}, ${externalsDepsArray}, function(${externalsArguments}) { return `,
source,
"});"
);
} else if (externalsArguments) {
return new ConcatSource(
`define(${externalsDepsArray}, function(${externalsArguments}) { return `,
source,
"});"
);
} else if(externalsArguments) {
return new ConcatSource(`define(${externalsDepsArray}, function(${externalsArguments}) { return `, source, "});");
} else {
return new ConcatSource("define(function() { return ", source, "});");
}
});
};
mainTemplate.plugin("global-hash-paths", (paths) => {
if(this.name) paths.push(this.name);
for (const template of [mainTemplate, chunkTemplate]) {
template.hooks.renderWithEntry.tap(
"AmdMainTemplatePlugin",
onRenderWithEntry
);
}
mainTemplate.hooks.globalHashPaths.tap("AmdMainTemplatePlugin", paths => {
if (this.name) {
paths.push(this.name);
}
return paths;
});
mainTemplate.plugin("hash", (hash) => {
mainTemplate.hooks.hash.tap("AmdMainTemplatePlugin", hash => {
hash.update("exports amd");
hash.update(this.name);
if (this.name) {
hash.update(this.name);
}
});
}
}

View File

@@ -3,41 +3,108 @@
Author Tobias Koppers @sokra
*/
"use strict";
const DependenciesBlock = require("./DependenciesBlock");
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("./util/createHash").Hash} Hash */
/** @typedef {TODO} GroupOptions */
module.exports = class AsyncDependenciesBlock extends DependenciesBlock {
constructor(name, module, loc) {
/**
* @param {GroupOptions} groupOptions options for the group
* @param {Module} module the Module object
* @param {DependencyLocation=} loc the line of code
* @param {TODO=} request the request
*/
constructor(groupOptions, module, loc, request) {
super();
this.chunkName = name;
this.chunks = null;
if (typeof groupOptions === "string") {
groupOptions = { name: groupOptions };
} else if (!groupOptions) {
groupOptions = { name: undefined };
}
this.groupOptions = groupOptions;
/** @type {ChunkGroup=} */
this.chunkGroup = undefined;
this.module = module;
this.loc = loc;
this.request = request;
/** @type {DependenciesBlock} */
this.parent = undefined;
}
get chunk() {
throw new Error("`chunk` was been renamed to `chunks` and is now an array");
/**
* @returns {string} The name of the chunk
*/
get chunkName() {
return this.groupOptions.name;
}
set chunk(chunk) {
throw new Error("`chunk` was been renamed to `chunks` and is now an array");
/**
* @param {string} value The new chunk name
* @returns {void}
*/
set chunkName(value) {
this.groupOptions.name = value;
}
/**
* @returns {never} this throws and should never be called
*/
get chunks() {
throw new Error("Moved to AsyncDependenciesBlock.chunkGroup");
}
/**
* @param {never} value setter value
* @returns {never} this is going to throw therefore we should throw type
* assertions by returning never
*/
set chunks(value) {
throw new Error("Moved to AsyncDependenciesBlock.chunkGroup");
}
/**
* @param {Hash} hash the hash used to track block changes, from "crypto" module
* @returns {void}
*/
updateHash(hash) {
hash.update(this.chunkName || "");
hash.update(this.chunks && this.chunks.map((chunk) => {
return chunk.id !== null ? chunk.id : "";
}).join(",") || "");
hash.update(JSON.stringify(this.groupOptions));
hash.update(
(this.chunkGroup &&
this.chunkGroup.chunks
.map(chunk => {
return chunk.id !== null ? chunk.id : "";
})
.join(",")) ||
""
);
super.updateHash(hash);
}
/**
* @returns {void}
*/
disconnect() {
this.chunks = null;
this.chunkGroup = undefined;
super.disconnect();
}
/**
* @returns {void}
*/
unseal() {
this.chunks = null;
this.chunkGroup = undefined;
super.unseal();
}
/**
* @returns {void}
*/
sortItems() {
super.sortItems();
if(this.chunks) {
this.chunks.sort((a, b) => a.compareTo(b));
}
}
};

View File

@@ -1,21 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sean Larkin @thelarkinn
*/
"use strict";
const WebpackError = require("./WebpackError");
module.exports = class AsyncDependencyToInitialChunkWarning extends WebpackError {
constructor(chunkName, module, loc) {
super();
this.name = "AsyncDependencyToInitialChunkWarning";
this.message = `It's not allowed to load an initial chunk on demand. The chunk name "${chunkName}" is already used by an entrypoint.`;
this.module = module;
this.origin = module;
this.originLoc = loc;
Error.captureStackTrace(this, this.constructor);
}
};

View File

@@ -4,33 +4,54 @@
*/
"use strict";
const asyncLib = require("async");
const asyncLib = require("neo-async");
const PrefetchDependency = require("./dependencies/PrefetchDependency");
const NormalModule = require("./NormalModule");
class AutomaticPrefetchPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
const normalModuleFactory = params.normalModuleFactory;
/** @typedef {import("./Compiler")} Compiler */
compilation.dependencyFactories.set(PrefetchDependency, normalModuleFactory);
});
class AutomaticPrefetchPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler Webpack Compiler
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"AutomaticPrefetchPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
PrefetchDependency,
normalModuleFactory
);
}
);
let lastModules = null;
compiler.plugin("after-compile", (compilation, callback) => {
compiler.hooks.afterCompile.tap("AutomaticPrefetchPlugin", compilation => {
lastModules = compilation.modules
.filter(m => m instanceof NormalModule)
.map(m => ({
.map((/** @type {NormalModule} */ m) => ({
context: m.context,
request: m.request
}));
callback();
});
compiler.plugin("make", (compilation, callback) => {
if(!lastModules) return callback();
asyncLib.forEach(lastModules, (m, callback) => {
compilation.prefetch(m.context || compiler.context, new PrefetchDependency(m.request), callback);
}, callback);
});
compiler.hooks.make.tapAsync(
"AutomaticPrefetchPlugin",
(compilation, callback) => {
if (!lastModules) return callback();
asyncLib.forEach(
lastModules,
(m, callback) => {
compilation.prefetch(
m.context || compiler.context,
new PrefetchDependency(m.request),
callback
);
},
callback
);
}
);
}
}
module.exports = AutomaticPrefetchPlugin;

View File

@@ -5,68 +5,118 @@
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const { ConcatSource } = require("webpack-sources");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const Template = require("./Template");
const wrapComment = (str) => {
if(!str.includes("\n")) return `/*! ${str} */`;
return `/*!\n * ${str.split("\n").join("\n * ")}\n */`;
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/BannerPlugin.json");
/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument */
/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginOptions} BannerPluginOptions */
const wrapComment = str => {
if (!str.includes("\n")) {
return Template.toComment(str);
}
return `/*!\n * ${str
.replace(/\*\//g, "* /")
.split("\n")
.join("\n * ")}\n */`;
};
class BannerPlugin {
/**
* @param {BannerPluginArgument} options options object
*/
constructor(options) {
if(arguments.length > 1)
throw new Error("BannerPlugin only takes one argument (pass an options object)");
if(typeof options === "string")
if (arguments.length > 1) {
throw new Error(
"BannerPlugin only takes one argument (pass an options object)"
);
}
validateOptions(schema, options, "Banner Plugin");
if (typeof options === "string" || typeof options === "function") {
options = {
banner: options
};
this.options = options || {};
this.banner = this.options.raw ? options.banner : wrapComment(options.banner);
}
/** @type {BannerPluginOptions} */
this.options = options;
const bannerOption = options.banner;
if (typeof bannerOption === "function") {
const getBanner = bannerOption;
this.banner = this.options.raw
? getBanner
: data => wrapComment(getBanner(data));
} else {
const banner = this.options.raw
? bannerOption
: wrapComment(bannerOption);
this.banner = () => banner;
}
}
apply(compiler) {
const options = this.options;
const banner = this.banner;
const matchObject = ModuleFilenameHelpers.matchObject.bind(
undefined,
options
);
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunk-assets", (chunks, callback) => {
chunks.forEach((chunk) => {
if(options.entryOnly && !chunk.isInitial()) return;
chunk.files
.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options))
.forEach((file) => {
let basename;
let query = "";
let filename = file;
const hash = compilation.hash;
const querySplit = filename.indexOf("?");
compiler.hooks.compilation.tap("BannerPlugin", compilation => {
compilation.hooks.optimizeChunkAssets.tap("BannerPlugin", chunks => {
for (const chunk of chunks) {
if (options.entryOnly && !chunk.canBeInitial()) {
continue;
}
if(querySplit >= 0) {
query = filename.substr(querySplit);
filename = filename.substr(0, querySplit);
}
for (const file of chunk.files) {
if (!matchObject(file)) {
continue;
}
const lastSlashIndex = filename.lastIndexOf("/");
let basename;
let query = "";
let filename = file;
const hash = compilation.hash;
const querySplit = filename.indexOf("?");
if(lastSlashIndex === -1) {
basename = filename;
} else {
basename = filename.substr(lastSlashIndex + 1);
}
if (querySplit >= 0) {
query = filename.substr(querySplit);
filename = filename.substr(0, querySplit);
}
const comment = compilation.getPath(banner, {
hash,
chunk,
filename,
basename,
query,
});
const lastSlashIndex = filename.lastIndexOf("/");
return compilation.assets[file] = new ConcatSource(comment, "\n", compilation.assets[file]);
});
});
callback();
if (lastSlashIndex === -1) {
basename = filename;
} else {
basename = filename.substr(lastSlashIndex + 1);
}
const data = {
hash,
chunk,
filename,
basename,
query
};
const comment = compilation.getPath(banner(data), data);
compilation.assets[file] = new ConcatSource(
comment,
"\n",
compilation.assets[file]
);
}
}
});
});
}

View File

@@ -5,54 +5,82 @@
"use strict";
class BasicEvaluatedExpression {
const TypeUnknown = 0;
const TypeNull = 1;
const TypeString = 2;
const TypeNumber = 3;
const TypeBoolean = 4;
const TypeRegExp = 5;
const TypeConditional = 6;
const TypeArray = 7;
const TypeConstArray = 8;
const TypeIdentifier = 9;
const TypeWrapped = 10;
const TypeTemplateString = 11;
class BasicEvaluatedExpression {
constructor() {
this.type = TypeUnknown;
this.range = null;
this.falsy = false;
this.truthy = false;
this.bool = null;
this.number = null;
this.regExp = null;
this.string = null;
this.quasis = null;
this.parts = null;
this.array = null;
this.items = null;
this.options = null;
this.prefix = null;
this.postfix = null;
this.wrappedInnerExpressions = null;
this.expression = null;
}
isNull() {
return !!this.null;
return this.type === TypeNull;
}
isString() {
return Object.prototype.hasOwnProperty.call(this, "string");
return this.type === TypeString;
}
isNumber() {
return Object.prototype.hasOwnProperty.call(this, "number");
return this.type === TypeNumber;
}
isBoolean() {
return Object.prototype.hasOwnProperty.call(this, "bool");
return this.type === TypeBoolean;
}
isRegExp() {
return Object.prototype.hasOwnProperty.call(this, "regExp");
return this.type === TypeRegExp;
}
isConditional() {
return Object.prototype.hasOwnProperty.call(this, "options");
return this.type === TypeConditional;
}
isArray() {
return Object.prototype.hasOwnProperty.call(this, "items");
return this.type === TypeArray;
}
isConstArray() {
return Object.prototype.hasOwnProperty.call(this, "array");
return this.type === TypeConstArray;
}
isIdentifier() {
return Object.prototype.hasOwnProperty.call(this, "identifier");
return this.type === TypeIdentifier;
}
isWrapped() {
return Object.prototype.hasOwnProperty.call(this, "prefix") || Object.prototype.hasOwnProperty.call(this, "postfix");
return this.type === TypeWrapped;
}
isTemplateString() {
return Object.prototype.hasOwnProperty.call(this, "quasis");
return this.type === TypeTemplateString;
}
isTruthy() {
@@ -64,112 +92,133 @@ class BasicEvaluatedExpression {
}
asBool() {
if(this.truthy) return true;
else if(this.falsy) return false;
else if(this.isBoolean()) return this.bool;
else if(this.isNull()) return false;
else if(this.isString()) return !!this.string;
else if(this.isNumber()) return !!this.number;
else if(this.isRegExp()) return true;
else if(this.isArray()) return true;
else if(this.isConstArray()) return true;
else if(this.isWrapped()) return this.prefix && this.prefix.asBool() || this.postfix && this.postfix.asBool() ? true : undefined;
else if(this.isTemplateString()) {
if(this.quasis.length === 1) return this.quasis[0].asBool();
for(let i = 0; i < this.quasis.length; i++) {
if(this.quasis[i].asBool()) return true;
}
// can't tell if string will be empty without executing
if (this.truthy) return true;
if (this.falsy) return false;
if (this.isBoolean()) return this.bool;
if (this.isNull()) return false;
if (this.isString()) return this.string !== "";
if (this.isNumber()) return this.number !== 0;
if (this.isRegExp()) return true;
if (this.isArray()) return true;
if (this.isConstArray()) return true;
if (this.isWrapped()) {
return (this.prefix && this.prefix.asBool()) ||
(this.postfix && this.postfix.asBool())
? true
: undefined;
}
if (this.isTemplateString()) {
const str = this.asString();
if (typeof str === "string") return str !== "";
}
return undefined;
}
setString(str) {
if(str === null)
delete this.string;
else
this.string = str;
asString() {
if (this.isBoolean()) return `${this.bool}`;
if (this.isNull()) return "null";
if (this.isString()) return this.string;
if (this.isNumber()) return `${this.number}`;
if (this.isRegExp()) return `${this.regExp}`;
if (this.isArray()) {
let array = [];
for (const item of this.items) {
const itemStr = item.asString();
if (itemStr === undefined) return undefined;
array.push(itemStr);
}
return `${array}`;
}
if (this.isConstArray()) return `${this.array}`;
if (this.isTemplateString()) {
let str = "";
for (const part of this.parts) {
const partStr = part.asString();
if (partStr === undefined) return undefined;
str += partStr;
}
return str;
}
return undefined;
}
setString(string) {
this.type = TypeString;
this.string = string;
return this;
}
setNull() {
this.null = true;
this.type = TypeNull;
return this;
}
setNumber(num) {
if(num === null)
delete this.number;
else
this.number = num;
setNumber(number) {
this.type = TypeNumber;
this.number = number;
return this;
}
setBoolean(bool) {
if(bool === null)
delete this.bool;
else
this.bool = bool;
this.type = TypeBoolean;
this.bool = bool;
return this;
}
setRegExp(regExp) {
if(regExp === null)
delete this.regExp;
else
this.regExp = regExp;
this.type = TypeRegExp;
this.regExp = regExp;
return this;
}
setIdentifier(identifier) {
if(identifier === null)
delete this.identifier;
else
this.identifier = identifier;
this.type = TypeIdentifier;
this.identifier = identifier;
return this;
}
setWrapped(prefix, postfix) {
setWrapped(prefix, postfix, innerExpressions) {
this.type = TypeWrapped;
this.prefix = prefix;
this.postfix = postfix;
return this;
}
unsetWrapped() {
delete this.prefix;
delete this.postfix;
this.wrappedInnerExpressions = innerExpressions;
return this;
}
setOptions(options) {
if(options === null)
delete this.options;
else
this.options = options;
this.type = TypeConditional;
this.options = options;
return this;
}
addOptions(options) {
if (!this.options) {
this.type = TypeConditional;
this.options = [];
}
for (const item of options) {
this.options.push(item);
}
return this;
}
setItems(items) {
if(items === null)
delete this.items;
else
this.items = items;
this.type = TypeArray;
this.items = items;
return this;
}
setArray(array) {
if(array === null)
delete this.array;
else
this.array = array;
this.type = TypeConstArray;
this.array = array;
return this;
}
setTemplateString(quasis) {
if(quasis === null)
delete this.quasis;
else
this.quasis = quasis;
setTemplateString(quasis, parts, kind) {
this.type = TypeTemplateString;
this.quasis = quasis;
this.parts = parts;
this.templateStringKind = kind;
return this;
}
@@ -185,19 +234,15 @@ class BasicEvaluatedExpression {
return this;
}
addOptions(options) {
if(!this.options) this.options = [];
options.forEach(item => {
this.options.push(item);
}, this);
return this;
}
setRange(range) {
this.range = range;
return this;
}
setExpression(expression) {
this.expression = expression;
return this;
}
}
module.exports = BasicEvaluatedExpression;

View File

@@ -4,92 +4,99 @@
*/
"use strict";
const asyncLib = require("async");
const asyncLib = require("neo-async");
class CachePlugin {
constructor(cache) {
this.cache = cache || {};
this.FS_ACCURENCY = 2000;
this.FS_ACCURACY = 2000;
}
apply(compiler) {
if(Array.isArray(compiler.compilers)) {
if (Array.isArray(compiler.compilers)) {
compiler.compilers.forEach((c, idx) => {
c.apply(new CachePlugin(this.cache[idx] = this.cache[idx] || {}));
new CachePlugin((this.cache[idx] = this.cache[idx] || {})).apply(c);
});
} else {
const registerCacheToCompiler = (compiler, cache) => {
compiler.plugin("this-compilation", compilation => {
// TODO remove notCacheable for webpack 4
if(!compilation.notCacheable) {
compilation.cache = cache;
compilation.plugin("child-compiler", (childCompiler, compilerName, compilerIndex) => {
if(cache) {
compiler.hooks.thisCompilation.tap("CachePlugin", compilation => {
compilation.cache = cache;
compilation.hooks.childCompiler.tap(
"CachePlugin",
(childCompiler, compilerName, compilerIndex) => {
if (cache) {
let childCache;
if(!cache.children) cache.children = {};
if(!cache.children[compilerName]) cache.children[compilerName] = [];
if(cache.children[compilerName][compilerIndex])
if (!cache.children) {
cache.children = {};
}
if (!cache.children[compilerName]) {
cache.children[compilerName] = [];
}
if (cache.children[compilerName][compilerIndex]) {
childCache = cache.children[compilerName][compilerIndex];
else
cache.children[compilerName].push(childCache = {});
} else {
cache.children[compilerName].push((childCache = {}));
}
registerCacheToCompiler(childCompiler, childCache);
}
});
} else if(this.watching) {
compilation.warnings.push(
new Error(`CachePlugin - Cache cannot be used because of: ${compilation.notCacheable}`)
);
}
}
);
});
};
registerCacheToCompiler(compiler, this.cache);
compiler.plugin("watch-run", (compiler, callback) => {
compiler.hooks.watchRun.tap("CachePlugin", () => {
this.watching = true;
callback();
});
compiler.plugin("run", (compiler, callback) => {
if(!compiler._lastCompilationFileDependencies) return callback();
compiler.hooks.run.tapAsync("CachePlugin", (compiler, callback) => {
if (!compiler._lastCompilationFileDependencies) {
return callback();
}
const fs = compiler.inputFileSystem;
const fileTs = compiler.fileTimestamps = {};
asyncLib.forEach(compiler._lastCompilationFileDependencies, (file, callback) => {
fs.stat(file, (err, stat) => {
if(err) {
if(err.code === "ENOENT") return callback();
return callback(err);
const fileTs = (compiler.fileTimestamps = new Map());
asyncLib.forEach(
compiler._lastCompilationFileDependencies,
(file, callback) => {
fs.stat(file, (err, stat) => {
if (err) {
if (err.code === "ENOENT") return callback();
return callback(err);
}
if (stat.mtime) this.applyMtime(+stat.mtime);
fileTs.set(file, +stat.mtime || Infinity);
callback();
});
},
err => {
if (err) return callback(err);
for (const [file, ts] of fileTs) {
fileTs.set(file, ts + this.FS_ACCURACY);
}
if(stat.mtime)
this.applyMtime(+stat.mtime);
fileTs[file] = +stat.mtime || Infinity;
callback();
});
}, err => {
if(err) return callback(err);
Object.keys(fileTs).forEach(key => {
fileTs[key] += this.FS_ACCURENCY;
});
callback();
});
}
);
});
compiler.plugin("after-compile", function(compilation, callback) {
compilation.compiler._lastCompilationFileDependencies = compilation.fileDependencies;
compilation.compiler._lastCompilationContextDependencies = compilation.contextDependencies;
callback();
compiler.hooks.afterCompile.tap("CachePlugin", compilation => {
compilation.compiler._lastCompilationFileDependencies =
compilation.fileDependencies;
compilation.compiler._lastCompilationContextDependencies =
compilation.contextDependencies;
});
}
}
/* istanbul ignore next */
applyMtime(mtime) {
if(this.FS_ACCURENCY > 1 && mtime % 2 !== 0)
this.FS_ACCURENCY = 1;
else if(this.FS_ACCURENCY > 10 && mtime % 20 !== 0)
this.FS_ACCURENCY = 10;
else if(this.FS_ACCURENCY > 100 && mtime % 200 !== 0)
this.FS_ACCURENCY = 100;
else if(this.FS_ACCURENCY > 1000 && mtime % 2000 !== 0)
this.FS_ACCURENCY = 1000;
if (this.FS_ACCURACY > 1 && mtime % 2 !== 0) this.FS_ACCURACY = 1;
else if (this.FS_ACCURACY > 10 && mtime % 20 !== 0) this.FS_ACCURACY = 10;
else if (this.FS_ACCURACY > 100 && mtime % 200 !== 0)
this.FS_ACCURACY = 100;
else if (this.FS_ACCURACY > 1000 && mtime % 2000 !== 0)
this.FS_ACCURACY = 1000;
}
}
module.exports = CachePlugin;

View File

@@ -6,44 +6,62 @@
const WebpackError = require("./WebpackError");
module.exports = class CaseSensitiveModulesWarning extends WebpackError {
constructor(modules) {
super();
/** @typedef {import("./Module")} Module */
this.name = "CaseSensitiveModulesWarning";
const sortedModules = this._sort(modules);
const modulesList = this._moduleMessages(sortedModules);
this.message = "There are multiple modules with names that only differ in casing.\n" +
"This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.\n" +
`Use equal casing. Compare these module identifiers:\n${modulesList}`;
this.origin = this.module = sortedModules[0];
/**
* @param {Module[]} modules the modules to be sorted
* @returns {Module[]} sorted version of original modules
*/
const sortModules = modules => {
return modules.slice().sort((a, b) => {
const aIdent = a.identifier();
const bIdent = b.identifier();
/* istanbul ignore next */
if (aIdent < bIdent) return -1;
/* istanbul ignore next */
if (aIdent > bIdent) return 1;
/* istanbul ignore next */
return 0;
});
};
Error.captureStackTrace(this, this.constructor);
}
_sort(modules) {
return modules.slice().sort((a, b) => {
a = a.identifier();
b = b.identifier();
/* istanbul ignore next */
if(a < b) return -1;
/* istanbul ignore next */
if(a > b) return 1;
/* istanbul ignore next */
return 0;
});
}
_moduleMessages(modules) {
return modules.map((m) => {
/**
* @param {Module[]} modules each module from throw
* @returns {string} each message from provided moduels
*/
const createModulesListMessage = modules => {
return modules
.map(m => {
let message = `* ${m.identifier()}`;
const validReasons = m.reasons.filter((reason) => reason.module);
const validReasons = m.reasons.filter(reason => reason.module);
if(validReasons.length > 0) {
if (validReasons.length > 0) {
message += `\n Used by ${validReasons.length} module(s), i. e.`;
message += `\n ${validReasons[0].module.identifier()}`;
}
return message;
}).join("\n");
}
})
.join("\n");
};
class CaseSensitiveModulesWarning extends WebpackError {
/**
* Creates an instance of CaseSensitiveModulesWarning.
* @param {Module[]} modules modules that were detected
*/
constructor(modules) {
const sortedModules = sortModules(modules);
const modulesList = createModulesListMessage(sortedModules);
super(`There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
${modulesList}`);
this.name = "CaseSensitiveModulesWarning";
this.origin = this.module = sortedModules[0];
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = CaseSensitiveModulesWarning;

971
node_modules/webpack/lib/Chunk.js generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,15 @@
const WebpackError = require("./WebpackError");
/** @typedef {import("./Chunk")} Chunk */
class ChunkRenderError extends WebpackError {
/**
* Create a new ChunkRenderError
* @param {Chunk} chunk A chunk
* @param {string} file Related file
* @param {Error} error Original error
*/
constructor(chunk, file, error) {
super();

View File

@@ -4,33 +4,84 @@
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const Template = require("./Template");
const { Tapable, SyncWaterfallHook, SyncHook } = require("tapable");
module.exports = class ChunkTemplate extends Template {
/** @typedef {import("./ModuleTemplate")} ModuleTemplate */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Module")} Module} */
/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate} */
/** @typedef {import("./util/createHash").Hash} Hash} */
/**
* @typedef {Object} RenderManifestOptions
* @property {Chunk} chunk the chunk used to render
* @property {string} hash
* @property {string} fullHash
* @property {TODO} outputOptions
* @property {{javascript: ModuleTemplate, webassembly: ModuleTemplate}} moduleTemplates
* @property {Map<TODO, TODO>} dependencyTemplates
*/
module.exports = class ChunkTemplate extends Tapable {
constructor(outputOptions) {
super(outputOptions);
super();
this.outputOptions = outputOptions || {};
this.hooks = {
/** @type {SyncWaterfallHook<TODO[], RenderManifestOptions>} */
renderManifest: new SyncWaterfallHook(["result", "options"]),
modules: new SyncWaterfallHook([
"source",
"chunk",
"moduleTemplate",
"dependencyTemplates"
]),
render: new SyncWaterfallHook([
"source",
"chunk",
"moduleTemplate",
"dependencyTemplates"
]),
renderWithEntry: new SyncWaterfallHook(["source", "chunk"]),
hash: new SyncHook(["hash"]),
hashForChunk: new SyncHook(["hash", "chunk"])
};
}
render(chunk, moduleTemplate, dependencyTemplates) {
const moduleSources = this.renderChunkModules(chunk, moduleTemplate, dependencyTemplates);
const core = this.applyPluginsWaterfall("modules", moduleSources, chunk, moduleTemplate, dependencyTemplates);
let source = this.applyPluginsWaterfall("render", core, chunk, moduleTemplate, dependencyTemplates);
if(chunk.hasEntryModule()) {
source = this.applyPluginsWaterfall("render-with-entry", source, chunk);
}
chunk.rendered = true;
return new ConcatSource(source, ";");
/**
*
* @param {RenderManifestOptions} options render manifest options
* @returns {TODO[]} returns render manifest
*/
getRenderManifest(options) {
const result = [];
this.hooks.renderManifest.call(result, options);
return result;
}
/**
* Updates hash with information from this template
* @param {Hash} hash the hash to update
* @returns {void}
*/
updateHash(hash) {
hash.update("ChunkTemplate");
hash.update("2");
this.applyPlugins("hash", hash);
this.hooks.hash.call(hash);
}
updateHashForChunk(hash, chunk) {
/**
* TODO webpack 5: remove moduleTemplate and dependencyTemplates
* Updates hash with chunk-specific information from this template
* @param {Hash} hash the hash to update
* @param {Chunk} chunk the chunk
* @param {ModuleTemplate} moduleTemplate ModuleTemplate instance for render
* @param {Map<Function, DependencyTemplate>} dependencyTemplates dependency templates
* @returns {void}
*/
updateHashForChunk(hash, chunk, moduleTemplate, dependencyTemplates) {
this.updateHash(hash);
this.applyPlugins("hash-for-chunk", hash, chunk);
this.hooks.hashForChunk.call(hash, chunk);
}
};

View File

@@ -8,50 +8,63 @@ const ConstDependency = require("./dependencies/ConstDependency");
const NullFactory = require("./NullFactory");
const jsonLoaderPath = require.resolve("json-loader");
const matchJson = /\.json$/i;
/** @typedef {import("./Compiler")} Compiler */
class CompatibilityPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler Webpack Compiler
* @returns {void}
*/
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
compiler.hooks.compilation.tap(
"CompatibilityPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
params.normalModuleFactory.plugin("parser", (parser, parserOptions) => {
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("CompatibilityPlugin", (parser, parserOptions) => {
if (
parserOptions.browserify !== undefined &&
!parserOptions.browserify
)
return;
if(typeof parserOptions.browserify !== "undefined" && !parserOptions.browserify)
return;
parser.plugin("call require", (expr) => {
// support for browserify style require delegator: "require(o, !0)"
if(expr.arguments.length !== 2) return;
const second = parser.evaluateExpression(expr.arguments[1]);
if(!second.isBoolean()) return;
if(second.asBool() !== true) return;
const dep = new ConstDependency("require", expr.callee.range);
dep.loc = expr.loc;
if(parser.state.current.dependencies.length > 1) {
const last = parser.state.current.dependencies[parser.state.current.dependencies.length - 1];
if(last.critical && last.request === "." && last.userRequest === "." && last.recursive)
parser.state.current.dependencies.pop();
}
parser.state.current.addDependency(dep);
return true;
});
});
params.normalModuleFactory.plugin("after-resolve", (data, done) => {
// if this is a json file and there are no loaders active, we use the json-loader in order to avoid parse errors
// @see https://github.com/webpack/webpack/issues/3363
if(matchJson.test(data.request) && data.loaders.length === 0) {
data.loaders.push({
loader: jsonLoaderPath
parser.hooks.call
.for("require")
.tap("CompatibilityPlugin", expr => {
// support for browserify style require delegator: "require(o, !0)"
if (expr.arguments.length !== 2) return;
const second = parser.evaluateExpression(expr.arguments[1]);
if (!second.isBoolean()) return;
if (second.asBool() !== true) return;
const dep = new ConstDependency("require", expr.callee.range);
dep.loc = expr.loc;
if (parser.state.current.dependencies.length > 1) {
const last =
parser.state.current.dependencies[
parser.state.current.dependencies.length - 1
];
if (
last.critical &&
last.options &&
last.options.request === "." &&
last.userRequest === "." &&
last.options.recursive
)
parser.state.current.dependencies.pop();
}
parser.state.current.addDependency(dep);
return true;
});
});
}
done(null, data);
});
});
}
);
}
}
module.exports = CompatibilityPlugin;

3014
node_modules/webpack/lib/Compilation.js generated vendored

File diff suppressed because it is too large Load Diff

778
node_modules/webpack/lib/Compiler.js generated vendored
View File

@@ -4,277 +4,282 @@
*/
"use strict";
const parseJson = require("json-parse-better-errors");
const asyncLib = require("neo-async");
const path = require("path");
const Tapable = require("tapable");
const { Source } = require("webpack-sources");
const util = require("util");
const {
Tapable,
SyncHook,
SyncBailHook,
AsyncParallelHook,
AsyncSeriesHook
} = require("tapable");
const Compilation = require("./Compilation");
const Stats = require("./Stats");
const Watching = require("./Watching");
const NormalModuleFactory = require("./NormalModuleFactory");
const ContextModuleFactory = require("./ContextModuleFactory");
const ResolverFactory = require("./ResolverFactory");
const makePathsRelative = require("./util/identifier").makePathsRelative;
const RequestShortener = require("./RequestShortener");
const { makePathsRelative } = require("./util/identifier");
const ConcurrentCompilationError = require("./ConcurrentCompilationError");
class Watching {
constructor(compiler, watchOptions, handler) {
this.startTime = null;
this.invalid = false;
this.handler = handler;
this.callbacks = [];
this.closed = false;
if(typeof watchOptions === "number") {
this.watchOptions = {
aggregateTimeout: watchOptions
};
} else if(watchOptions && typeof watchOptions === "object") {
this.watchOptions = Object.assign({}, watchOptions);
} else {
this.watchOptions = {};
}
this.watchOptions.aggregateTimeout = this.watchOptions.aggregateTimeout || 200;
this.compiler = compiler;
this.running = true;
this.compiler.readRecords(err => {
if(err) return this._done(err);
/** @typedef {import("../declarations/WebpackOptions").Entry} Entry */
/** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
this._go();
});
}
_go() {
this.startTime = Date.now();
this.running = true;
this.invalid = false;
this.compiler.applyPluginsAsync("watch-run", this, err => {
if(err) return this._done(err);
const onCompiled = (err, compilation) => {
if(err) return this._done(err);
if(this.invalid) return this._done();
if(this.compiler.applyPluginsBailResult("should-emit", compilation) === false) {
return this._done(null, compilation);
}
this.compiler.emitAssets(compilation, err => {
if(err) return this._done(err);
if(this.invalid) return this._done();
this.compiler.emitRecords(err => {
if(err) return this._done(err);
if(compilation.applyPluginsBailResult("need-additional-pass")) {
compilation.needAdditionalPass = true;
const stats = new Stats(compilation);
stats.startTime = this.startTime;
stats.endTime = Date.now();
this.compiler.applyPlugins("done", stats);
this.compiler.applyPluginsAsync("additional-pass", err => {
if(err) return this._done(err);
this.compiler.compile(onCompiled);
});
return;
}
return this._done(null, compilation);
});
});
};
this.compiler.compile(onCompiled);
});
}
_getStats(compilation) {
const stats = new Stats(compilation);
stats.startTime = this.startTime;
stats.endTime = Date.now();
return stats;
}
_done(err, compilation) {
this.running = false;
if(this.invalid) return this._go();
const stats = compilation ? this._getStats(compilation) : null;
if(err) {
this.compiler.applyPlugins("failed", err);
this.handler(err, stats);
return;
}
this.compiler.applyPlugins("done", stats);
this.handler(null, stats);
if(!this.closed) {
this.watch(compilation.fileDependencies, compilation.contextDependencies, compilation.missingDependencies);
}
this.callbacks.forEach(cb => cb());
this.callbacks.length = 0;
}
watch(files, dirs, missing) {
this.pausedWatcher = null;
this.watcher = this.compiler.watchFileSystem.watch(files, dirs, missing, this.startTime, this.watchOptions, (err, filesModified, contextModified, missingModified, fileTimestamps, contextTimestamps) => {
this.pausedWatcher = this.watcher;
this.watcher = null;
if(err) return this.handler(err);
this.compiler.fileTimestamps = fileTimestamps;
this.compiler.contextTimestamps = contextTimestamps;
this.invalidate();
}, (fileName, changeTime) => {
this.compiler.applyPlugins("invalid", fileName, changeTime);
});
}
invalidate(callback) {
if(callback) {
this.callbacks.push(callback);
}
if(this.watcher) {
this.pausedWatcher = this.watcher;
this.watcher.pause();
this.watcher = null;
}
if(this.running) {
this.invalid = true;
return false;
} else {
this._go();
}
}
close(callback) {
if(callback === undefined) callback = function() {};
this.closed = true;
if(this.watcher) {
this.watcher.close();
this.watcher = null;
}
if(this.pausedWatcher) {
this.pausedWatcher.close();
this.pausedWatcher = null;
}
if(this.running) {
this.invalid = true;
this._done = () => {
this.compiler.applyPlugins("watch-close");
callback();
};
} else {
this.compiler.applyPlugins("watch-close");
callback();
}
}
}
/**
* @typedef {Object} CompilationParams
* @property {NormalModuleFactory} normalModuleFactory
* @property {ContextModuleFactory} contextModuleFactory
* @property {Set<string>} compilationDependencies
*/
class Compiler extends Tapable {
constructor() {
constructor(context) {
super();
this.hooks = {
/** @type {SyncBailHook<Compilation>} */
shouldEmit: new SyncBailHook(["compilation"]),
/** @type {AsyncSeriesHook<Stats>} */
done: new AsyncSeriesHook(["stats"]),
/** @type {AsyncSeriesHook<>} */
additionalPass: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook<Compiler>} */
beforeRun: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook<Compiler>} */
run: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook<Compilation>} */
emit: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<Compilation>} */
afterEmit: new AsyncSeriesHook(["compilation"]),
/** @type {SyncHook<Compilation, CompilationParams>} */
thisCompilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook<Compilation, CompilationParams>} */
compilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook<NormalModuleFactory>} */
normalModuleFactory: new SyncHook(["normalModuleFactory"]),
/** @type {SyncHook<ContextModuleFactory>} */
contextModuleFactory: new SyncHook(["contextModulefactory"]),
/** @type {AsyncSeriesHook<CompilationParams>} */
beforeCompile: new AsyncSeriesHook(["params"]),
/** @type {SyncHook<CompilationParams>} */
compile: new SyncHook(["params"]),
/** @type {AsyncParallelHook<Compilation>} */
make: new AsyncParallelHook(["compilation"]),
/** @type {AsyncSeriesHook<Compilation>} */
afterCompile: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<Compiler>} */
watchRun: new AsyncSeriesHook(["compiler"]),
/** @type {SyncHook<Error>} */
failed: new SyncHook(["error"]),
/** @type {SyncHook<string, string>} */
invalid: new SyncHook(["filename", "changeTime"]),
/** @type {SyncHook} */
watchClose: new SyncHook([]),
// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook} */
environment: new SyncHook([]),
/** @type {SyncHook} */
afterEnvironment: new SyncHook([]),
/** @type {SyncHook<Compiler>} */
afterPlugins: new SyncHook(["compiler"]),
/** @type {SyncHook<Compiler>} */
afterResolvers: new SyncHook(["compiler"]),
/** @type {SyncBailHook<string, Entry>} */
entryOption: new SyncBailHook(["context", "entry"])
};
this._pluginCompat.tap("Compiler", options => {
switch (options.name) {
case "additional-pass":
case "before-run":
case "run":
case "emit":
case "after-emit":
case "before-compile":
case "make":
case "after-compile":
case "watch-run":
options.async = true;
break;
}
});
/** @type {string=} */
this.name = undefined;
/** @type {Compilation=} */
this.parentCompilation = undefined;
/** @type {string} */
this.outputPath = "";
this.outputFileSystem = null;
this.inputFileSystem = null;
/** @type {string|null} */
this.recordsInputPath = null;
/** @type {string|null} */
this.recordsOutputPath = null;
this.records = {};
this.removedFiles = new Set();
/** @type {Map<string, number>} */
this.fileTimestamps = new Map();
/** @type {Map<string, number>} */
this.contextTimestamps = new Map();
/** @type {ResolverFactory} */
this.resolverFactory = new ResolverFactory();
this.fileTimestamps = {};
this.contextTimestamps = {};
// TODO remove in webpack 5
this.resolvers = {
normal: null,
loader: null,
context: null
};
this.parser = {
plugin: util.deprecate(
(hook, fn) => {
this.plugin("compilation", (compilation, data) => {
data.normalModuleFactory.plugin("parser", parser => {
parser.plugin(hook, fn);
});
normal: {
plugins: util.deprecate((hook, fn) => {
this.resolverFactory.plugin("resolver normal", resolver => {
resolver.plugin(hook, fn);
});
},
"webpack: Using compiler.parser is deprecated.\n" +
"Use compiler.plugin(\"compilation\", function(compilation, data) {\n data.normalModuleFactory.plugin(\"parser\", function(parser, options) { parser.plugin(/* ... */); });\n}); instead. "
),
apply: util.deprecate(
() => {
const args = arguments;
this.plugin("compilation", (compilation, data) => {
data.normalModuleFactory.plugin("parser", parser => {
parser.apply.apply(parser, args);
});
}, "webpack: Using compiler.resolvers.normal is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver normal", resolver => {\n resolver.plugin(/* … */);\n}); instead.'),
apply: util.deprecate((...args) => {
this.resolverFactory.plugin("resolver normal", resolver => {
resolver.apply(...args);
});
},
"webpack: Using compiler.parser is deprecated.\n" +
"Use compiler.plugin(\"compilation\", function(compilation, data) {\n data.normalModuleFactory.plugin(\"parser\", function(parser, options) { parser.apply(/* ... */); });\n}); instead. "
)
}, "webpack: Using compiler.resolvers.normal is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver normal", resolver => {\n resolver.apply(/* … */);\n}); instead.')
},
loader: {
plugins: util.deprecate((hook, fn) => {
this.resolverFactory.plugin("resolver loader", resolver => {
resolver.plugin(hook, fn);
});
}, "webpack: Using compiler.resolvers.loader is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver loader", resolver => {\n resolver.plugin(/* … */);\n}); instead.'),
apply: util.deprecate((...args) => {
this.resolverFactory.plugin("resolver loader", resolver => {
resolver.apply(...args);
});
}, "webpack: Using compiler.resolvers.loader is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver loader", resolver => {\n resolver.apply(/* … */);\n}); instead.')
},
context: {
plugins: util.deprecate((hook, fn) => {
this.resolverFactory.plugin("resolver context", resolver => {
resolver.plugin(hook, fn);
});
}, "webpack: Using compiler.resolvers.context is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver context", resolver => {\n resolver.plugin(/* … */);\n}); instead.'),
apply: util.deprecate((...args) => {
this.resolverFactory.plugin("resolver context", resolver => {
resolver.apply(...args);
});
}, "webpack: Using compiler.resolvers.context is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver context", resolver => {\n resolver.apply(/* … */);\n}); instead.')
}
};
this.options = {};
/** @type {WebpackOptions} */
this.options = /** @type {WebpackOptions} */ ({});
this.context = context;
this.requestShortener = new RequestShortener(context);
/** @type {boolean} */
this.running = false;
/** @type {boolean} */
this.watchMode = false;
/** @private @type {WeakMap<Source, { sizeOnlySource: SizeOnlySource, writtenTo: Map<string, number> }>} */
this._assetEmittingSourceCache = new WeakMap();
/** @private @type {Map<string, number>} */
this._assetEmittingWrittenFiles = new Map();
}
watch(watchOptions, handler) {
this.fileTimestamps = {};
this.contextTimestamps = {};
const watching = new Watching(this, watchOptions, handler);
return watching;
if (this.running) return handler(new ConcurrentCompilationError());
this.running = true;
this.watchMode = true;
this.fileTimestamps = new Map();
this.contextTimestamps = new Map();
this.removedFiles = new Set();
return new Watching(this, watchOptions, handler);
}
run(callback) {
if (this.running) return callback(new ConcurrentCompilationError());
const finalCallback = (err, stats) => {
this.running = false;
if (err) {
this.hooks.failed.call(err);
}
if (callback !== undefined) return callback(err, stats);
};
const startTime = Date.now();
const onCompiled = (err, compilation) => {
if(err) return callback(err);
this.running = true;
if(this.applyPluginsBailResult("should-emit", compilation) === false) {
const onCompiled = (err, compilation) => {
if (err) return finalCallback(err);
if (this.hooks.shouldEmit.call(compilation) === false) {
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.applyPlugins("done", stats);
return callback(null, stats);
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
return;
}
this.emitAssets(compilation, err => {
if(err) return callback(err);
if (err) return finalCallback(err);
if(compilation.applyPluginsBailResult("need-additional-pass")) {
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true;
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.applyPlugins("done", stats);
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
this.applyPluginsAsync("additional-pass", err => {
if(err) return callback(err);
this.compile(onCompiled);
this.hooks.additionalPass.callAsync(err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
return;
}
this.emitRecords(err => {
if(err) return callback(err);
if (err) return finalCallback(err);
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.applyPlugins("done", stats);
return callback(null, stats);
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
});
});
};
this.applyPluginsAsync("before-run", this, err => {
if(err) return callback(err);
this.hooks.beforeRun.callAsync(this, err => {
if (err) return finalCallback(err);
this.applyPluginsAsync("run", this, err => {
if(err) return callback(err);
this.hooks.run.callAsync(this, err => {
if (err) return finalCallback(err);
this.readRecords(err => {
if(err) return callback(err);
if (err) return finalCallback(err);
this.compile(onCompiled);
});
@@ -284,16 +289,17 @@ class Compiler extends Tapable {
runAsChild(callback) {
this.compile((err, compilation) => {
if(err) return callback(err);
if (err) return callback(err);
this.parentCompilation.children.push(compilation);
Object.keys(compilation.assets).forEach(name => {
for (const name of Object.keys(compilation.assets)) {
this.parentCompilation.assets[name] = compilation.assets[name];
});
}
const entries = Object.keys(compilation.entrypoints).map(name => {
return compilation.entrypoints[name].chunks;
}).reduce((array, chunks) => {
const entries = Array.from(
compilation.entrypoints.values(),
ep => ep.chunks
).reduce((array, chunks) => {
return array.concat(chunks);
}, []);
@@ -302,105 +308,187 @@ class Compiler extends Tapable {
}
purgeInputFileSystem() {
if(this.inputFileSystem && this.inputFileSystem.purge)
if (this.inputFileSystem && this.inputFileSystem.purge) {
this.inputFileSystem.purge();
}
}
emitAssets(compilation, callback) {
let outputPath;
const emitFiles = err => {
if (err) return callback(err);
const emitFiles = (err) => {
if(err) return callback(err);
asyncLib.forEachLimit(
compilation.assets,
15,
(source, file, callback) => {
let targetFile = file;
const queryStringIdx = targetFile.indexOf("?");
if (queryStringIdx >= 0) {
targetFile = targetFile.substr(0, queryStringIdx);
}
require("async").forEach(Object.keys(compilation.assets), (file, callback) => {
const writeOut = err => {
if (err) return callback(err);
const targetPath = this.outputFileSystem.join(
outputPath,
targetFile
);
// TODO webpack 5 remove futureEmitAssets option and make it on by default
if (this.options.output.futureEmitAssets) {
// check if the target file has already been written by this Compiler
const targetFileGeneration = this._assetEmittingWrittenFiles.get(
targetPath
);
let targetFile = file;
const queryStringIdx = targetFile.indexOf("?");
if(queryStringIdx >= 0) {
targetFile = targetFile.substr(0, queryStringIdx);
}
// create an cache entry for this Source if not already existing
let cacheEntry = this._assetEmittingSourceCache.get(source);
if (cacheEntry === undefined) {
cacheEntry = {
sizeOnlySource: undefined,
writtenTo: new Map()
};
this._assetEmittingSourceCache.set(source, cacheEntry);
}
// if the target file has already been written
if (targetFileGeneration !== undefined) {
// check if the Source has been written to this target file
const writtenGeneration = cacheEntry.writtenTo.get(targetPath);
if (writtenGeneration === targetFileGeneration) {
// if yes, we skip writing the file
// as it's already there
// (we assume one doesn't remove files while the Compiler is running)
return callback();
}
}
// get the binary (Buffer) content from the Source
/** @type {Buffer} */
let content;
if (typeof source.buffer === "function") {
content = source.buffer();
} else {
const bufferOrString = source.source();
if (Buffer.isBuffer(bufferOrString)) {
content = bufferOrString;
} else {
content = Buffer.from(bufferOrString, "utf8");
}
}
// Create a replacement resource which only allows to ask for size
// This allows to GC all memory allocated by the Source
// (expect when the Source is stored in any other cache)
cacheEntry.sizeOnlySource = new SizeOnlySource(content.length);
compilation.assets[file] = cacheEntry.sizeOnlySource;
// Write the file to output file system
this.outputFileSystem.writeFile(targetPath, content, err => {
if (err) return callback(err);
// information marker that the asset has been emitted
compilation.emittedAssets.add(file);
// cache the information that the Source has been written to that location
const newGeneration =
targetFileGeneration === undefined
? 1
: targetFileGeneration + 1;
cacheEntry.writtenTo.set(targetPath, newGeneration);
this._assetEmittingWrittenFiles.set(targetPath, newGeneration);
callback();
});
} else {
if (source.existsAt === targetPath) {
source.emitted = false;
return callback();
}
let content = source.source();
if (!Buffer.isBuffer(content)) {
content = Buffer.from(content, "utf8");
}
source.existsAt = targetPath;
source.emitted = true;
this.outputFileSystem.writeFile(targetPath, content, callback);
}
};
if (targetFile.match(/\/|\\/)) {
const dir = path.dirname(targetFile);
this.outputFileSystem.mkdirp(
this.outputFileSystem.join(outputPath, dir),
writeOut
);
} else {
writeOut();
}
},
err => {
if (err) return callback(err);
this.hooks.afterEmit.callAsync(compilation, err => {
if (err) return callback(err);
const writeOut = (err) => {
if(err) return callback(err);
const targetPath = this.outputFileSystem.join(outputPath, targetFile);
const source = compilation.assets[file];
if(source.existsAt === targetPath) {
source.emitted = false;
return callback();
}
let content = source.source();
if(!Buffer.isBuffer(content)) {
content = new Buffer(content, "utf8"); // eslint-disable-line
}
source.existsAt = targetPath;
source.emitted = true;
this.outputFileSystem.writeFile(targetPath, content, callback);
};
if(targetFile.match(/\/|\\/)) {
const dir = path.dirname(targetFile);
this.outputFileSystem.mkdirp(this.outputFileSystem.join(outputPath, dir), writeOut);
} else writeOut();
}, err => {
if(err) return callback(err);
afterEmit.call(this);
});
});
}
);
};
this.applyPluginsAsync("emit", compilation, err => {
if(err) return callback(err);
this.hooks.emit.callAsync(compilation, err => {
if (err) return callback(err);
outputPath = compilation.getPath(this.outputPath);
this.outputFileSystem.mkdirp(outputPath, emitFiles);
});
function afterEmit() {
this.applyPluginsAsyncSeries1("after-emit", compilation, err => {
if(err) return callback(err);
return callback();
});
}
}
emitRecords(callback) {
if(!this.recordsOutputPath) return callback();
if (!this.recordsOutputPath) return callback();
const idx1 = this.recordsOutputPath.lastIndexOf("/");
const idx2 = this.recordsOutputPath.lastIndexOf("\\");
let recordsOutputPathDirectory = null;
if(idx1 > idx2) recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx1);
if(idx1 < idx2) recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx2);
if(!recordsOutputPathDirectory) return writeFile.call(this);
this.outputFileSystem.mkdirp(recordsOutputPathDirectory, err => {
if(err) return callback(err);
writeFile.call(this);
});
function writeFile() {
this.outputFileSystem.writeFile(this.recordsOutputPath, JSON.stringify(this.records, undefined, 2), callback);
if (idx1 > idx2) {
recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx1);
} else if (idx1 < idx2) {
recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx2);
}
const writeFile = () => {
this.outputFileSystem.writeFile(
this.recordsOutputPath,
JSON.stringify(this.records, undefined, 2),
callback
);
};
if (!recordsOutputPathDirectory) {
return writeFile();
}
this.outputFileSystem.mkdirp(recordsOutputPathDirectory, err => {
if (err) return callback(err);
writeFile();
});
}
readRecords(callback) {
if(!this.recordsInputPath) {
if (!this.recordsInputPath) {
this.records = {};
return callback();
}
this.inputFileSystem.stat(this.recordsInputPath, err => {
// It doesn't exist
// We can ignore this.
if(err) return callback();
if (err) return callback();
this.inputFileSystem.readFile(this.recordsInputPath, (err, content) => {
if(err) return callback(err);
if (err) return callback(err);
try {
this.records = JSON.parse(content.toString("utf-8"));
} catch(e) {
this.records = parseJson(content.toString("utf-8"));
} catch (e) {
e.message = "Cannot parse records: " + e.message;
return callback(e);
}
@@ -410,38 +498,66 @@ class Compiler extends Tapable {
});
}
createChildCompiler(compilation, compilerName, compilerIndex, outputOptions, plugins) {
const childCompiler = new Compiler();
if(Array.isArray(plugins)) {
plugins.forEach(plugin => childCompiler.apply(plugin));
createChildCompiler(
compilation,
compilerName,
compilerIndex,
outputOptions,
plugins
) {
const childCompiler = new Compiler(this.context);
if (Array.isArray(plugins)) {
for (const plugin of plugins) {
plugin.apply(childCompiler);
}
}
for(const name in this._plugins) {
if(["make", "compile", "emit", "after-emit", "invalid", "done", "this-compilation"].indexOf(name) < 0)
childCompiler._plugins[name] = this._plugins[name].slice();
for (const name in this.hooks) {
if (
![
"make",
"compile",
"emit",
"afterEmit",
"invalid",
"done",
"thisCompilation"
].includes(name)
) {
if (childCompiler.hooks[name]) {
childCompiler.hooks[name].taps = this.hooks[name].taps.slice();
}
}
}
childCompiler.name = compilerName;
childCompiler.outputPath = this.outputPath;
childCompiler.inputFileSystem = this.inputFileSystem;
childCompiler.outputFileSystem = null;
childCompiler.resolvers = this.resolvers;
childCompiler.resolverFactory = this.resolverFactory;
childCompiler.fileTimestamps = this.fileTimestamps;
childCompiler.contextTimestamps = this.contextTimestamps;
const relativeCompilerName = makePathsRelative(this.context, compilerName);
if(!this.records[relativeCompilerName]) this.records[relativeCompilerName] = [];
if(this.records[relativeCompilerName][compilerIndex])
if (!this.records[relativeCompilerName]) {
this.records[relativeCompilerName] = [];
}
if (this.records[relativeCompilerName][compilerIndex]) {
childCompiler.records = this.records[relativeCompilerName][compilerIndex];
else
this.records[relativeCompilerName].push(childCompiler.records = {});
} else {
this.records[relativeCompilerName].push((childCompiler.records = {}));
}
childCompiler.options = Object.create(this.options);
childCompiler.options.output = Object.create(childCompiler.options.output);
for(const name in outputOptions) {
for (const name in outputOptions) {
childCompiler.options.output[name] = outputOptions[name];
}
childCompiler.parentCompilation = compilation;
compilation.applyPlugins("child-compiler", childCompiler, compilerName, compilerIndex);
compilation.hooks.childCompiler.call(
childCompiler,
compilerName,
compilerIndex
);
return childCompiler;
}
@@ -461,20 +577,24 @@ class Compiler extends Tapable {
compilation.name = this.name;
compilation.records = this.records;
compilation.compilationDependencies = params.compilationDependencies;
this.applyPlugins("this-compilation", compilation, params);
this.applyPlugins("compilation", compilation, params);
this.hooks.thisCompilation.call(compilation, params);
this.hooks.compilation.call(compilation, params);
return compilation;
}
createNormalModuleFactory() {
const normalModuleFactory = new NormalModuleFactory(this.options.context, this.resolvers, this.options.module || {});
this.applyPlugins("normal-module-factory", normalModuleFactory);
const normalModuleFactory = new NormalModuleFactory(
this.options.context,
this.resolverFactory,
this.options.module || {}
);
this.hooks.normalModuleFactory.call(normalModuleFactory);
return normalModuleFactory;
}
createContextModuleFactory() {
const contextModuleFactory = new ContextModuleFactory(this.resolvers, this.inputFileSystem);
this.applyPlugins("context-module-factory", contextModuleFactory);
const contextModuleFactory = new ContextModuleFactory(this.resolverFactory);
this.hooks.contextModuleFactory.call(contextModuleFactory);
return contextModuleFactory;
}
@@ -482,30 +602,30 @@ class Compiler extends Tapable {
const params = {
normalModuleFactory: this.createNormalModuleFactory(),
contextModuleFactory: this.createContextModuleFactory(),
compilationDependencies: []
compilationDependencies: new Set()
};
return params;
}
compile(callback) {
const params = this.newCompilationParams();
this.applyPluginsAsync("before-compile", params, err => {
if(err) return callback(err);
this.hooks.beforeCompile.callAsync(params, err => {
if (err) return callback(err);
this.applyPlugins("compile", params);
this.hooks.compile.call(params);
const compilation = this.newCompilation(params);
this.applyPluginsParallel("make", compilation, err => {
if(err) return callback(err);
this.hooks.make.callAsync(compilation, err => {
if (err) return callback(err);
compilation.finish();
compilation.seal(err => {
if(err) return callback(err);
if (err) return callback(err);
this.applyPluginsAsync("after-compile", compilation, err => {
if(err) return callback(err);
this.hooks.afterCompile.callAsync(compilation, err => {
if (err) return callback(err);
return callback(null, compilation);
});
@@ -515,5 +635,49 @@ class Compiler extends Tapable {
}
}
Compiler.Watching = Watching;
module.exports = Compiler;
class SizeOnlySource extends Source {
constructor(size) {
super();
this._size = size;
}
_error() {
return new Error(
"Content and Map of this Source is no longer available (only size() is supported)"
);
}
size() {
return this._size;
}
/**
* @param {any} options options
* @returns {string} the source
*/
source(options) {
throw this._error();
}
node() {
throw this._error();
}
listMap() {
throw this._error();
}
map() {
throw this._error();
}
listNode() {
throw this._error();
}
updateHash() {
throw this._error();
}
}

View File

@@ -7,53 +7,336 @@ const ConstDependency = require("./dependencies/ConstDependency");
const NullFactory = require("./NullFactory");
const ParserHelpers = require("./ParserHelpers");
const getQuery = (request) => {
const getQuery = request => {
const i = request.indexOf("?");
return request.indexOf("?") < 0 ? "" : request.substr(i);
return i !== -1 ? request.substr(i) : "";
};
const collectDeclaration = (declarations, pattern) => {
const stack = [pattern];
while (stack.length > 0) {
const node = stack.pop();
switch (node.type) {
case "Identifier":
declarations.add(node.name);
break;
case "ArrayPattern":
for (const element of node.elements) {
if (element) {
stack.push(element);
}
}
break;
case "AssignmentPattern":
stack.push(node.left);
break;
case "ObjectPattern":
for (const property of node.properties) {
stack.push(property.value);
}
break;
case "RestElement":
stack.push(node.argument);
break;
}
}
};
const getHoistedDeclarations = (branch, includeFunctionDeclarations) => {
const declarations = new Set();
const stack = [branch];
while (stack.length > 0) {
const node = stack.pop();
// Some node could be `null` or `undefined`.
if (!node) continue;
switch (node.type) {
// Walk through control statements to look for hoisted declarations.
// Some branches are skipped since they do not allow declarations.
case "BlockStatement":
for (const stmt of node.body) {
stack.push(stmt);
}
break;
case "IfStatement":
stack.push(node.consequent);
stack.push(node.alternate);
break;
case "ForStatement":
stack.push(node.init);
stack.push(node.body);
break;
case "ForInStatement":
case "ForOfStatement":
stack.push(node.left);
stack.push(node.body);
break;
case "DoWhileStatement":
case "WhileStatement":
case "LabeledStatement":
stack.push(node.body);
break;
case "SwitchStatement":
for (const cs of node.cases) {
for (const consequent of cs.consequent) {
stack.push(consequent);
}
}
break;
case "TryStatement":
stack.push(node.block);
if (node.handler) {
stack.push(node.handler.body);
}
stack.push(node.finalizer);
break;
case "FunctionDeclaration":
if (includeFunctionDeclarations) {
collectDeclaration(declarations, node.id);
}
break;
case "VariableDeclaration":
if (node.kind === "var") {
for (const decl of node.declarations) {
collectDeclaration(declarations, decl.id);
}
}
break;
}
}
return Array.from(declarations);
};
class ConstPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
compiler.hooks.compilation.tap(
"ConstPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
params.normalModuleFactory.plugin("parser", parser => {
parser.plugin("statement if", function(statement) {
const param = this.evaluateExpression(statement.test);
const bool = param.asBool();
if(typeof bool === "boolean") {
if(statement.test.type !== "Literal") {
const dep = new ConstDependency(`${bool}`, param.range);
dep.loc = statement.loc;
this.state.current.addDependency(dep);
const handler = parser => {
parser.hooks.statementIf.tap("ConstPlugin", statement => {
const param = parser.evaluateExpression(statement.test);
const bool = param.asBool();
if (typeof bool === "boolean") {
if (statement.test.type !== "Literal") {
const dep = new ConstDependency(`${bool}`, param.range);
dep.loc = statement.loc;
parser.state.current.addDependency(dep);
}
const branchToRemove = bool
? statement.alternate
: statement.consequent;
if (branchToRemove) {
// Before removing the dead branch, the hoisted declarations
// must be collected.
//
// Given the following code:
//
// if (true) f() else g()
// if (false) {
// function f() {}
// const g = function g() {}
// if (someTest) {
// let a = 1
// var x, {y, z} = obj
// }
// } else {
// …
// }
//
// the generated code is:
//
// if (true) f() else {}
// if (false) {
// var f, x, y, z; (in loose mode)
// var x, y, z; (in strict mode)
// } else {
// …
// }
//
// NOTE: When code runs in strict mode, `var` declarations
// are hoisted but `function` declarations don't.
//
let declarations;
if (parser.scope.isStrict) {
// If the code runs in strict mode, variable declarations
// using `var` must be hoisted.
declarations = getHoistedDeclarations(branchToRemove, false);
} else {
// Otherwise, collect all hoisted declaration.
declarations = getHoistedDeclarations(branchToRemove, true);
}
let replacement;
if (declarations.length > 0) {
replacement = `{ var ${declarations.join(", ")}; }`;
} else {
replacement = "{}";
}
const dep = new ConstDependency(
replacement,
branchToRemove.range
);
dep.loc = branchToRemove.loc;
parser.state.current.addDependency(dep);
}
return bool;
}
return bool;
}
});
parser.plugin("expression ?:", function(expression) {
const param = this.evaluateExpression(expression.test);
const bool = param.asBool();
if(typeof bool === "boolean") {
if(expression.test.type !== "Literal") {
const dep = new ConstDependency(` ${bool}`, param.range);
dep.loc = expression.loc;
this.state.current.addDependency(dep);
});
parser.hooks.expressionConditionalOperator.tap(
"ConstPlugin",
expression => {
const param = parser.evaluateExpression(expression.test);
const bool = param.asBool();
if (typeof bool === "boolean") {
if (expression.test.type !== "Literal") {
const dep = new ConstDependency(` ${bool}`, param.range);
dep.loc = expression.loc;
parser.state.current.addDependency(dep);
}
// Expressions do not hoist.
// It is safe to remove the dead branch.
//
// Given the following code:
//
// false ? someExpression() : otherExpression();
//
// the generated code is:
//
// false ? undefined : otherExpression();
//
const branchToRemove = bool
? expression.alternate
: expression.consequent;
const dep = new ConstDependency(
"undefined",
branchToRemove.range
);
dep.loc = branchToRemove.loc;
parser.state.current.addDependency(dep);
return bool;
}
}
return bool;
}
});
parser.plugin("evaluate Identifier __resourceQuery", function(expr) {
if(!this.state.module) return;
return ParserHelpers.evaluateToString(getQuery(this.state.module.resource))(expr);
});
parser.plugin("expression __resourceQuery", function() {
if(!this.state.module) return;
this.state.current.addVariable("__resourceQuery", JSON.stringify(getQuery(this.state.module.resource)));
return true;
});
});
});
);
parser.hooks.expressionLogicalOperator.tap(
"ConstPlugin",
expression => {
if (
expression.operator === "&&" ||
expression.operator === "||"
) {
const param = parser.evaluateExpression(expression.left);
const bool = param.asBool();
if (typeof bool === "boolean") {
// Expressions do not hoist.
// It is safe to remove the dead branch.
//
// ------------------------------------------
//
// Given the following code:
//
// falsyExpression() && someExpression();
//
// the generated code is:
//
// falsyExpression() && false;
//
// ------------------------------------------
//
// Given the following code:
//
// truthyExpression() && someExpression();
//
// the generated code is:
//
// true && someExpression();
//
// ------------------------------------------
//
// Given the following code:
//
// truthyExpression() || someExpression();
//
// the generated code is:
//
// truthyExpression() || false;
//
// ------------------------------------------
//
// Given the following code:
//
// falsyExpression() || someExpression();
//
// the generated code is:
//
// false && someExpression();
//
const keepRight =
(expression.operator === "&&" && bool) ||
(expression.operator === "||" && !bool);
if (param.isBoolean() || keepRight) {
// for case like
//
// return'development'===process.env.NODE_ENV&&'foo'
//
// we need a space before the bool to prevent result like
//
// returnfalse&&'foo'
//
const dep = new ConstDependency(` ${bool}`, param.range);
dep.loc = expression.loc;
parser.state.current.addDependency(dep);
} else {
parser.walkExpression(expression.left);
}
if (!keepRight) {
const dep = new ConstDependency(
"false",
expression.right.range
);
dep.loc = expression.loc;
parser.state.current.addDependency(dep);
}
return keepRight;
}
}
}
);
parser.hooks.evaluateIdentifier
.for("__resourceQuery")
.tap("ConstPlugin", expr => {
if (!parser.state.module) return;
return ParserHelpers.evaluateToString(
getQuery(parser.state.module.resource)
)(expr);
});
parser.hooks.expression
.for("__resourceQuery")
.tap("ConstPlugin", () => {
if (!parser.state.module) return;
parser.state.current.addVariable(
"__resourceQuery",
JSON.stringify(getQuery(parser.state.module.resource))
);
return true;
});
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("ConstPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("ConstPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("ConstPlugin", handler);
}
);
}
}

View File

@@ -1,13 +1,24 @@
"use strict";
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./ContextModuleFactory")} ContextModuleFactory */
class ContextExclusionPlugin {
/**
* @param {RegExp} negativeMatcher Matcher regular expression
*/
constructor(negativeMatcher) {
this.negativeMatcher = negativeMatcher;
}
/**
* Apply the plugin
* @param {Compiler} compiler Webpack Compiler
* @returns {void}
*/
apply(compiler) {
compiler.plugin("context-module-factory", (cmf) => {
cmf.plugin("context-module-files", (files) => {
compiler.hooks.contextModuleFactory.tap("ContextExclusionPlugin", cmf => {
cmf.hooks.contextModuleFiles.tap("ContextExclusionPlugin", files => {
return files.filter(filePath => !this.negativeMatcher.test(filePath));
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -4,166 +4,259 @@
*/
"use strict";
const asyncLib = require("async");
const asyncLib = require("neo-async");
const path = require("path");
const Tapable = require("tapable");
const {
Tapable,
AsyncSeriesWaterfallHook,
SyncWaterfallHook
} = require("tapable");
const ContextModule = require("./ContextModule");
const ContextElementDependency = require("./dependencies/ContextElementDependency");
/** @typedef {import("./Module")} Module */
const EMPTY_RESOLVE_OPTIONS = {};
module.exports = class ContextModuleFactory extends Tapable {
constructor(resolvers) {
constructor(resolverFactory) {
super();
this.resolvers = resolvers;
this.hooks = {
/** @type {AsyncSeriesWaterfallHook<TODO>} */
beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
/** @type {AsyncSeriesWaterfallHook<TODO>} */
afterResolve: new AsyncSeriesWaterfallHook(["data"]),
/** @type {SyncWaterfallHook<string[]>} */
contextModuleFiles: new SyncWaterfallHook(["files"]),
/** @type {SyncWaterfallHook<TODO[]>} */
alternatives: new AsyncSeriesWaterfallHook(["modules"])
};
this._pluginCompat.tap("ContextModuleFactory", options => {
switch (options.name) {
case "before-resolve":
case "after-resolve":
case "alternatives":
options.async = true;
break;
}
});
this.resolverFactory = resolverFactory;
}
create(data, callback) {
const context = data.context;
const dependencies = data.dependencies;
const resolveOptions = data.resolveOptions;
const dependency = dependencies[0];
this.applyPluginsAsyncWaterfall("before-resolve", {
context: context,
request: dependency.request,
recursive: dependency.recursive,
regExp: dependency.regExp,
async: dependency.async,
dependencies: dependencies
}, (err, result) => {
if(err) return callback(err);
// Ignored
if(!result) return callback();
const context = result.context;
const request = result.request;
const recursive = result.recursive;
const regExp = result.regExp;
const asyncContext = result.async;
const dependencies = result.dependencies;
let loaders, resource, loadersPrefix = "";
const idx = request.lastIndexOf("!");
if(idx >= 0) {
loaders = request.substr(0, idx + 1);
let i;
for(i = 0; i < loaders.length && loaders[i] === "!"; i++) {
loadersPrefix += "!";
}
loaders = loaders.substr(i).replace(/!+$/, "").replace(/!!+/g, "!");
if(loaders === "") loaders = [];
else loaders = loaders.split("!");
resource = request.substr(idx + 1);
} else {
loaders = [];
resource = request;
}
const resolvers = this.resolvers;
asyncLib.parallel([
function(callback) {
resolvers.context.resolve({}, context, resource, function(err, result) {
if(err) return callback(err);
callback(null, result);
});
},
function(callback) {
asyncLib.map(loaders, function(loader, callback) {
resolvers.loader.resolve({}, context, loader, function(err, result) {
if(err) return callback(err);
callback(null, result);
});
}, callback);
}
], (err, result) => {
if(err) return callback(err);
this.applyPluginsAsyncWaterfall("after-resolve", {
loaders: loadersPrefix + result[1].join("!") + (result[1].length > 0 ? "!" : ""),
resource: result[0],
recursive: recursive,
regExp: regExp,
async: asyncContext,
this.hooks.beforeResolve.callAsync(
Object.assign(
{
context: context,
dependencies: dependencies,
resolveDependencies: this.resolveDependencies.bind(this)
}, function(err, result) {
if(err) return callback(err);
resolveOptions
},
dependency.options
),
(err, beforeResolveResult) => {
if (err) return callback(err);
// Ignored
if(!result) return callback();
// Ignored
if (!beforeResolveResult) return callback();
return callback(null, new ContextModule(result.resolveDependencies, result.resource, result.recursive, result.regExp, result.loaders, result.async, dependency.chunkName));
});
});
});
const context = beforeResolveResult.context;
const request = beforeResolveResult.request;
const resolveOptions = beforeResolveResult.resolveOptions;
let loaders,
resource,
loadersPrefix = "";
const idx = request.lastIndexOf("!");
if (idx >= 0) {
let loadersRequest = request.substr(0, idx + 1);
let i;
for (
i = 0;
i < loadersRequest.length && loadersRequest[i] === "!";
i++
) {
loadersPrefix += "!";
}
loadersRequest = loadersRequest
.substr(i)
.replace(/!+$/, "")
.replace(/!!+/g, "!");
if (loadersRequest === "") {
loaders = [];
} else {
loaders = loadersRequest.split("!");
}
resource = request.substr(idx + 1);
} else {
loaders = [];
resource = request;
}
const contextResolver = this.resolverFactory.get(
"context",
resolveOptions || EMPTY_RESOLVE_OPTIONS
);
const loaderResolver = this.resolverFactory.get(
"loader",
EMPTY_RESOLVE_OPTIONS
);
asyncLib.parallel(
[
callback => {
contextResolver.resolve(
{},
context,
resource,
{},
(err, result) => {
if (err) return callback(err);
callback(null, result);
}
);
},
callback => {
asyncLib.map(
loaders,
(loader, callback) => {
loaderResolver.resolve(
{},
context,
loader,
{},
(err, result) => {
if (err) return callback(err);
callback(null, result);
}
);
},
callback
);
}
],
(err, result) => {
if (err) return callback(err);
this.hooks.afterResolve.callAsync(
Object.assign(
{
addon:
loadersPrefix +
result[1].join("!") +
(result[1].length > 0 ? "!" : ""),
resource: result[0],
resolveDependencies: this.resolveDependencies.bind(this)
},
beforeResolveResult
),
(err, result) => {
if (err) return callback(err);
// Ignored
if (!result) return callback();
return callback(
null,
new ContextModule(result.resolveDependencies, result)
);
}
);
}
);
}
);
}
resolveDependencies(fs, resource, recursive, regExp, callback) {
resolveDependencies(fs, options, callback) {
const cmf = this;
if(!regExp || !resource)
return callback(null, []);
(function addDirectory(directory, callback) {
let resource = options.resource;
let resourceQuery = options.resourceQuery;
let recursive = options.recursive;
let regExp = options.regExp;
let include = options.include;
let exclude = options.exclude;
if (!regExp || !resource) return callback(null, []);
const addDirectory = (directory, callback) => {
fs.readdir(directory, (err, files) => {
if(err) return callback(err);
files = cmf.applyPluginsWaterfall("context-module-files", files);
if(!files || files.length === 0) return callback(null, []);
asyncLib.map(files.filter(function(p) {
return p.indexOf(".") !== 0;
}), (seqment, callback) => {
if (err) return callback(err);
files = cmf.hooks.contextModuleFiles.call(files);
if (!files || files.length === 0) return callback(null, []);
asyncLib.map(
files.filter(p => p.indexOf(".") !== 0),
(segment, callback) => {
const subResource = path.join(directory, segment);
const subResource = path.join(directory, seqment);
if (!exclude || !subResource.match(exclude)) {
fs.stat(subResource, (err, stat) => {
if (err) {
if (err.code === "ENOENT") {
// ENOENT is ok here because the file may have been deleted between
// the readdir and stat calls.
return callback();
} else {
return callback(err);
}
}
fs.stat(subResource, (err, stat) => {
if(err) {
if(err.code === "ENOENT") {
// ENOENT is ok here because the file may have been deleted between
// the readdir and stat calls.
return callback();
} else {
return callback(err);
}
}
if (stat.isDirectory()) {
if (!recursive) return callback();
addDirectory.call(this, subResource, callback);
} else if (
stat.isFile() &&
(!include || subResource.match(include))
) {
const obj = {
context: resource,
request:
"." +
subResource.substr(resource.length).replace(/\\/g, "/")
};
if(stat.isDirectory()) {
if(!recursive) return callback();
addDirectory.call(this, subResource, callback);
} else if(stat.isFile()) {
const obj = {
context: resource,
request: "." + subResource.substr(resource.length).replace(/\\/g, "/")
};
this.applyPluginsAsyncWaterfall("alternatives", [obj], (err, alternatives) => {
if(err) return callback(err);
alternatives = alternatives.filter(function(obj) {
return regExp.test(obj.request);
}).map(function(obj) {
const dep = new ContextElementDependency(obj.request);
dep.optional = true;
return dep;
});
callback(null, alternatives);
this.hooks.alternatives.callAsync(
[obj],
(err, alternatives) => {
if (err) return callback(err);
alternatives = alternatives
.filter(obj => regExp.test(obj.request))
.map(obj => {
const dep = new ContextElementDependency(
obj.request + resourceQuery,
obj.request
);
dep.optional = true;
return dep;
});
callback(null, alternatives);
}
);
} else {
callback();
}
});
} else {
callback();
}
},
(err, result) => {
if (err) return callback(err);
} else callback();
if (!result) return callback(null, []);
});
}, (err, result) => {
if(err) return callback(err);
if(!result) return callback(null, []);
callback(null, result.filter(function(i) {
return !!i;
}).reduce(function(a, i) {
return a.concat(i);
}, []));
});
callback(
null,
result.filter(Boolean).reduce((a, i) => a.concat(i), [])
);
}
);
});
}.call(this, resource, callback));
};
addDirectory(resource, callback);
}
};

View File

@@ -8,26 +8,37 @@ const path = require("path");
const ContextElementDependency = require("./dependencies/ContextElementDependency");
class ContextReplacementPlugin {
constructor(resourceRegExp, newContentResource, newContentRecursive, newContentRegExp) {
constructor(
resourceRegExp,
newContentResource,
newContentRecursive,
newContentRegExp
) {
this.resourceRegExp = resourceRegExp;
if(typeof newContentResource === "function") {
if (typeof newContentResource === "function") {
this.newContentCallback = newContentResource;
} else if(typeof newContentResource === "string" && typeof newContentRecursive === "object") {
} else if (
typeof newContentResource === "string" &&
typeof newContentRecursive === "object"
) {
this.newContentResource = newContentResource;
this.newContentCreateContextMap = (fs, callback) => {
callback(null, newContentRecursive);
};
} else if(typeof newContentResource === "string" && typeof newContentRecursive === "function") {
} else if (
typeof newContentResource === "string" &&
typeof newContentRecursive === "function"
) {
this.newContentResource = newContentResource;
this.newContentCreateContextMap = newContentRecursive;
} else {
if(typeof newContentResource !== "string") {
if (typeof newContentResource !== "string") {
newContentRegExp = newContentRecursive;
newContentRecursive = newContentResource;
newContentResource = undefined;
}
if(typeof newContentRecursive !== "boolean") {
if (typeof newContentRecursive !== "boolean") {
newContentRegExp = newContentRecursive;
newContentRecursive = undefined;
}
@@ -45,67 +56,78 @@ class ContextReplacementPlugin {
const newContentRegExp = this.newContentRegExp;
const newContentCreateContextMap = this.newContentCreateContextMap;
compiler.plugin("context-module-factory", (cmf) => {
cmf.plugin("before-resolve", (result, callback) => {
if(!result) return callback();
if(resourceRegExp.test(result.request)) {
if(typeof newContentResource !== "undefined")
compiler.hooks.contextModuleFactory.tap("ContextReplacementPlugin", cmf => {
cmf.hooks.beforeResolve.tap("ContextReplacementPlugin", result => {
if (!result) return;
if (resourceRegExp.test(result.request)) {
if (newContentResource !== undefined) {
result.request = newContentResource;
if(typeof newContentRecursive !== "undefined")
}
if (newContentRecursive !== undefined) {
result.recursive = newContentRecursive;
if(typeof newContentRegExp !== "undefined")
}
if (newContentRegExp !== undefined) {
result.regExp = newContentRegExp;
if(typeof newContentCallback === "function") {
}
if (typeof newContentCallback === "function") {
newContentCallback(result);
} else {
result.dependencies.forEach((d) => {
if(d.critical)
d.critical = false;
});
for (const d of result.dependencies) {
if (d.critical) d.critical = false;
}
}
}
return callback(null, result);
return result;
});
cmf.plugin("after-resolve", (result, callback) => {
if(!result) return callback();
if(resourceRegExp.test(result.resource)) {
if(typeof newContentResource !== "undefined")
cmf.hooks.afterResolve.tap("ContextReplacementPlugin", result => {
if (!result) return;
if (resourceRegExp.test(result.resource)) {
if (newContentResource !== undefined) {
result.resource = path.resolve(result.resource, newContentResource);
if(typeof newContentRecursive !== "undefined")
}
if (newContentRecursive !== undefined) {
result.recursive = newContentRecursive;
if(typeof newContentRegExp !== "undefined")
}
if (newContentRegExp !== undefined) {
result.regExp = newContentRegExp;
if(typeof newContentCreateContextMap === "function")
result.resolveDependencies = createResolveDependenciesFromContextMap(newContentCreateContextMap);
if(typeof newContentCallback === "function") {
}
if (typeof newContentCreateContextMap === "function") {
result.resolveDependencies = createResolveDependenciesFromContextMap(
newContentCreateContextMap
);
}
if (typeof newContentCallback === "function") {
const origResource = result.resource;
newContentCallback(result);
if(result.resource !== origResource) {
if (result.resource !== origResource) {
result.resource = path.resolve(origResource, result.resource);
}
} else {
result.dependencies.forEach((d) => {
if(d.critical)
d.critical = false;
});
for (const d of result.dependencies) {
if (d.critical) d.critical = false;
}
}
}
return callback(null, result);
return result;
});
});
}
}
const createResolveDependenciesFromContextMap = (createContextMap) => {
return function resolveDependenciesFromContextMap(fs, resource, recursive, regExp, callback) {
const createResolveDependenciesFromContextMap = createContextMap => {
const resolveDependenciesFromContextMap = (fs, options, callback) => {
createContextMap(fs, (err, map) => {
if(err) return callback(err);
const dependencies = Object.keys(map).map((key) => {
return new ContextElementDependency(map[key], key);
if (err) return callback(err);
const dependencies = Object.keys(map).map(key => {
return new ContextElementDependency(
map[key] + options.resourceQuery,
key
);
});
callback(null, dependencies);
});
};
return resolveDependenciesFromContextMap;
};
module.exports = ContextReplacementPlugin;

View File

@@ -9,115 +9,279 @@ const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
const ParserHelpers = require("./ParserHelpers");
const NullFactory = require("./NullFactory");
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./Parser")} Parser */
/** @typedef {null|undefined|RegExp|Function|string|number} CodeValuePrimitive */
/** @typedef {CodeValuePrimitive|Record<string, CodeValuePrimitive>|RuntimeValue} CodeValue */
class RuntimeValue {
constructor(fn, fileDependencies) {
this.fn = fn;
this.fileDependencies = fileDependencies || [];
}
exec(parser) {
if (this.fileDependencies === true) {
parser.state.module.buildInfo.cacheable = false;
} else {
for (const fileDependency of this.fileDependencies) {
parser.state.module.buildInfo.fileDependencies.add(fileDependency);
}
}
return this.fn({ module: parser.state.module });
}
}
const stringifyObj = (obj, parser) => {
return (
"Object({" +
Object.keys(obj)
.map(key => {
const code = obj[key];
return JSON.stringify(key) + ":" + toCode(code, parser);
})
.join(",") +
"})"
);
};
/**
* Convert code to a string that evaluates
* @param {CodeValue} code Code to evaluate
* @param {Parser} parser Parser
* @returns {string} code converted to string that evaluates
*/
const toCode = (code, parser) => {
if (code === null) {
return "null";
}
if (code === undefined) {
return "undefined";
}
if (code instanceof RuntimeValue) {
return toCode(code.exec(parser), parser);
}
if (code instanceof RegExp && code.toString) {
return code.toString();
}
if (typeof code === "function" && code.toString) {
return "(" + code.toString() + ")";
}
if (typeof code === "object") {
return stringifyObj(code, parser);
}
return code + "";
};
class DefinePlugin {
/**
* Create a new define plugin
* @param {Record<string, CodeValue>} definitions A map of global object definitions
*/
constructor(definitions) {
this.definitions = definitions;
}
static runtimeValue(fn, fileDependencies) {
return new RuntimeValue(fn, fileDependencies);
}
/**
* Apply the plugin
* @param {Compiler} compiler Webpack compiler
* @returns {void}
*/
apply(compiler) {
const definitions = this.definitions;
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
compiler.hooks.compilation.tap(
"DefinePlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
params.normalModuleFactory.plugin("parser", (parser) => {
(function walkDefinitions(definitions, prefix) {
Object.keys(definitions).forEach((key) => {
const code = definitions[key];
if(code && typeof code === "object" && !(code instanceof RegExp)) {
walkDefinitions(code, prefix + key + ".");
applyObjectDefine(prefix + key, code);
return;
/**
* Handler
* @param {Parser} parser Parser
* @returns {void}
*/
const handler = parser => {
/**
* Walk definitions
* @param {Object} definitions Definitions map
* @param {string} prefix Prefix string
* @returns {void}
*/
const walkDefinitions = (definitions, prefix) => {
Object.keys(definitions).forEach(key => {
const code = definitions[key];
if (
code &&
typeof code === "object" &&
!(code instanceof RuntimeValue) &&
!(code instanceof RegExp)
) {
walkDefinitions(code, prefix + key + ".");
applyObjectDefine(prefix + key, code);
return;
}
applyDefineKey(prefix, key);
applyDefine(prefix + key, code);
});
};
/**
* Apply define key
* @param {string} prefix Prefix
* @param {string} key Key
* @returns {void}
*/
const applyDefineKey = (prefix, key) => {
const splittedKey = key.split(".");
splittedKey.slice(1).forEach((_, i) => {
const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
parser.hooks.canRename
.for(fullKey)
.tap("DefinePlugin", ParserHelpers.approve);
});
};
/**
* Apply Code
* @param {string} key Key
* @param {CodeValue} code Code
* @returns {void}
*/
const applyDefine = (key, code) => {
const isTypeof = /^typeof\s+/.test(key);
if (isTypeof) key = key.replace(/^typeof\s+/, "");
let recurse = false;
let recurseTypeof = false;
if (!isTypeof) {
parser.hooks.canRename
.for(key)
.tap("DefinePlugin", ParserHelpers.approve);
parser.hooks.evaluateIdentifier
.for(key)
.tap("DefinePlugin", expr => {
/**
* this is needed in case there is a recursion in the DefinePlugin
* to prevent an endless recursion
* e.g.: new DefinePlugin({
* "a": "b",
* "b": "a"
* });
*/
if (recurse) return;
recurse = true;
const res = parser.evaluate(toCode(code, parser));
recurse = false;
res.setRange(expr.range);
return res;
});
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
const strCode = toCode(code, parser);
if (/__webpack_require__/.test(strCode)) {
return ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
strCode
)(expr);
} else {
return ParserHelpers.toConstantDependency(parser, strCode)(
expr
);
}
});
}
applyDefineKey(prefix, key);
applyDefine(prefix + key, code);
});
}(definitions, ""));
function stringifyObj(obj) {
return "Object({" + Object.keys(obj).map((key) => {
const code = obj[key];
return JSON.stringify(key) + ":" + toCode(code);
}).join(",") + "})";
}
function toCode(code) {
if(code === null) return "null";
else if(code === undefined) return "undefined";
else if(code instanceof RegExp && code.toString) return code.toString();
else if(typeof code === "function" && code.toString) return "(" + code.toString() + ")";
else if(typeof code === "object") return stringifyObj(code);
else return code + "";
}
function applyDefineKey(prefix, key) {
const splittedKey = key.split(".");
splittedKey.slice(1).forEach((_, i) => {
const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
parser.plugin("can-rename " + fullKey, ParserHelpers.approve);
});
}
function applyDefine(key, code) {
const isTypeof = /^typeof\s+/.test(key);
if(isTypeof) key = key.replace(/^typeof\s+/, "");
let recurse = false;
let recurseTypeof = false;
code = toCode(code);
if(!isTypeof) {
parser.plugin("can-rename " + key, ParserHelpers.approve);
parser.plugin("evaluate Identifier " + key, (expr) => {
parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", expr => {
/**
* this is needed in case there is a recursion in the DefinePlugin
* to prevent an endless recursion
* e.g.: new DefinePlugin({
* "a": "b",
* "b": "a"
* "typeof a": "typeof b",
* "typeof b": "typeof a"
* });
*/
if(recurse) return;
recurse = true;
const res = parser.evaluate(code);
recurse = false;
if (recurseTypeof) return;
recurseTypeof = true;
const typeofCode = isTypeof
? toCode(code, parser)
: "typeof (" + toCode(code, parser) + ")";
const res = parser.evaluate(typeofCode);
recurseTypeof = false;
res.setRange(expr.range);
return res;
});
parser.plugin("expression " + key, ParserHelpers.toConstantDependency(code));
}
const typeofCode = isTypeof ? code : "typeof (" + code + ")";
parser.plugin("evaluate typeof " + key, (expr) => {
/**
* this is needed in case there is a recursion in the DefinePlugin
* to prevent an endless recursion
* e.g.: new DefinePlugin({
* "typeof a": "tyepof b",
* "typeof b": "typeof a"
* });
*/
if(recurseTypeof) return;
recurseTypeof = true;
const res = parser.evaluate(typeofCode);
recurseTypeof = false;
res.setRange(expr.range);
return res;
});
parser.plugin("typeof " + key, (expr) => {
const res = parser.evaluate(typeofCode);
if(!res.isString()) return;
return ParserHelpers.toConstantDependency(JSON.stringify(res.string)).bind(parser)(expr);
});
}
parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
const typeofCode = isTypeof
? toCode(code, parser)
: "typeof (" + toCode(code, parser) + ")";
const res = parser.evaluate(typeofCode);
if (!res.isString()) return;
return ParserHelpers.toConstantDependency(
parser,
JSON.stringify(res.string)
).bind(parser)(expr);
});
};
function applyObjectDefine(key, obj) {
const code = stringifyObj(obj);
parser.plugin("can-rename " + key, ParserHelpers.approve);
parser.plugin("evaluate Identifier " + key, (expr) => new BasicEvaluatedExpression().setTruthy().setRange(expr.range));
parser.plugin("evaluate typeof " + key, ParserHelpers.evaluateToString("object"));
parser.plugin("expression " + key, ParserHelpers.toConstantDependency(code));
parser.plugin("typeof " + key, ParserHelpers.toConstantDependency(JSON.stringify("object")));
}
});
});
/**
* Apply Object
* @param {string} key Key
* @param {Object} obj Object
* @returns {void}
*/
const applyObjectDefine = (key, obj) => {
parser.hooks.canRename
.for(key)
.tap("DefinePlugin", ParserHelpers.approve);
parser.hooks.evaluateIdentifier
.for(key)
.tap("DefinePlugin", expr =>
new BasicEvaluatedExpression().setTruthy().setRange(expr.range)
);
parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", expr => {
return ParserHelpers.evaluateToString("object")(expr);
});
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
const strCode = stringifyObj(obj, parser);
if (/__webpack_require__/.test(strCode)) {
return ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
strCode
)(expr);
} else {
return ParserHelpers.toConstantDependency(parser, strCode)(
expr
);
}
});
parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
return ParserHelpers.toConstantDependency(
parser,
JSON.stringify("object")
)(expr);
});
};
walkDefinitions(definitions, "");
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("DefinePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("DefinePlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("DefinePlugin", handler);
}
);
}
}
module.exports = DefinePlugin;

View File

@@ -4,33 +4,42 @@
*/
"use strict";
const { OriginalSource, RawSource } = require("webpack-sources");
const Module = require("./Module");
const OriginalSource = require("webpack-sources").OriginalSource;
const RawSource = require("webpack-sources").RawSource;
const WebpackMissingModule = require("./dependencies/WebpackMissingModule");
const DelegatedSourceDependency = require("./dependencies/DelegatedSourceDependency");
const DelegatedExportsDependency = require("./dependencies/DelegatedExportsDependency");
/** @typedef {import("./dependencies/ModuleDependency")} ModuleDependency */
/** @typedef {import("./util/createHash").Hash} Hash */
class DelegatedModule extends Module {
constructor(sourceRequest, data, type, userRequest, originalRequest) {
super();
super("javascript/dynamic", null);
// Info from Factory
this.sourceRequest = sourceRequest;
this.request = data.id;
this.meta = data.meta;
this.type = type;
this.originalRequest = originalRequest;
this.userRequest = userRequest;
this.built = false;
this.delegated = true;
this.originalRequest = originalRequest;
this.delegateData = data;
// Build info
this.delegatedSourceDependency = undefined;
}
libIdent(options) {
return typeof this.originalRequest === "string" ? this.originalRequest : this.originalRequest.libIdent(options);
return typeof this.originalRequest === "string"
? this.originalRequest
: this.originalRequest.libIdent(options);
}
identifier() {
return `delegated ${JSON.stringify(this.request)} from ${this.sourceRequest}`;
return `delegated ${JSON.stringify(this.request)} from ${
this.sourceRequest
}`;
}
readableIdentifier() {
@@ -43,29 +52,32 @@ class DelegatedModule extends Module {
build(options, compilation, resolver, fs, callback) {
this.built = true;
this.builtTime = Date.now();
this.cacheable = true;
this.dependencies.length = 0;
this.addDependency(new DelegatedSourceDependency(this.sourceRequest));
this.addDependency(new DelegatedExportsDependency(this, this.delegateData.exports || true));
this.buildMeta = Object.assign({}, this.delegateData.buildMeta);
this.buildInfo = {};
this.delegatedSourceDependency = new DelegatedSourceDependency(
this.sourceRequest
);
this.addDependency(this.delegatedSourceDependency);
this.addDependency(
new DelegatedExportsDependency(this, this.delegateData.exports || true)
);
callback();
}
unbuild() {
this.built = false;
super.unbuild();
}
source() {
const sourceModule = this.dependencies[0].module;
source(depTemplates, runtime) {
const dep = /** @type {DelegatedSourceDependency} */ (this.dependencies[0]);
const sourceModule = dep.module;
let str;
if(!sourceModule) {
if (!sourceModule) {
str = WebpackMissingModule.moduleCode(this.sourceRequest);
} else {
str = `module.exports = (__webpack_require__(${JSON.stringify(sourceModule.id)}))`;
str = `module.exports = (${runtime.moduleExports({
module: sourceModule,
request: dep.request
})})`;
switch(this.type) {
switch (this.type) {
case "require":
str += `(${JSON.stringify(this.request)})`;
break;
@@ -77,7 +89,7 @@ class DelegatedModule extends Module {
str += ";";
}
if(this.useSourceMap) {
if (this.useSourceMap) {
return new OriginalSource(str, this.identifier());
} else {
return new RawSource(str);
@@ -88,6 +100,10 @@ class DelegatedModule extends Module {
return 42;
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
hash.update(this.type);
hash.update(JSON.stringify(this.request));

View File

@@ -15,44 +15,80 @@ class DelegatedModuleFactoryPlugin {
constructor(options) {
this.options = options;
options.type = options.type || "require";
options.extensions = options.extensions || ["", ".js"];
options.extensions = options.extensions || [
"",
".wasm",
".mjs",
".js",
".json"
];
}
apply(normalModuleFactory) {
const scope = this.options.scope;
if(scope) {
normalModuleFactory.plugin("factory", factory => (data, callback) => {
const dependency = data.dependencies[0];
const request = dependency.request;
if(request && request.indexOf(scope + "/") === 0) {
const innerRequest = "." + request.substr(scope.length);
let resolved;
if(innerRequest in this.options.content) {
resolved = this.options.content[innerRequest];
return callback(null, new DelegatedModule(this.options.source, resolved, this.options.type, innerRequest, request));
}
for(let i = 0; i < this.options.extensions.length; i++) {
const extension = this.options.extensions[i];
const requestPlusExt = innerRequest + extension;
if(requestPlusExt in this.options.content) {
resolved = this.options.content[requestPlusExt];
return callback(null, new DelegatedModule(this.options.source, resolved, this.options.type, requestPlusExt, request + extension));
if (scope) {
normalModuleFactory.hooks.factory.tap(
"DelegatedModuleFactoryPlugin",
factory => (data, callback) => {
const dependency = data.dependencies[0];
const request = dependency.request;
if (request && request.indexOf(scope + "/") === 0) {
const innerRequest = "." + request.substr(scope.length);
let resolved;
if (innerRequest in this.options.content) {
resolved = this.options.content[innerRequest];
return callback(
null,
new DelegatedModule(
this.options.source,
resolved,
this.options.type,
innerRequest,
request
)
);
}
for (let i = 0; i < this.options.extensions.length; i++) {
const extension = this.options.extensions[i];
const requestPlusExt = innerRequest + extension;
if (requestPlusExt in this.options.content) {
resolved = this.options.content[requestPlusExt];
return callback(
null,
new DelegatedModule(
this.options.source,
resolved,
this.options.type,
requestPlusExt,
request + extension
)
);
}
}
}
return factory(data, callback);
}
return factory(data, callback);
});
);
} else {
normalModuleFactory.plugin("module", module => {
if(module.libIdent) {
const request = module.libIdent(this.options);
if(request && request in this.options.content) {
const resolved = this.options.content[request];
return new DelegatedModule(this.options.source, resolved, this.options.type, request, module);
normalModuleFactory.hooks.module.tap(
"DelegatedModuleFactoryPlugin",
module => {
if (module.libIdent) {
const request = module.libIdent(this.options);
if (request && request in this.options.content) {
const resolved = this.options.content[request];
return new DelegatedModule(
this.options.source,
resolved,
this.options.type,
request,
module
);
}
}
return module;
}
return module;
});
);
}
}
}

View File

@@ -16,13 +16,22 @@ class DelegatedPlugin {
}
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(DelegatedSourceDependency, params.normalModuleFactory);
compilation.dependencyFactories.set(DelegatedExportsDependency, new NullFactory());
});
compiler.hooks.compilation.tap(
"DelegatedPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
DelegatedSourceDependency,
normalModuleFactory
);
compilation.dependencyFactories.set(
DelegatedExportsDependency,
new NullFactory()
);
}
);
compiler.plugin("compile", (params) => {
params.normalModuleFactory.apply(new DelegatedModuleFactoryPlugin(this.options));
compiler.hooks.compile.tap("DelegatedPlugin", ({ normalModuleFactory }) => {
new DelegatedModuleFactoryPlugin(this.options).apply(normalModuleFactory);
});
}
}

View File

@@ -6,75 +6,118 @@
const DependenciesBlockVariable = require("./DependenciesBlockVariable");
function disconnect(i) {
i.disconnect();
}
function unseal(i) {
i.unseal();
}
/** @typedef {import("./ChunkGroup")} ChunkGroup */
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("./DependenciesBlockVariable")} DependenciesBlockVariable */
/** @typedef {(d: Dependency) => boolean} DependencyFilterFunction */
/** @typedef {import("./util/createHash").Hash} Hash */
class DependenciesBlock {
constructor() {
/** @type {Dependency[]} */
this.dependencies = [];
/** @type {AsyncDependenciesBlock[]} */
this.blocks = [];
/** @type {DependenciesBlockVariable[]} */
this.variables = [];
}
/**
* Adds a DependencyBlock to DependencyBlock relationship.
* This is used for when a Module has a AsyncDependencyBlock tie (for code-splitting)
*
* @param {AsyncDependenciesBlock} block block being added
* @returns {void}
*/
addBlock(block) {
this.blocks.push(block);
block.parent = this;
}
/**
* @param {string} name name of dependency
* @param {string} expression expression string for variable
* @param {Dependency[]} dependencies dependency instances tied to variable
* @returns {void}
*/
addVariable(name, expression, dependencies) {
for(let v of this.variables) {
if(v.name === name && v.expression === expression) {
for (let v of this.variables) {
if (v.name === name && v.expression === expression) {
return;
}
}
this.variables.push(new DependenciesBlockVariable(name, expression, dependencies));
this.variables.push(
new DependenciesBlockVariable(name, expression, dependencies)
);
}
/**
* @param {Dependency} dependency dependency being tied to block.
* This is an "edge" pointing to another "node" on module graph.
* @returns {void}
*/
addDependency(dependency) {
this.dependencies.push(dependency);
}
updateHash(hash) {
function updateHash(i) {
i.updateHash(hash);
/**
* @param {Dependency} dependency dependency being removed
* @returns {void}
*/
removeDependency(dependency) {
const idx = this.dependencies.indexOf(dependency);
if (idx >= 0) {
this.dependencies.splice(idx, 1);
}
}
this.dependencies.forEach(updateHash);
this.blocks.forEach(updateHash);
this.variables.forEach(updateHash);
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
for (const dep of this.dependencies) dep.updateHash(hash);
for (const block of this.blocks) block.updateHash(hash);
for (const variable of this.variables) variable.updateHash(hash);
}
disconnect() {
this.dependencies.forEach(disconnect);
this.blocks.forEach(disconnect);
this.variables.forEach(disconnect);
for (const dep of this.dependencies) dep.disconnect();
for (const block of this.blocks) block.disconnect();
for (const variable of this.variables) variable.disconnect();
}
unseal() {
this.blocks.forEach(unseal);
for (const block of this.blocks) block.unseal();
}
/**
* @param {DependencyFilterFunction} filter filter function for dependencies, gets passed all dependency ties from current instance
* @returns {boolean} returns boolean for filter
*/
hasDependencies(filter) {
if(filter) {
if(this.dependencies.some(filter)) {
return true;
if (filter) {
for (const dep of this.dependencies) {
if (filter(dep)) return true;
}
} else {
if(this.dependencies.length > 0) {
if (this.dependencies.length > 0) {
return true;
}
}
return this.blocks.concat(this.variables).some(item => item.hasDependencies(filter));
for (const block of this.blocks) {
if (block.hasDependencies(filter)) return true;
}
for (const variable of this.variables) {
if (variable.hasDependencies(filter)) return true;
}
return false;
}
sortItems() {
this.blocks.forEach(block => block.sortItems());
for (const block of this.blocks) block.sortItems();
}
}

View File

@@ -4,47 +4,68 @@
*/
"use strict";
const ReplaceSource = require("webpack-sources").ReplaceSource;
const RawSource = require("webpack-sources").RawSource;
const { RawSource, ReplaceSource } = require("webpack-sources");
/** @typedef {import("./Dependency")} Dependency */
/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("./util/createHash").Hash} Hash */
/** @typedef {(d: Dependency) => boolean} DependencyFilterFunction */
/** @typedef {Map<Function, DependencyTemplate>} DependencyTemplates */
class DependenciesBlockVariable {
/**
* Creates an instance of DependenciesBlockVariable.
* @param {string} name name of DependenciesBlockVariable
* @param {string} expression expression string
* @param {Dependency[]=} dependencies dependencies tied to this varaiable
*/
constructor(name, expression, dependencies) {
this.name = name;
this.expression = expression;
this.dependencies = dependencies || [];
}
/**
* @param {Hash} hash hash for instance to update
* @returns {void}
*/
updateHash(hash) {
hash.update(this.name);
hash.update(this.expression);
this.dependencies.forEach(d => {
for (const d of this.dependencies) {
d.updateHash(hash);
});
}
}
expressionSource(dependencyTemplates, outputOptions, requestShortener) {
/**
* @param {DependencyTemplates} dependencyTemplates Dependency constructors and templates Map.
* @param {RuntimeTemplate} runtimeTemplate runtimeTemplate to generate expression souce
* @returns {ReplaceSource} returns constructed source for expression via templates
*/
expressionSource(dependencyTemplates, runtimeTemplate) {
const source = new ReplaceSource(new RawSource(this.expression));
this.dependencies.forEach(dep => {
for (const dep of this.dependencies) {
const template = dependencyTemplates.get(dep.constructor);
if(!template) throw new Error(`No template for dependency: ${dep.constructor.name}`);
template.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
});
if (!template) {
throw new Error(`No template for dependency: ${dep.constructor.name}`);
}
template.apply(dep, source, runtimeTemplate, dependencyTemplates);
}
return source;
}
disconnect() {
this.dependencies.forEach(d => {
for (const d of this.dependencies) {
d.disconnect();
});
}
}
hasDependencies(filter) {
if(filter) {
if(this.dependencies.some(filter)) return true;
} else {
if(this.dependencies.length > 0) return true;
if (filter) {
return this.dependencies.some(filter);
}
return false;
return this.dependencies.length > 0;
}
}

View File

@@ -3,24 +3,59 @@
Author Tobias Koppers @sokra
*/
"use strict";
const util = require("util");
const compareLocations = require("./compareLocations");
const DependencyReference = require("./dependencies/DependencyReference");
/** @typedef {import("./Module")} Module */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
/**
* @typedef {Object} DependencyTemplate
* @property {function(Dependency, Source, RuntimeTemplate, Map<Function, DependencyTemplate>): void} apply
*/
/** @typedef {Object} SourcePosition
* @property {number} line
* @property {number=} column
*/
/** @typedef {Object} RealDependencyLocation
* @property {SourcePosition} start
* @property {SourcePosition=} end
* @property {number=} index
*/
/** @typedef {Object} SynteticDependencyLocation
* @property {string} name
* @property {number=} index
*/
/** @typedef {SynteticDependencyLocation|RealDependencyLocation} DependencyLocation */
class Dependency {
constructor() {
/** @type {Module|null} */
this.module = null;
// TODO remove in webpack 5
/** @type {boolean} */
this.weak = false;
/** @type {boolean} */
this.optional = false;
/** @type {DependencyLocation} */
this.loc = undefined;
}
isEqualResource() {
return false;
getResourceIdentifier() {
return null;
}
// Returns the referenced module and export
getReference() {
if(!this.module) return null;
return {
module: this.module,
importedNames: true, // true: full object, false: only sideeffects/no export, array of strings: the exports with this names
};
if (!this.module) return null;
return new DependencyReference(this.module, true, this.weak);
}
// Returns the exported names
@@ -43,12 +78,12 @@ class Dependency {
disconnect() {
this.module = null;
}
// TODO: remove in webpack 3
compare(a, b) {
return compareLocations(a.loc, b.loc);
}
}
Dependency.compare = (a, b) => compareLocations(a.loc, b.loc);
// TODO remove in webpack 5
Dependency.compare = util.deprecate(
(a, b) => compareLocations(a.loc, b.loc),
"Dependency.compare is deprecated and will be removed in the next major version"
);
module.exports = Dependency;

View File

@@ -16,20 +16,37 @@ class DllEntryPlugin {
}
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
const dllModuleFactory = new DllModuleFactory();
const normalModuleFactory = params.normalModuleFactory;
compilation.dependencyFactories.set(DllEntryDependency, dllModuleFactory);
compilation.dependencyFactories.set(SingleEntryDependency, normalModuleFactory);
});
compiler.plugin("make", (compilation, callback) => {
compilation.addEntry(this.context, new DllEntryDependency(this.entries.map((e, idx) => {
const dep = new SingleEntryDependency(e);
dep.loc = `${this.name}:${idx}`;
return dep;
}), this.name), this.name, callback);
compiler.hooks.compilation.tap(
"DllEntryPlugin",
(compilation, { normalModuleFactory }) => {
const dllModuleFactory = new DllModuleFactory();
compilation.dependencyFactories.set(
DllEntryDependency,
dllModuleFactory
);
compilation.dependencyFactories.set(
SingleEntryDependency,
normalModuleFactory
);
}
);
compiler.hooks.make.tapAsync("DllEntryPlugin", (compilation, callback) => {
compilation.addEntry(
this.context,
new DllEntryDependency(
this.entries.map((e, idx) => {
const dep = new SingleEntryDependency(e);
dep.loc = {
name: this.name,
index: idx
};
return dep;
}),
this.name
),
this.name,
callback
);
});
}
}

View File

@@ -4,17 +4,18 @@
*/
"use strict";
const { RawSource } = require("webpack-sources");
const Module = require("./Module");
const RawSource = require("webpack-sources").RawSource;
/** @typedef {import("./util/createHash").Hash} Hash */
class DllModule extends Module {
constructor(context, dependencies, name, type) {
super();
this.context = context;
super("javascript/dynamic", context);
// Info from Factory
this.dependencies = dependencies;
this.name = name;
this.built = false;
this.cacheable = true;
this.type = type;
}
@@ -26,13 +27,10 @@ class DllModule extends Module {
return `dll ${this.name}`;
}
disconnect() {
this.built = false;
super.disconnect();
}
build(options, compilation, resolver, fs, callback) {
this.built = true;
this.buildMeta = {};
this.buildInfo = {};
return callback();
}
@@ -48,6 +46,10 @@ class DllModule extends Module {
return 12;
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
hash.update("dll module");
hash.update(this.name || "");

View File

@@ -4,16 +4,25 @@
*/
"use strict";
const Tapable = require("tapable");
const { Tapable } = require("tapable");
const DllModule = require("./DllModule");
class DllModuleFactory extends Tapable {
constructor() {
super();
this.hooks = {};
}
create(data, callback) {
const dependency = data.dependencies[0];
callback(null, new DllModule(data.context, dependency.dependencies, dependency.name, dependency.type));
callback(
null,
new DllModule(
data.context,
dependency.dependencies,
dependency.name,
dependency.type
)
);
}
}

View File

@@ -8,30 +8,41 @@ const DllEntryPlugin = require("./DllEntryPlugin");
const LibManifestPlugin = require("./LibManifestPlugin");
const FlagInitialModulesAsUsedPlugin = require("./FlagInitialModulesAsUsedPlugin");
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/DllPlugin.json");
/** @typedef {import("../declarations/plugins/DllPlugin").DllPluginOptions} DllPluginOptions */
class DllPlugin {
/**
* @param {DllPluginOptions} options options object
*/
constructor(options) {
validateOptions(schema, options, "Dll Plugin");
this.options = options;
}
apply(compiler) {
compiler.plugin("entry-option", (context, entry) => {
function itemToPlugin(item, name) {
if(Array.isArray(item))
compiler.hooks.entryOption.tap("DllPlugin", (context, entry) => {
const itemToPlugin = (item, name) => {
if (Array.isArray(item)) {
return new DllEntryPlugin(context, item, name);
else
throw new Error("DllPlugin: supply an Array as entry");
}
if(typeof entry === "object" && !Array.isArray(entry)) {
}
throw new Error("DllPlugin: supply an Array as entry");
};
if (typeof entry === "object" && !Array.isArray(entry)) {
Object.keys(entry).forEach(name => {
compiler.apply(itemToPlugin(entry[name], name));
itemToPlugin(entry[name], name).apply(compiler);
});
} else {
compiler.apply(itemToPlugin(entry, "main"));
itemToPlugin(entry, "main").apply(compiler);
}
return true;
});
compiler.apply(new LibManifestPlugin(this.options));
compiler.apply(new FlagInitialModulesAsUsedPlugin());
new LibManifestPlugin(this.options).apply(compiler);
if (!this.options.entryOnly) {
new FlagInitialModulesAsUsedPlugin("DllPlugin").apply(compiler);
}
}
}

View File

@@ -4,58 +4,152 @@
*/
"use strict";
const parseJson = require("json-parse-better-errors");
const DelegatedSourceDependency = require("./dependencies/DelegatedSourceDependency");
const DelegatedModuleFactoryPlugin = require("./DelegatedModuleFactoryPlugin");
const ExternalModuleFactoryPlugin = require("./ExternalModuleFactoryPlugin");
const DelegatedExportsDependency = require("./dependencies/DelegatedExportsDependency");
const NullFactory = require("./NullFactory");
const makePathsRelative = require("./util/identifier").makePathsRelative;
const WebpackError = require("./WebpackError");
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/DllReferencePlugin.json");
/** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptions} DllReferencePluginOptions */
/** @typedef {import("../declarations/plugins/DllReferencePlugin").DllReferencePluginOptionsManifest} DllReferencePluginOptionsManifest */
class DllReferencePlugin {
/**
* @param {DllReferencePluginOptions} options options object
*/
constructor(options) {
validateOptions(schema, options, "Dll Reference Plugin");
this.options = options;
}
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
const normalModuleFactory = params.normalModuleFactory;
compilation.dependencyFactories.set(DelegatedSourceDependency, normalModuleFactory);
compilation.dependencyFactories.set(DelegatedExportsDependency, new NullFactory());
});
compiler.hooks.compilation.tap(
"DllReferencePlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
DelegatedSourceDependency,
normalModuleFactory
);
compilation.dependencyFactories.set(
DelegatedExportsDependency,
new NullFactory()
);
}
);
compiler.plugin("before-compile", (params, callback) => {
const manifest = this.options.manifest;
if(typeof manifest === "string") {
params.compilationDependencies.push(manifest);
compiler.inputFileSystem.readFile(manifest, function(err, result) {
if(err) return callback(err);
params["dll reference " + manifest] = JSON.parse(result.toString("utf-8"));
return callback();
});
} else {
compiler.hooks.beforeCompile.tapAsync(
"DllReferencePlugin",
(params, callback) => {
if ("manifest" in this.options) {
const manifest = this.options.manifest;
if (typeof manifest === "string") {
params.compilationDependencies.add(manifest);
compiler.inputFileSystem.readFile(manifest, (err, result) => {
if (err) return callback(err);
// Catch errors parsing the manifest so that blank
// or malformed manifest files don't kill the process.
try {
params["dll reference " + manifest] = parseJson(
result.toString("utf-8")
);
} catch (e) {
// Store the error in the params so that it can
// be added as a compilation error later on.
const manifestPath = makePathsRelative(
compiler.options.context,
manifest
);
params[
"dll reference parse error " + manifest
] = new DllManifestError(manifestPath, e.message);
}
return callback();
});
return;
}
}
return callback();
}
});
);
compiler.plugin("compile", (params) => {
let manifest = this.options.manifest;
if(typeof manifest === "string") {
manifest = params["dll reference " + manifest];
compiler.hooks.compile.tap("DllReferencePlugin", params => {
let name = this.options.name;
let sourceType = this.options.sourceType;
let content =
"content" in this.options ? this.options.content : undefined;
if ("manifest" in this.options) {
let manifestParameter = this.options.manifest;
let manifest;
if (typeof manifestParameter === "string") {
// If there was an error parsing the manifest
// file, exit now because the error will be added
// as a compilation error in the "compilation" hook.
if (params["dll reference parse error " + manifestParameter]) {
return;
}
manifest =
/** @type {DllReferencePluginOptionsManifest} */ (params[
"dll reference " + manifestParameter
]);
} else {
manifest = manifestParameter;
}
if (manifest) {
if (!name) name = manifest.name;
if (!sourceType) sourceType = manifest.type;
if (!content) content = manifest.content;
}
}
const name = this.options.name || manifest.name;
const sourceType = this.options.sourceType || (manifest && manifest.type) || "var";
const externals = {};
const source = "dll-reference " + name;
externals[source] = name;
params.normalModuleFactory.apply(new ExternalModuleFactoryPlugin(sourceType, externals));
params.normalModuleFactory.apply(new DelegatedModuleFactoryPlugin({
const normalModuleFactory = params.normalModuleFactory;
new ExternalModuleFactoryPlugin(sourceType || "var", externals).apply(
normalModuleFactory
);
new DelegatedModuleFactoryPlugin({
source: source,
type: this.options.type,
scope: this.options.scope,
context: this.options.context || compiler.options.context,
content: this.options.content || manifest.content,
content,
extensions: this.options.extensions
}));
}).apply(normalModuleFactory);
});
compiler.hooks.compilation.tap(
"DllReferencePlugin",
(compilation, params) => {
if ("manifest" in this.options) {
let manifest = this.options.manifest;
if (typeof manifest === "string") {
// If there was an error parsing the manifest file, add the
// error as a compilation error to make the compilation fail.
let e = params["dll reference parse error " + manifest];
if (e) {
compilation.errors.push(e);
}
}
}
}
);
}
}
class DllManifestError extends WebpackError {
constructor(filename, message) {
super();
this.name = "DllManifestError";
this.message = `Dll manifest ${filename}\n${message}`;
Error.captureStackTrace(this, this.constructor);
}
}

View File

@@ -10,50 +10,85 @@ const MultiModuleFactory = require("./MultiModuleFactory");
const MultiEntryPlugin = require("./MultiEntryPlugin");
const SingleEntryPlugin = require("./SingleEntryPlugin");
/** @typedef {import("../declarations/WebpackOptions").EntryDynamic} EntryDynamic */
/** @typedef {import("../declarations/WebpackOptions").EntryStatic} EntryStatic */
/** @typedef {import("./Compiler")} Compiler */
class DynamicEntryPlugin {
/**
* @param {string} context the context path
* @param {EntryDynamic} entry the entry value
*/
constructor(context, entry) {
this.context = context;
this.entry = entry;
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
const multiModuleFactory = new MultiModuleFactory();
const normalModuleFactory = params.normalModuleFactory;
compiler.hooks.compilation.tap(
"DynamicEntryPlugin",
(compilation, { normalModuleFactory }) => {
const multiModuleFactory = new MultiModuleFactory();
compilation.dependencyFactories.set(MultiEntryDependency, multiModuleFactory);
compilation.dependencyFactories.set(SingleEntryDependency, normalModuleFactory);
});
compilation.dependencyFactories.set(
MultiEntryDependency,
multiModuleFactory
);
compilation.dependencyFactories.set(
SingleEntryDependency,
normalModuleFactory
);
}
);
compiler.plugin("make", (compilation, callback) => {
const addEntry = (entry, name) => {
const dep = DynamicEntryPlugin.createDependency(entry, name);
return new Promise((resolve, reject) => {
compilation.addEntry(this.context, dep, name, (err) => {
if(err) return reject(err);
resolve();
compiler.hooks.make.tapAsync(
"DynamicEntryPlugin",
(compilation, callback) => {
/**
* @param {string|string[]} entry entry value or array of entry values
* @param {string} name name of entry
* @returns {Promise<EntryStatic>} returns the promise resolving the Compilation#addEntry function
*/
const addEntry = (entry, name) => {
const dep = DynamicEntryPlugin.createDependency(entry, name);
return new Promise((resolve, reject) => {
compilation.addEntry(this.context, dep, name, err => {
if (err) return reject(err);
resolve();
});
});
});
};
};
Promise.resolve(this.entry()).then((entry) => {
if(typeof entry === "string" || Array.isArray(entry)) {
addEntry(entry, "main").then(() => callback(), callback);
} else if(typeof entry === "object") {
Promise.all(Object.keys(entry).map((name) => {
return addEntry(entry[name], name);
})).then(() => callback(), callback);
}
});
});
Promise.resolve(this.entry()).then(entry => {
if (typeof entry === "string" || Array.isArray(entry)) {
addEntry(entry, "main").then(() => callback(), callback);
} else if (typeof entry === "object") {
Promise.all(
Object.keys(entry).map(name => {
return addEntry(entry[name], name);
})
).then(() => callback(), callback);
}
});
}
);
}
}
module.exports = DynamicEntryPlugin;
DynamicEntryPlugin.createDependency = function(entry, name) {
if(Array.isArray(entry))
/**
* @param {string|string[]} entry entry value or array of entry paths
* @param {string} name name of entry
* @returns {SingleEntryDependency|MultiEntryDependency} returns dep
*/
DynamicEntryPlugin.createDependency = (entry, name) => {
if (Array.isArray(entry)) {
return MultiEntryPlugin.createDependency(entry, name);
else
} else {
return SingleEntryPlugin.createDependency(entry, name);
}
};

View File

@@ -8,10 +8,9 @@ const WebpackError = require("./WebpackError");
class EntryModuleNotFoundError extends WebpackError {
constructor(err) {
super();
super("Entry module not found: " + err);
this.name = "EntryModuleNotFoundError";
this.message = "Entry module not found: " + err;
this.details = err.details;
this.error = err;

View File

@@ -8,22 +8,37 @@ const SingleEntryPlugin = require("./SingleEntryPlugin");
const MultiEntryPlugin = require("./MultiEntryPlugin");
const DynamicEntryPlugin = require("./DynamicEntryPlugin");
function itemToPlugin(context, item, name) {
if(Array.isArray(item)) {
/** @typedef {import("../declarations/WebpackOptions").EntryItem} EntryItem */
/** @typedef {import("./Compiler")} Compiler */
/**
* @param {string} context context path
* @param {EntryItem} item entry array or single path
* @param {string} name entry key name
* @returns {SingleEntryPlugin | MultiEntryPlugin} returns either a single or multi entry plugin
*/
const itemToPlugin = (context, item, name) => {
if (Array.isArray(item)) {
return new MultiEntryPlugin(context, item, name);
}
return new SingleEntryPlugin(context, item, name);
}
};
module.exports = class EntryOptionPlugin {
/**
* @param {Compiler} compiler the compiler instance one is tapping into
* @returns {void}
*/
apply(compiler) {
compiler.plugin("entry-option", (context, entry) => {
if(typeof entry === "string" || Array.isArray(entry)) {
compiler.apply(itemToPlugin(context, entry, "main"));
} else if(typeof entry === "object") {
Object.keys(entry).forEach(name => compiler.apply(itemToPlugin(context, entry[name], name)));
} else if(typeof entry === "function") {
compiler.apply(new DynamicEntryPlugin(context, entry));
compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
if (typeof entry === "string" || Array.isArray(entry)) {
itemToPlugin(context, entry, "main").apply(compiler);
} else if (typeof entry === "object") {
for (const name of Object.keys(entry)) {
itemToPlugin(context, entry[name], name).apply(compiler);
}
} else if (typeof entry === "function") {
new DynamicEntryPlugin(context, entry).apply(compiler);
}
return true;
});

View File

@@ -4,39 +4,60 @@
*/
"use strict";
class Entrypoint {
const ChunkGroup = require("./ChunkGroup");
/** @typedef {import("./Chunk")} Chunk */
/**
* Entrypoint serves as an encapsulation primitive for chunks that are
* a part of a single ChunkGroup. They represent all bundles that need to be loaded for a
* single instance of a page. Multi-page application architectures will typically yield multiple Entrypoint objects
* inside of the compilation, whereas a Single Page App may only contain one with many lazy-loaded chunks.
*/
class Entrypoint extends ChunkGroup {
/**
* Creates an instance of Entrypoint.
* @param {string} name the name of the entrypoint
*/
constructor(name) {
this.name = name;
this.chunks = [];
super(name);
/** @type {Chunk=} */
this.runtimeChunk = undefined;
}
unshiftChunk(chunk) {
this.chunks.unshift(chunk);
chunk.entrypoints.push(this);
/**
* isInitial will always return true for Entrypoint ChunkGroup.
* @returns {true} returns true as all entrypoints are initial ChunkGroups
*/
isInitial() {
return true;
}
insertChunk(chunk, before) {
const idx = this.chunks.indexOf(before);
if(idx >= 0) {
this.chunks.splice(idx, 0, chunk);
} else {
throw new Error("before chunk not found");
}
chunk.entrypoints.push(this);
/**
* Sets the runtimeChunk for an entrypoint.
* @param {Chunk} chunk the chunk being set as the runtime chunk.
* @returns {void}
*/
setRuntimeChunk(chunk) {
this.runtimeChunk = chunk;
}
getFiles() {
const files = [];
/**
* Fetches the chunk reference containing the webpack bootstrap code
* @returns {Chunk} returns the runtime chunk or first chunk in `this.chunks`
*/
getRuntimeChunk() {
return this.runtimeChunk || this.chunks[0];
}
for(let chunkIdx = 0; chunkIdx < this.chunks.length; chunkIdx++) {
for(let fileIdx = 0; fileIdx < this.chunks[chunkIdx].files.length; fileIdx++) {
if(files.indexOf(this.chunks[chunkIdx].files[fileIdx]) === -1) {
files.push(this.chunks[chunkIdx].files[fileIdx]);
}
}
}
return files;
/**
* @param {Chunk} oldChunk chunk to be replaced
* @param {Chunk} newChunk New chunkt that will be replaced
* @returns {boolean} rerturns true for
*/
replaceChunk(oldChunk, newChunk) {
if (this.runtimeChunk === oldChunk) this.runtimeChunk = newChunk;
return super.replaceChunk(oldChunk, newChunk);
}
}

View File

@@ -5,25 +5,33 @@
"use strict";
/** @typedef {import("./Compiler")} Compiler */
const WebpackError = require("./WebpackError");
const DefinePlugin = require("./DefinePlugin");
const needsEnvVarFix = ["8", "9"].indexOf(process.versions.node.split(".")[0]) >= 0 &&
const needsEnvVarFix =
["8", "9"].indexOf(process.versions.node.split(".")[0]) >= 0 &&
process.platform === "win32";
class EnvironmentPlugin {
constructor(keys) {
if(Array.isArray(keys)) {
this.keys = keys;
constructor(...keys) {
if (keys.length === 1 && Array.isArray(keys[0])) {
this.keys = keys[0];
this.defaultValues = {};
} else if(keys && typeof keys === "object") {
this.keys = Object.keys(keys);
this.defaultValues = keys;
} else if (keys.length === 1 && keys[0] && typeof keys[0] === "object") {
this.keys = Object.keys(keys[0]);
this.defaultValues = keys[0];
} else {
this.keys = Array.prototype.slice.call(arguments);
this.keys = keys;
this.defaultValues = {};
}
}
/**
* @param {Compiler} compiler webpack compiler instance
* @returns {void}
*/
apply(compiler) {
const definitions = this.keys.reduce((defs, key) => {
// TODO remove once the fix has made its way into Node 8.
@@ -31,16 +39,19 @@ class EnvironmentPlugin {
// affecting Node 8 & 9 by performing an OS-level
// operation that always succeeds before reading
// environment variables:
if(needsEnvVarFix) require("os").cpus();
if (needsEnvVarFix) require("os").cpus();
const value = process.env[key] !== undefined ? process.env[key] : this.defaultValues[key];
const value =
process.env[key] !== undefined
? process.env[key]
: this.defaultValues[key];
if(value === undefined) {
compiler.plugin("this-compilation", compilation => {
const error = new Error(
if (value === undefined) {
compiler.hooks.thisCompilation.tap("EnvironmentPlugin", compilation => {
const error = new WebpackError(
`EnvironmentPlugin - ${key} environment variable is undefined.\n\n` +
"You can pass an object with default values to suppress this warning.\n" +
"See https://webpack.js.org/plugins/environment-plugin for example."
"You can pass an object with default values to suppress this warning.\n" +
"See https://webpack.js.org/plugins/environment-plugin for example."
);
error.name = "EnvVariableNotDefinedError";
@@ -48,12 +59,13 @@ class EnvironmentPlugin {
});
}
defs[`process.env.${key}`] = typeof value === "undefined" ? "undefined" : JSON.stringify(value);
defs[`process.env.${key}`] =
value === undefined ? "undefined" : JSON.stringify(value);
return defs;
}, {});
compiler.apply(new DefinePlugin(definitions));
new DefinePlugin(definitions).apply(compiler);
}
}

View File

@@ -6,17 +6,40 @@
const loaderFlag = "LOADER_EXECUTION";
exports.cutOffLoaderExecution = (stack) => {
const webpackOptionsFlag = "WEBPACK_OPTIONS";
exports.cutOffByFlag = (stack, flag) => {
stack = stack.split("\n");
for(let i = 0; i < stack.length; i++)
if(stack[i].indexOf(loaderFlag) >= 0)
for (let i = 0; i < stack.length; i++) {
if (stack[i].includes(flag)) {
stack.length = i;
}
}
return stack.join("\n");
};
exports.cutOffLoaderExecution = stack =>
exports.cutOffByFlag(stack, loaderFlag);
exports.cutOffWebpackOptions = stack =>
exports.cutOffByFlag(stack, webpackOptionsFlag);
exports.cutOffMultilineMessage = (stack, message) => {
stack = stack.split("\n");
message = message.split("\n");
return stack
.reduce(
(acc, line, idx) =>
line.includes(message[idx]) ? acc : acc.concat(line),
[]
)
.join("\n");
};
exports.cutOffMessage = (stack, message) => {
const nextLine = stack.indexOf("\n");
if(nextLine === -1) {
if (nextLine === -1) {
return stack === message ? "" : stack;
} else {
const firstLine = stack.substr(0, nextLine);
@@ -29,3 +52,9 @@ exports.cleanUp = (stack, message) => {
stack = exports.cutOffMessage(stack, message);
return stack;
};
exports.cleanUpWebpackOptions = (stack, message) => {
stack = exports.cutOffWebpackOptions(stack);
stack = exports.cutOffMultilineMessage(stack, message);
return stack;
};

View File

@@ -7,14 +7,19 @@
const EvalDevToolModuleTemplatePlugin = require("./EvalDevToolModuleTemplatePlugin");
class EvalDevToolModulePlugin {
constructor(sourceUrlComment, moduleFilenameTemplate) {
this.sourceUrlComment = sourceUrlComment;
this.moduleFilenameTemplate = moduleFilenameTemplate;
constructor(options) {
this.sourceUrlComment = options.sourceUrlComment;
this.moduleFilenameTemplate = options.moduleFilenameTemplate;
this.namespace = options.namespace;
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.moduleTemplate.apply(new EvalDevToolModuleTemplatePlugin(this.sourceUrlComment, this.moduleFilenameTemplate));
compiler.hooks.compilation.tap("EvalDevToolModulePlugin", compilation => {
new EvalDevToolModuleTemplatePlugin({
sourceUrlComment: this.sourceUrlComment,
moduleFilenameTemplate: this.moduleFilenameTemplate,
namespace: this.namespace
}).apply(compilation.moduleTemplates.javascript);
});
}
}

View File

@@ -4,26 +4,54 @@
*/
"use strict";
const RawSource = require("webpack-sources").RawSource;
const { RawSource } = require("webpack-sources");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const cache = new WeakMap();
class EvalDevToolModuleTemplatePlugin {
constructor(sourceUrlComment, moduleFilenameTemplate) {
this.sourceUrlComment = sourceUrlComment || "\n//# sourceURL=[url]";
this.moduleFilenameTemplate = moduleFilenameTemplate || "webpack:///[resourcePath]?[loaders]";
constructor(options) {
this.sourceUrlComment = options.sourceUrlComment || "\n//# sourceURL=[url]";
this.moduleFilenameTemplate =
options.moduleFilenameTemplate ||
"webpack://[namespace]/[resourcePath]?[loaders]";
this.namespace = options.namespace || "";
}
apply(moduleTemplate) {
moduleTemplate.plugin("module", (source, module) => {
const content = source.source();
const str = ModuleFilenameHelpers.createFilename(module, this.moduleFilenameTemplate, moduleTemplate.requestShortener);
const footer = ["\n",
ModuleFilenameHelpers.createFooter(module, moduleTemplate.requestShortener),
this.sourceUrlComment.replace(/\[url\]/g, encodeURI(str).replace(/%2F/g, "/").replace(/%20/g, "_").replace(/%5E/g, "^").replace(/%5C/g, "\\").replace(/^\//, ""))
].join("\n");
return new RawSource(`eval(${JSON.stringify(content + footer)});`);
});
moduleTemplate.plugin("hash", hash => {
moduleTemplate.hooks.module.tap(
"EvalDevToolModuleTemplatePlugin",
(source, module) => {
const cacheEntry = cache.get(source);
if (cacheEntry !== undefined) return cacheEntry;
const content = source.source();
const str = ModuleFilenameHelpers.createFilename(
module,
{
moduleFilenameTemplate: this.moduleFilenameTemplate,
namespace: this.namespace
},
moduleTemplate.runtimeTemplate.requestShortener
);
const footer =
"\n" +
this.sourceUrlComment.replace(
/\[url\]/g,
encodeURI(str)
.replace(/%2F/g, "/")
.replace(/%20/g, "_")
.replace(/%5E/g, "^")
.replace(/%5C/g, "\\")
.replace(/^\//, "")
);
const result = new RawSource(
`eval(${JSON.stringify(content + footer)});`
);
cache.set(source, result);
return result;
}
);
moduleTemplate.hooks.hash.tap("EvalDevToolModuleTemplatePlugin", hash => {
hash.update("EvalDevToolModuleTemplatePlugin");
hash.update("2");
});

View File

@@ -4,72 +4,112 @@
*/
"use strict";
const RawSource = require("webpack-sources").RawSource;
const { RawSource } = require("webpack-sources");
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const cache = new WeakMap();
class EvalSourceMapDevToolModuleTemplatePlugin {
constructor(compilation, options) {
this.compilation = compilation;
this.sourceMapComment = options.append || "//# sourceURL=[module]\n//# sourceMappingURL=[url]";
this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack:///[resource-path]?[hash]";
this.sourceMapComment =
options.append || "//# sourceURL=[module]\n//# sourceMappingURL=[url]";
this.moduleFilenameTemplate =
options.moduleFilenameTemplate ||
"webpack://[namespace]/[resource-path]?[hash]";
this.namespace = options.namespace || "";
this.options = options;
}
apply(moduleTemplate) {
const self = this;
const options = this.options;
moduleTemplate.plugin("module", function(source, module) {
if(source.__EvalSourceMapDevToolData)
return source.__EvalSourceMapDevToolData;
let sourceMap;
let content;
if(source.sourceAndMap) {
const sourceAndMap = source.sourceAndMap(options);
sourceMap = sourceAndMap.map;
content = sourceAndMap.source;
} else {
sourceMap = source.map(options);
content = source.source();
}
if(!sourceMap) {
return source;
}
const matchModule = ModuleFilenameHelpers.matchObject.bind(
ModuleFilenameHelpers,
options
);
moduleTemplate.hooks.module.tap(
"EvalSourceMapDevToolModuleTemplatePlugin",
(source, module) => {
const cachedSource = cache.get(source);
if (cachedSource !== undefined) {
return cachedSource;
}
// Clone (flat) the sourcemap to ensure that the mutations below do not persist.
sourceMap = Object.keys(sourceMap).reduce(function(obj, key) {
obj[key] = sourceMap[key];
return obj;
}, {});
const modules = sourceMap.sources.map(function(source) {
const module = self.compilation.findModule(source);
return module || source;
});
let moduleFilenames = modules.map(function(module) {
return ModuleFilenameHelpers.createFilename(module, self.moduleFilenameTemplate, this.requestShortener);
}, this);
moduleFilenames = ModuleFilenameHelpers.replaceDuplicates(moduleFilenames, function(filename, i, n) {
for(let j = 0; j < n; j++)
filename += "*";
return filename;
});
sourceMap.sources = moduleFilenames;
if(sourceMap.sourcesContent) {
sourceMap.sourcesContent = sourceMap.sourcesContent.map(function(content, i) {
return typeof content === "string" ? `${content}\n\n\n${ModuleFilenameHelpers.createFooter(modules[i], this.requestShortener)}` : null;
}, this);
}
sourceMap.sourceRoot = options.sourceRoot || "";
sourceMap.file = `${module.id}.js`;
if (!matchModule(module.resource)) {
return source;
}
const footer = self.sourceMapComment.replace(/\[url\]/g, `data:application/json;charset=utf-8;base64,${new Buffer(JSON.stringify(sourceMap), "utf8").toString("base64")}`) + //eslint-disable-line
`\n//# sourceURL=webpack-internal:///${module.id}\n`; // workaround for chrome bug
source.__EvalSourceMapDevToolData = new RawSource(`eval(${JSON.stringify(content + footer)});`);
return source.__EvalSourceMapDevToolData;
});
moduleTemplate.plugin("hash", function(hash) {
hash.update("eval-source-map");
hash.update("2");
});
/** @type {{ [key: string]: TODO; }} */
let sourceMap;
let content;
if (source.sourceAndMap) {
const sourceAndMap = source.sourceAndMap(options);
sourceMap = sourceAndMap.map;
content = sourceAndMap.source;
} else {
sourceMap = source.map(options);
content = source.source();
}
if (!sourceMap) {
return source;
}
// Clone (flat) the sourcemap to ensure that the mutations below do not persist.
sourceMap = Object.keys(sourceMap).reduce((obj, key) => {
obj[key] = sourceMap[key];
return obj;
}, {});
const modules = sourceMap.sources.map(source => {
const module = self.compilation.findModule(source);
return module || source;
});
let moduleFilenames = modules.map(module => {
return ModuleFilenameHelpers.createFilename(
module,
{
moduleFilenameTemplate: self.moduleFilenameTemplate,
namespace: self.namespace
},
moduleTemplate.runtimeTemplate.requestShortener
);
});
moduleFilenames = ModuleFilenameHelpers.replaceDuplicates(
moduleFilenames,
(filename, i, n) => {
for (let j = 0; j < n; j++) filename += "*";
return filename;
}
);
sourceMap.sources = moduleFilenames;
sourceMap.sourceRoot = options.sourceRoot || "";
sourceMap.file = `${module.id}.js`;
const footer =
self.sourceMapComment.replace(
/\[url\]/g,
`data:application/json;charset=utf-8;base64,${Buffer.from(
JSON.stringify(sourceMap),
"utf8"
).toString("base64")}`
) + `\n//# sourceURL=webpack-internal:///${module.id}\n`; // workaround for chrome bug
const evalSource = new RawSource(
`eval(${JSON.stringify(content + footer)});`
);
cache.set(source, evalSource);
return evalSource;
}
);
moduleTemplate.hooks.hash.tap(
"EvalSourceMapDevToolModuleTemplatePlugin",
hash => {
hash.update("eval-source-map");
hash.update("2");
}
);
}
}
module.exports = EvalSourceMapDevToolModuleTemplatePlugin;

View File

@@ -9,23 +9,32 @@ const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOpt
class EvalSourceMapDevToolPlugin {
constructor(options) {
if(arguments.length > 1)
throw new Error("EvalSourceMapDevToolPlugin only takes one argument (pass an options object)");
if(typeof options === "string") {
if (arguments.length > 1) {
throw new Error(
"EvalSourceMapDevToolPlugin only takes one argument (pass an options object)"
);
}
if (typeof options === "string") {
options = {
append: options
};
}
if(!options) options = {};
if (!options) options = {};
this.options = options;
}
apply(compiler) {
const options = this.options;
compiler.plugin("compilation", (compilation) => {
new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
compilation.moduleTemplate.apply(new EvalSourceMapDevToolModuleTemplatePlugin(compilation, options));
});
compiler.hooks.compilation.tap(
"EvalSourceMapDevToolPlugin",
compilation => {
new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
new EvalSourceMapDevToolModuleTemplatePlugin(
compilation,
options
).apply(compilation.moduleTemplates.javascript);
}
);
}
}

View File

@@ -4,24 +4,46 @@
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const { ConcatSource } = require("webpack-sources");
function accessorToObjectAccess(accessor) {
/** @typedef {import("./Compilation")} Compilation */
/**
* @param {string[]} accessor the accessor to convert to path
* @returns {string} the path
*/
const accessorToObjectAccess = accessor => {
return accessor.map(a => `[${JSON.stringify(a)}]`).join("");
}
};
class ExportPropertyMainTemplatePlugin {
/**
* @param {string|string[]} property the name of the property to export
*/
constructor(property) {
this.property = property;
}
/**
* @param {Compilation} compilation the compilation instance
* @returns {void}
*/
apply(compilation) {
const mainTemplate = compilation.mainTemplate;
compilation.templatesPlugin("render-with-entry", (source, chunk, hash) => {
const { mainTemplate, chunkTemplate } = compilation;
const onRenderWithEntry = (source, chunk, hash) => {
const postfix = `${accessorToObjectAccess([].concat(this.property))}`;
return new ConcatSource(source, postfix);
});
mainTemplate.plugin("hash", hash => {
};
for (const template of [mainTemplate, chunkTemplate]) {
template.hooks.renderWithEntry.tap(
"ExportPropertyMainTemplatePlugin",
onRenderWithEntry
);
}
mainTemplate.hooks.hash.tap("ExportPropertyMainTemplatePlugin", hash => {
hash.update("export property");
hash.update(`${this.property}`);
});

View File

@@ -4,43 +4,84 @@
*/
"use strict";
const Template = require("./Template");
const ConstDependency = require("./dependencies/ConstDependency");
const ParserHelpers = require("./ParserHelpers");
const NullFactory = require("./NullFactory");
const REPLACEMENTS = {
__webpack_hash__: "__webpack_require__.h", // eslint-disable-line camelcase
__webpack_chunkname__: "__webpack_require__.cn" // eslint-disable-line camelcase
// eslint-disable-next-line camelcase
__webpack_hash__: "__webpack_require__.h",
// eslint-disable-next-line camelcase
__webpack_chunkname__: "__webpack_require__.cn"
};
const REPLACEMENT_TYPES = {
__webpack_hash__: "string", // eslint-disable-line camelcase
__webpack_chunkname__: "string" // eslint-disable-line camelcase
// eslint-disable-next-line camelcase
__webpack_hash__: "string",
// eslint-disable-next-line camelcase
__webpack_chunkname__: "string"
};
class ExtendedAPIPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
compilation.mainTemplate.plugin("require-extensions", function(source, chunk, hash) {
const buf = [source];
buf.push("");
buf.push("// __webpack_hash__");
buf.push(`${this.requireFn}.h = ${JSON.stringify(hash)};`);
buf.push("");
buf.push("// __webpack_chunkname__");
buf.push(`${this.requireFn}.cn = ${JSON.stringify(chunk.name)};`);
return this.asString(buf);
});
compilation.mainTemplate.plugin("global-hash", () => true);
compiler.hooks.compilation.tap(
"ExtendedAPIPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
params.normalModuleFactory.plugin("parser", (parser, parserOptions) => {
Object.keys(REPLACEMENTS).forEach(key => {
parser.plugin(`expression ${key}`, ParserHelpers.toConstantDependency(REPLACEMENTS[key]));
parser.plugin(`evaluate typeof ${key}`, ParserHelpers.evaluateToString(REPLACEMENT_TYPES[key]));
});
});
});
const mainTemplate = compilation.mainTemplate;
mainTemplate.hooks.requireExtensions.tap(
"ExtendedAPIPlugin",
(source, chunk, hash) => {
const buf = [source];
buf.push("");
buf.push("// __webpack_hash__");
buf.push(`${mainTemplate.requireFn}.h = ${JSON.stringify(hash)};`);
buf.push("");
buf.push("// __webpack_chunkname__");
buf.push(
`${mainTemplate.requireFn}.cn = ${JSON.stringify(chunk.name)};`
);
return Template.asString(buf);
}
);
mainTemplate.hooks.globalHash.tap("ExtendedAPIPlugin", () => true);
const handler = (parser, parserOptions) => {
Object.keys(REPLACEMENTS).forEach(key => {
parser.hooks.expression
.for(key)
.tap(
"ExtendedAPIPlugin",
ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
REPLACEMENTS[key]
)
);
parser.hooks.evaluateTypeof
.for(key)
.tap(
"ExtendedAPIPlugin",
ParserHelpers.evaluateToString(REPLACEMENT_TYPES[key])
);
});
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("ExtendedAPIPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("ExtendedAPIPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("ExtendedAPIPlugin", handler);
}
);
}
}

View File

@@ -3,19 +3,22 @@
Author Tobias Koppers @sokra
*/
"use strict";
const { OriginalSource, RawSource } = require("webpack-sources");
const Module = require("./Module");
const OriginalSource = require("webpack-sources").OriginalSource;
const RawSource = require("webpack-sources").RawSource;
const WebpackMissingModule = require("./dependencies/WebpackMissingModule");
const Template = require("./Template");
/** @typedef {import("./util/createHash").Hash} Hash */
class ExternalModule extends Module {
constructor(request, type, userRequest) {
super();
super("javascript/dynamic", null);
// Info from Factory
this.request = request;
this.externalType = type;
this.userRequest = userRequest;
this.type = type;
this.built = false;
this.external = true;
}
@@ -40,85 +43,132 @@ class ExternalModule extends Module {
}
build(options, compilation, resolver, fs, callback) {
this.builtTime = Date.now();
this.built = true;
this.buildMeta = {};
this.buildInfo = {};
callback();
}
getSourceForGlobalVariableExternal(variableName, type) {
if(!Array.isArray(variableName)) {
if (!Array.isArray(variableName)) {
// make it an array as the look up works the same basically
variableName = [variableName];
}
// needed for e.g. window["some"]["thing"]
const objectLookup = variableName.map(r => `[${JSON.stringify(r)}]`).join("");
const objectLookup = variableName
.map(r => `[${JSON.stringify(r)}]`)
.join("");
return `(function() { module.exports = ${type}${objectLookup}; }());`;
}
getSourceForCommonJsExternal(moduleAndSpecifiers) {
if(!Array.isArray(moduleAndSpecifiers)) {
return `module.exports = require(${JSON.stringify(moduleAndSpecifiers)});`;
if (!Array.isArray(moduleAndSpecifiers)) {
return `module.exports = require(${JSON.stringify(
moduleAndSpecifiers
)});`;
}
const moduleName = moduleAndSpecifiers[0];
const objectLookup = moduleAndSpecifiers.slice(1).map(r => `[${JSON.stringify(r)}]`).join("");
return `module.exports = require(${moduleName})${objectLookup};`;
const objectLookup = moduleAndSpecifiers
.slice(1)
.map(r => `[${JSON.stringify(r)}]`)
.join("");
return `module.exports = require(${JSON.stringify(
moduleName
)})${objectLookup};`;
}
checkExternalVariable(variableToCheck, request) {
return `if(typeof ${variableToCheck} === 'undefined') {${WebpackMissingModule.moduleCode(request)}}\n`;
return `if(typeof ${variableToCheck} === 'undefined') {${WebpackMissingModule.moduleCode(
request
)}}\n`;
}
getSourceForAmdOrUmdExternal(id, optional, request) {
const externalVariable = Template.toIdentifier(`__WEBPACK_EXTERNAL_MODULE_${id}__`);
const missingModuleError = optional ? this.checkExternalVariable(externalVariable, request) : "";
const externalVariable = `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
`${id}`
)}__`;
const missingModuleError = optional
? this.checkExternalVariable(externalVariable, request)
: "";
return `${missingModuleError}module.exports = ${externalVariable};`;
}
getSourceForDefaultCase(optional, request) {
const missingModuleError = optional ? this.checkExternalVariable(request, request) : "";
return `${missingModuleError}module.exports = ${request};`;
if (!Array.isArray(request)) {
// make it an array as the look up works the same basically
request = [request];
}
const variableName = request[0];
const missingModuleError = optional
? this.checkExternalVariable(variableName, request.join("."))
: "";
const objectLookup = request
.slice(1)
.map(r => `[${JSON.stringify(r)}]`)
.join("");
return `${missingModuleError}module.exports = ${variableName}${objectLookup};`;
}
getSourceString() {
const request = typeof this.request === "object" ? this.request[this.type] : this.request;
switch(this.type) {
getSourceString(runtime) {
const request =
typeof this.request === "object" && !Array.isArray(this.request)
? this.request[this.externalType]
: this.request;
switch (this.externalType) {
case "this":
case "window":
case "self":
return this.getSourceForGlobalVariableExternal(
request,
this.externalType
);
case "global":
return this.getSourceForGlobalVariableExternal(request, this.type);
return this.getSourceForGlobalVariableExternal(
request,
runtime.outputOptions.globalObject
);
case "commonjs":
case "commonjs2":
return this.getSourceForCommonJsExternal(request);
case "amd":
case "amd-require":
case "umd":
case "umd2":
return this.getSourceForAmdOrUmdExternal(this.id, this.optional, request);
return this.getSourceForAmdOrUmdExternal(
this.id,
this.optional,
request
);
default:
return this.getSourceForDefaultCase(this.optional, request);
}
}
getSource(sourceString) {
if(this.useSourceMap) {
if (this.useSourceMap) {
return new OriginalSource(sourceString, this.identifier());
}
return new RawSource(sourceString);
}
source() {
return this.getSource(
this.getSourceString()
);
source(dependencyTemplates, runtime) {
return this.getSource(this.getSourceString(runtime));
}
size() {
return 42;
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
hash.update(this.type);
hash.update(this.externalType);
hash.update(JSON.stringify(this.request));
hash.update(JSON.stringify(Boolean(this.optional)));
super.updateHash(hash);

View File

@@ -14,78 +14,97 @@ class ExternalModuleFactoryPlugin {
apply(normalModuleFactory) {
const globalType = this.type;
normalModuleFactory.plugin("factory", factory => (data, callback) => {
const context = data.context;
const dependency = data.dependencies[0];
normalModuleFactory.hooks.factory.tap(
"ExternalModuleFactoryPlugin",
factory => (data, callback) => {
const context = data.context;
const dependency = data.dependencies[0];
function handleExternal(value, type, callback) {
if(typeof type === "function") {
callback = type;
type = undefined;
}
if(value === false) return factory(data, callback);
if(value === true) value = dependency.request;
if(typeof type === "undefined" && /^[a-z0-9]+ /.test(value)) {
const idx = value.indexOf(" ");
type = value.substr(0, idx);
value = value.substr(idx + 1);
}
callback(null, new ExternalModule(value, type || globalType, dependency.request));
return true;
}
(function handleExternals(externals, callback) {
if(typeof externals === "string") {
if(externals === dependency.request) {
return handleExternal(dependency.request, callback);
const handleExternal = (value, type, callback) => {
if (typeof type === "function") {
callback = type;
type = undefined;
}
} else if(Array.isArray(externals)) {
let i = 0;
(function next() {
let asyncFlag;
const handleExternalsAndCallback = function handleExternalsAndCallback(err, module) {
if(err) return callback(err);
if(!module) {
if(asyncFlag) {
asyncFlag = false;
return;
if (value === false) return factory(data, callback);
if (value === true) value = dependency.request;
if (type === undefined && /^[a-z0-9]+ /.test(value)) {
const idx = value.indexOf(" ");
type = value.substr(0, idx);
value = value.substr(idx + 1);
}
callback(
null,
new ExternalModule(value, type || globalType, dependency.request)
);
return true;
};
const handleExternals = (externals, callback) => {
if (typeof externals === "string") {
if (externals === dependency.request) {
return handleExternal(dependency.request, callback);
}
} else if (Array.isArray(externals)) {
let i = 0;
const next = () => {
let asyncFlag;
const handleExternalsAndCallback = (err, module) => {
if (err) return callback(err);
if (!module) {
if (asyncFlag) {
asyncFlag = false;
return;
}
return next();
}
return next();
}
callback(null, module);
callback(null, module);
};
do {
asyncFlag = true;
if (i >= externals.length) return callback();
handleExternals(externals[i++], handleExternalsAndCallback);
} while (!asyncFlag);
asyncFlag = false;
};
do {
asyncFlag = true;
if(i >= externals.length) return callback();
handleExternals(externals[i++], handleExternalsAndCallback);
} while (!asyncFlag); // eslint-disable-line keyword-spacing
asyncFlag = false;
}());
return;
} else if(externals instanceof RegExp) {
if(externals.test(dependency.request)) {
return handleExternal(dependency.request, callback);
}
} else if(typeof externals === "function") {
externals.call(null, context, dependency.request, function(err, value, type) {
if(err) return callback(err);
if(typeof value !== "undefined") {
handleExternal(value, type, callback);
} else {
callback();
next();
return;
} else if (externals instanceof RegExp) {
if (externals.test(dependency.request)) {
return handleExternal(dependency.request, callback);
}
});
return;
} else if(typeof externals === "object" && Object.prototype.hasOwnProperty.call(externals, dependency.request)) {
return handleExternal(externals[dependency.request], callback);
}
callback();
}(this.externals, function(err, module) {
if(err) return callback(err);
if(!module) return handleExternal(false, callback);
return callback(null, module);
}));
});
} else if (typeof externals === "function") {
externals.call(
null,
context,
dependency.request,
(err, value, type) => {
if (err) return callback(err);
if (value !== undefined) {
handleExternal(value, type, callback);
} else {
callback();
}
}
);
return;
} else if (
typeof externals === "object" &&
Object.prototype.hasOwnProperty.call(externals, dependency.request)
) {
return handleExternal(externals[dependency.request], callback);
}
callback();
};
handleExternals(this.externals, (err, module) => {
if (err) return callback(err);
if (!module) return handleExternal(false, callback);
return callback(null, module);
});
}
);
}
}
module.exports = ExternalModuleFactoryPlugin;

View File

@@ -12,8 +12,10 @@ class ExternalsPlugin {
this.externals = externals;
}
apply(compiler) {
compiler.plugin("compile", (params) => {
params.normalModuleFactory.apply(new ExternalModuleFactoryPlugin(this.type, this.externals));
compiler.hooks.compile.tap("ExternalsPlugin", ({ normalModuleFactory }) => {
new ExternalModuleFactoryPlugin(this.type, this.externals).apply(
normalModuleFactory
);
});
}
}

View File

@@ -4,97 +4,151 @@
*/
"use strict";
const Queue = require("./util/Queue");
const addToSet = (a, b) => {
let changed = false;
for (const item of b) {
if (!a.has(item)) {
a.add(item);
changed = true;
}
}
return changed;
};
class FlagDependencyExportsPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("finish-modules", (modules) => {
const dependencies = Object.create(null);
compiler.hooks.compilation.tap(
"FlagDependencyExportsPlugin",
compilation => {
compilation.hooks.finishModules.tap(
"FlagDependencyExportsPlugin",
modules => {
const dependencies = new Map();
let module;
let moduleWithExports;
let moduleProvidedExports;
const queue = modules.filter((m) => !m.providedExports);
for(let i = 0; i < queue.length; i++) {
module = queue[i];
const queue = new Queue();
if(module.providedExports !== true) {
moduleWithExports = module.meta && module.meta.harmonyModule;
moduleProvidedExports = Array.isArray(module.providedExports) ? new Set(module.providedExports) : new Set();
processDependenciesBlock(module);
if(!moduleWithExports) {
module.providedExports = true;
notifyDependencies();
} else if(module.providedExports !== true) {
module.providedExports = Array.from(moduleProvidedExports);
}
}
}
let module;
let moduleWithExports;
let moduleProvidedExports;
let providedExportsAreTemporary;
function processDependenciesBlock(depBlock) {
depBlock.dependencies.forEach((dep) => processDependency(dep));
depBlock.variables.forEach((variable) => {
variable.dependencies.forEach((dep) => processDependency(dep));
});
depBlock.blocks.forEach(processDependenciesBlock);
}
function processDependency(dep) {
const exportDesc = dep.getExports && dep.getExports();
if(!exportDesc) return;
moduleWithExports = true;
const exports = exportDesc.exports;
const exportDeps = exportDesc.dependencies;
if(exportDeps) {
exportDeps.forEach((dep) => {
const depIdent = dep.identifier();
// if this was not yet initialized
// initialize it as an array containing the module and stop
const array = dependencies[depIdent];
if(!array) {
dependencies[depIdent] = [module];
return;
const processDependenciesBlock = depBlock => {
for (const dep of depBlock.dependencies) {
if (processDependency(dep)) return true;
}
for (const variable of depBlock.variables) {
for (const dep of variable.dependencies) {
if (processDependency(dep)) return true;
}
}
for (const block of depBlock.blocks) {
if (processDependenciesBlock(block)) return true;
}
return false;
};
// check if this module is known
// if not, add it to the dependencies for this identifier
if(array.indexOf(module) < 0)
array.push(module);
});
}
let changed = false;
if(module.providedExports !== true) {
if(exports === true) {
module.providedExports = true;
changed = true;
} else if(Array.isArray(exports)) {
changed = addToSet(moduleProvidedExports, exports);
const processDependency = dep => {
const exportDesc = dep.getExports && dep.getExports();
if (!exportDesc) return;
moduleWithExports = true;
const exports = exportDesc.exports;
// break early if it's only in the worst state
if (module.buildMeta.providedExports === true) {
return true;
}
// break if it should move to the worst state
if (exports === true) {
module.buildMeta.providedExports = true;
notifyDependencies();
return true;
}
// merge in new exports
if (Array.isArray(exports)) {
if (addToSet(moduleProvidedExports, exports)) {
notifyDependencies();
}
}
// store dependencies
const exportDeps = exportDesc.dependencies;
if (exportDeps) {
providedExportsAreTemporary = true;
for (const exportDependency of exportDeps) {
// add dependency for this module
const set = dependencies.get(exportDependency);
if (set === undefined) {
dependencies.set(exportDependency, new Set([module]));
} else {
set.add(module);
}
}
}
return false;
};
const notifyDependencies = () => {
const deps = dependencies.get(module);
if (deps !== undefined) {
for (const dep of deps) {
queue.enqueue(dep);
}
}
};
// Start with all modules without provided exports
for (const module of modules) {
if (module.buildInfo.temporaryProvidedExports) {
// Clear exports when they are temporary
// and recreate them
module.buildMeta.providedExports = null;
queue.enqueue(module);
} else if (!module.buildMeta.providedExports) {
queue.enqueue(module);
}
}
while (queue.length > 0) {
module = queue.dequeue();
if (module.buildMeta.providedExports !== true) {
moduleWithExports =
module.buildMeta && module.buildMeta.exportsType;
moduleProvidedExports = Array.isArray(
module.buildMeta.providedExports
)
? new Set(module.buildMeta.providedExports)
: new Set();
providedExportsAreTemporary = false;
processDependenciesBlock(module);
module.buildInfo.temporaryProvidedExports = providedExportsAreTemporary;
if (!moduleWithExports) {
module.buildMeta.providedExports = true;
notifyDependencies();
} else if (module.buildMeta.providedExports !== true) {
module.buildMeta.providedExports = Array.from(
moduleProvidedExports
);
}
}
}
}
if(changed) {
notifyDependencies();
);
const providedExportsCache = new WeakMap();
compilation.hooks.rebuildModule.tap(
"FlagDependencyExportsPlugin",
module => {
providedExportsCache.set(module, module.buildMeta.providedExports);
}
}
function notifyDependencies() {
const deps = dependencies[module.identifier()];
if(deps) {
deps.forEach((dep) => queue.push(dep));
);
compilation.hooks.finishRebuildingModule.tap(
"FlagDependencyExportsPlugin",
module => {
module.buildMeta.providedExports = providedExportsCache.get(module);
}
}
});
function addToSet(a, b) {
let changed = false;
b.forEach((item) => {
if(!a.has(item)) {
a.add(item);
changed = true;
}
});
return changed;
);
}
});
);
}
}

View File

@@ -4,77 +4,112 @@
*/
"use strict";
/** @typedef {import("./Module")} Module */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {false | true | string[]} UsedExports */
const addToSet = (a, b) => {
for (const item of b) {
if (!a.includes(item)) a.push(item);
}
return a;
};
const isSubset = (biggerSet, subset) => {
if (biggerSet === true) return true;
if (subset === true) return false;
return subset.every(item => biggerSet.indexOf(item) >= 0);
};
class FlagDependencyUsagePlugin {
apply(compiler) {
compiler.plugin("compilation", compilation => {
compilation.plugin("optimize-modules-advanced", modules => {
modules.forEach(module => module.used = false);
const queue = [];
compilation.chunks.forEach(chunk => {
if(chunk.entryModule) {
processModule(chunk.entryModule, true);
}
});
while(queue.length) {
const queueItem = queue.pop();
processDependenciesBlock(queueItem[0], queueItem[1]);
}
function processModule(module, usedExports) {
module.used = true;
if(module.usedExports === true)
return;
else if(usedExports === true)
module.usedExports = true;
else if(Array.isArray(usedExports)) {
const old = module.usedExports ? module.usedExports.length : -1;
module.usedExports = addToSet(module.usedExports || [], usedExports);
if(module.usedExports.length === old)
compiler.hooks.compilation.tap("FlagDependencyUsagePlugin", compilation => {
compilation.hooks.optimizeDependencies.tap(
"FlagDependencyUsagePlugin",
modules => {
const processModule = (module, usedExports) => {
module.used = true;
if (module.usedExports === true) return;
if (usedExports === true) {
module.usedExports = true;
} else if (Array.isArray(usedExports)) {
const old = module.usedExports ? module.usedExports.length : -1;
module.usedExports = addToSet(
module.usedExports || [],
usedExports
);
if (module.usedExports.length === old) {
return;
}
} else if (Array.isArray(module.usedExports)) {
return;
} else if(Array.isArray(module.usedExports))
return;
else
module.usedExports = false;
} else {
module.usedExports = false;
}
queue.push([module, module.usedExports]);
}
// for a module without side effects we stop tracking usage here when no export is used
// This module won't be evaluated in this case
if (module.factoryMeta.sideEffectFree) {
if (module.usedExports === false) return;
if (
Array.isArray(module.usedExports) &&
module.usedExports.length === 0
)
return;
}
function processDependenciesBlock(depBlock, usedExports) {
depBlock.dependencies.forEach(dep => processDependency(dep));
depBlock.variables.forEach(variable => variable.dependencies.forEach(dep => processDependency(dep)));
depBlock.blocks.forEach(block => queue.push([block, usedExports]));
}
queue.push([module, module, module.usedExports]);
};
function processDependency(dep) {
const reference = dep.getReference && dep.getReference();
if(!reference) return;
const module = reference.module;
const importedNames = reference.importedNames;
const oldUsed = module.used;
const oldUsedExports = module.usedExports;
if(!oldUsed || (importedNames && (!oldUsedExports || !isSubset(oldUsedExports, importedNames)))) {
processModule(module, importedNames);
const processDependenciesBlock = (module, depBlock, usedExports) => {
for (const dep of depBlock.dependencies) {
processDependency(module, dep);
}
for (const variable of depBlock.variables) {
for (const dep of variable.dependencies) {
processDependency(module, dep);
}
}
for (const block of depBlock.blocks) {
queue.push([module, block, usedExports]);
}
};
const processDependency = (module, dep) => {
const reference = compilation.getDependencyReference(module, dep);
if (!reference) return;
const referenceModule = reference.module;
const importedNames = reference.importedNames;
const oldUsed = referenceModule.used;
const oldUsedExports = referenceModule.usedExports;
if (
!oldUsed ||
(importedNames &&
(!oldUsedExports || !isSubset(oldUsedExports, importedNames)))
) {
processModule(referenceModule, importedNames);
}
};
for (const module of modules) {
module.used = false;
}
/** @type {[Module, DependenciesBlock, UsedExports][]} */
const queue = [];
for (const preparedEntrypoint of compilation._preparedEntrypoints) {
if (preparedEntrypoint.module) {
processModule(preparedEntrypoint.module, true);
}
}
while (queue.length) {
const queueItem = queue.pop();
processDependenciesBlock(queueItem[0], queueItem[1], queueItem[2]);
}
}
});
function addToSet(a, b) {
b.forEach(item => {
if(a.indexOf(item) < 0)
a.push(item);
});
return a;
}
function isSubset(biggerSet, subset) {
if(biggerSet === true) return true;
if(subset === true) return false;
return subset.every(item => biggerSet.indexOf(item) >= 0);
}
);
});
}
}

View File

@@ -5,19 +5,31 @@
"use strict";
class FlagInitialModulesAsUsedPlugin {
constructor(explanation) {
this.explanation = explanation;
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("after-optimize-chunks", (chunks) => {
chunks.forEach((chunk) => {
if(!chunk.isInitial()) {
return;
compiler.hooks.compilation.tap(
"FlagInitialModulesAsUsedPlugin",
compilation => {
compilation.hooks.afterOptimizeChunks.tap(
"FlagInitialModulesAsUsedPlugin",
chunks => {
for (const chunk of chunks) {
if (!chunk.isOnlyInitial()) {
return;
}
for (const module of chunk.modulesIterable) {
module.used = true;
module.usedExports = true;
module.addReason(null, null, this.explanation);
}
}
}
chunk.forEachModule((module) => {
module.usedExports = true;
});
});
});
});
);
}
);
}
}

View File

@@ -5,18 +5,13 @@
"use strict";
const FunctionModuleTemplatePlugin = require("./FunctionModuleTemplatePlugin");
const RequestShortener = require("./RequestShortener");
class FunctionModulePlugin {
constructor(options, requestShortener) {
this.options = options;
this.requestShortener = requestShortener;
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.moduleTemplate.requestShortener = this.requestShortener || new RequestShortener(compiler.context);
compilation.moduleTemplate.apply(new FunctionModuleTemplatePlugin());
compiler.hooks.compilation.tap("FunctionModulePlugin", compilation => {
new FunctionModuleTemplatePlugin().apply(
compilation.moduleTemplates.javascript
);
});
}
}

View File

@@ -4,55 +4,94 @@
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const { ConcatSource } = require("webpack-sources");
const Template = require("./Template");
class FunctionModuleTemplatePlugin {
apply(moduleTemplate) {
moduleTemplate.plugin("render", function(moduleSource, module) {
const source = new ConcatSource();
const defaultArguments = [module.moduleArgument || "module", module.exportsArgument || "exports"];
if((module.arguments && module.arguments.length !== 0) || module.hasDependencies(d => d.requireWebpackRequire !== false)) {
defaultArguments.push("__webpack_require__");
}
source.add("/***/ (function(" + defaultArguments.concat(module.arguments || []).join(", ") + ") {\n\n");
if(module.strict) source.add("\"use strict\";\n");
source.add(moduleSource);
source.add("\n\n/***/ })");
return source;
});
moduleTemplate.plugin("package", function(moduleSource, module) {
if(this.outputOptions.pathinfo) {
moduleTemplate.hooks.render.tap(
"FunctionModuleTemplatePlugin",
(moduleSource, module) => {
const source = new ConcatSource();
const req = module.readableIdentifier(this.requestShortener);
source.add("/*!****" + req.replace(/./g, "*") + "****!*\\\n");
source.add(" !*** " + req.replace(/\*\//g, "*_/") + " ***!\n");
source.add(" \\****" + req.replace(/./g, "*") + "****/\n");
if(Array.isArray(module.providedExports) && module.providedExports.length === 0)
source.add("/*! no exports provided */\n");
else if(Array.isArray(module.providedExports))
source.add("/*! exports provided: " + module.providedExports.join(", ") + " */\n");
else if(module.providedExports)
source.add("/*! dynamic exports provided */\n");
if(Array.isArray(module.usedExports) && module.usedExports.length === 0)
source.add("/*! no exports used */\n");
else if(Array.isArray(module.usedExports))
source.add("/*! exports used: " + module.usedExports.join(", ") + " */\n");
else if(module.usedExports)
source.add("/*! all exports used */\n");
if(module.optimizationBailout) {
module.optimizationBailout.forEach(text => {
if(typeof text === "function") text = text(this.requestShortener);
source.add(`/*! ${text} */\n`);
});
const args = [module.moduleArgument];
// TODO remove HACK checking type for javascript
if (module.type && module.type.startsWith("javascript")) {
args.push(module.exportsArgument);
if (module.hasDependencies(d => d.requireWebpackRequire !== false)) {
args.push("__webpack_require__");
}
} else if (module.type && module.type.startsWith("json")) {
// no additional arguments needed
} else {
args.push(module.exportsArgument, "__webpack_require__");
}
source.add("/***/ (function(" + args.join(", ") + ") {\n\n");
if (module.buildInfo.strict) source.add('"use strict";\n');
source.add(moduleSource);
source.add("\n\n/***/ })");
return source;
}
return moduleSource;
});
);
moduleTemplate.plugin("hash", function(hash) {
moduleTemplate.hooks.package.tap(
"FunctionModuleTemplatePlugin",
(moduleSource, module) => {
if (moduleTemplate.runtimeTemplate.outputOptions.pathinfo) {
const source = new ConcatSource();
const req = module.readableIdentifier(
moduleTemplate.runtimeTemplate.requestShortener
);
source.add("/*!****" + req.replace(/./g, "*") + "****!*\\\n");
source.add(" !*** " + req.replace(/\*\//g, "*_/") + " ***!\n");
source.add(" \\****" + req.replace(/./g, "*") + "****/\n");
if (
Array.isArray(module.buildMeta.providedExports) &&
module.buildMeta.providedExports.length === 0
) {
source.add(Template.toComment("no exports provided") + "\n");
} else if (Array.isArray(module.buildMeta.providedExports)) {
source.add(
Template.toComment(
"exports provided: " +
module.buildMeta.providedExports.join(", ")
) + "\n"
);
} else if (module.buildMeta.providedExports) {
source.add(Template.toComment("no static exports found") + "\n");
}
if (
Array.isArray(module.usedExports) &&
module.usedExports.length === 0
) {
source.add(Template.toComment("no exports used") + "\n");
} else if (Array.isArray(module.usedExports)) {
source.add(
Template.toComment(
"exports used: " + module.usedExports.join(", ")
) + "\n"
);
} else if (module.usedExports) {
source.add(Template.toComment("all exports used") + "\n");
}
if (module.optimizationBailout) {
for (const text of module.optimizationBailout) {
let code;
if (typeof text === "function") {
code = text(moduleTemplate.runtimeTemplate.requestShortener);
} else {
code = text;
}
source.add(Template.toComment(`${code}`) + "\n");
}
}
source.add(moduleSource);
return source;
}
return moduleSource;
}
);
moduleTemplate.hooks.hash.tap("FunctionModuleTemplatePlugin", hash => {
hash.update("FunctionModuleTemplatePlugin");
hash.update("2");
});

View File

@@ -3,38 +3,57 @@
Author Tobias Koppers @sokra
*/
"use strict";
const createHash = require("crypto").createHash;
const createHash = require("./util/createHash");
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/HashedModuleIdsPlugin.json");
/** @typedef {import("../declarations/plugins/HashedModuleIdsPlugin").HashedModuleIdsPluginOptions} HashedModuleIdsPluginOptions */
class HashedModuleIdsPlugin {
/**
* @param {HashedModuleIdsPluginOptions=} options options object
*/
constructor(options) {
this.options = Object.assign({
hashFunction: "md5",
hashDigest: "base64",
hashDigestLength: 4
}, options);
if (!options) options = {};
validateOptions(schema, options, "Hashed Module Ids Plugin");
/** @type {HashedModuleIdsPluginOptions} */
this.options = Object.assign(
{
context: null,
hashFunction: "md4",
hashDigest: "base64",
hashDigestLength: 4
},
options
);
}
apply(compiler) {
const options = this.options;
compiler.plugin("compilation", (compilation) => {
compiler.hooks.compilation.tap("HashedModuleIdsPlugin", compilation => {
const usedIds = new Set();
compilation.plugin("before-module-ids", (modules) => {
modules.forEach((module) => {
if(module.id === null && module.libIdent) {
const id = module.libIdent({
context: this.options.context || compiler.options.context
});
const hash = createHash(options.hashFunction);
hash.update(id);
const hashId = hash.digest(options.hashDigest);
let len = options.hashDigestLength;
while(usedIds.has(hashId.substr(0, len)))
len++;
module.id = hashId.substr(0, len);
usedIds.add(module.id);
compilation.hooks.beforeModuleIds.tap(
"HashedModuleIdsPlugin",
modules => {
for (const module of modules) {
if (module.id === null && module.libIdent) {
const id = module.libIdent({
context: this.options.context || compiler.options.context
});
const hash = createHash(options.hashFunction);
hash.update(id);
const hashId = hash.digest(options.hashDigest);
let len = options.hashDigestLength;
while (usedIds.has(hashId.substr(0, len))) len++;
module.id = hashId.substr(0, len);
usedIds.add(module.id);
}
}
});
});
}
);
});
}
}

View File

@@ -4,31 +4,41 @@
*/
/*global $hash$ $requestTimeout$ installedModules $require$ hotDownloadManifest hotDownloadUpdateChunk hotDisposeChunk modules */
module.exports = function() {
var hotApplyOnUpdate = true;
var hotCurrentHash = $hash$; // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
var hotCurrentHash = $hash$;
var hotRequestTimeout = $requestTimeout$;
var hotCurrentModuleData = {};
var hotCurrentChildModule; // eslint-disable-line no-unused-vars
var hotCurrentParents = []; // eslint-disable-line no-unused-vars
var hotCurrentParentsTemp = []; // eslint-disable-line no-unused-vars
var hotCurrentChildModule;
// eslint-disable-next-line no-unused-vars
var hotCurrentParents = [];
// eslint-disable-next-line no-unused-vars
var hotCurrentParentsTemp = [];
function hotCreateRequire(moduleId) { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotCreateRequire(moduleId) {
var me = installedModules[moduleId];
if(!me) return $require$;
if (!me) return $require$;
var fn = function(request) {
if(me.hot.active) {
if(installedModules[request]) {
if(installedModules[request].parents.indexOf(moduleId) < 0)
if (me.hot.active) {
if (installedModules[request]) {
if (installedModules[request].parents.indexOf(moduleId) === -1) {
installedModules[request].parents.push(moduleId);
}
} else {
hotCurrentParents = [moduleId];
hotCurrentChildModule = request;
}
if(me.children.indexOf(request) < 0)
if (me.children.indexOf(request) === -1) {
me.children.push(request);
}
} else {
console.warn("[HMR] unexpected require(" + request + ") from disposed module " + moduleId);
console.warn(
"[HMR] unexpected require(" +
request +
") from disposed module " +
moduleId
);
hotCurrentParents = [];
}
return $require$(request);
@@ -45,14 +55,17 @@ module.exports = function() {
}
};
};
for(var name in $require$) {
if(Object.prototype.hasOwnProperty.call($require$, name) && name !== "e") {
for (var name in $require$) {
if (
Object.prototype.hasOwnProperty.call($require$, name) &&
name !== "e" &&
name !== "t"
) {
Object.defineProperty(fn, name, ObjectFactory(name));
}
}
fn.e = function(chunkId) {
if(hotStatus === "ready")
hotSetStatus("prepare");
if (hotStatus === "ready") hotSetStatus("prepare");
hotChunksLoading++;
return $require$.e(chunkId).then(finishChunkLoading, function(err) {
finishChunkLoading();
@@ -61,20 +74,25 @@ module.exports = function() {
function finishChunkLoading() {
hotChunksLoading--;
if(hotStatus === "prepare") {
if(!hotWaitingFilesMap[chunkId]) {
if (hotStatus === "prepare") {
if (!hotWaitingFilesMap[chunkId]) {
hotEnsureUpdateChunk(chunkId);
}
if(hotChunksLoading === 0 && hotWaitingFiles === 0) {
if (hotChunksLoading === 0 && hotWaitingFiles === 0) {
hotUpdateDownloaded();
}
}
}
};
fn.t = function(value, mode) {
if (mode & 1) value = fn(value);
return $require$.t(value, mode & ~1);
};
return fn;
}
function hotCreateModule(moduleId) { // eslint-disable-line no-unused-vars
// eslint-disable-next-line no-unused-vars
function hotCreateModule(moduleId) {
var hot = {
// private stuff
_acceptedDependencies: {},
@@ -87,24 +105,19 @@ module.exports = function() {
// Module API
active: true,
accept: function(dep, callback) {
if(typeof dep === "undefined")
hot._selfAccepted = true;
else if(typeof dep === "function")
hot._selfAccepted = dep;
else if(typeof dep === "object")
for(var i = 0; i < dep.length; i++)
if (dep === undefined) hot._selfAccepted = true;
else if (typeof dep === "function") hot._selfAccepted = dep;
else if (typeof dep === "object")
for (var i = 0; i < dep.length; i++)
hot._acceptedDependencies[dep[i]] = callback || function() {};
else
hot._acceptedDependencies[dep] = callback || function() {};
else hot._acceptedDependencies[dep] = callback || function() {};
},
decline: function(dep) {
if(typeof dep === "undefined")
hot._selfDeclined = true;
else if(typeof dep === "object")
for(var i = 0; i < dep.length; i++)
if (dep === undefined) hot._selfDeclined = true;
else if (typeof dep === "object")
for (var i = 0; i < dep.length; i++)
hot._declinedDependencies[dep[i]] = true;
else
hot._declinedDependencies[dep] = true;
else hot._declinedDependencies[dep] = true;
},
dispose: function(callback) {
hot._disposeHandlers.push(callback);
@@ -114,14 +127,14 @@ module.exports = function() {
},
removeDisposeHandler: function(callback) {
var idx = hot._disposeHandlers.indexOf(callback);
if(idx >= 0) hot._disposeHandlers.splice(idx, 1);
if (idx >= 0) hot._disposeHandlers.splice(idx, 1);
},
// Management API
check: hotCheck,
apply: hotApply,
status: function(l) {
if(!l) return hotStatus;
if (!l) return hotStatus;
hotStatusHandlers.push(l);
},
addStatusHandler: function(l) {
@@ -129,7 +142,7 @@ module.exports = function() {
},
removeStatusHandler: function(l) {
var idx = hotStatusHandlers.indexOf(l);
if(idx >= 0) hotStatusHandlers.splice(idx, 1);
if (idx >= 0) hotStatusHandlers.splice(idx, 1);
},
//inherit from previous dispose call
@@ -144,7 +157,7 @@ module.exports = function() {
function hotSetStatus(newStatus) {
hotStatus = newStatus;
for(var i = 0; i < hotStatusHandlers.length; i++)
for (var i = 0; i < hotStatusHandlers.length; i++)
hotStatusHandlers[i].call(null, newStatus);
}
@@ -160,16 +173,18 @@ module.exports = function() {
var hotUpdate, hotUpdateNewHash;
function toModuleId(id) {
var isNumber = (+id) + "" === id;
var isNumber = +id + "" === id;
return isNumber ? +id : id;
}
function hotCheck(apply) {
if(hotStatus !== "idle") throw new Error("check() is only allowed in idle status");
if (hotStatus !== "idle") {
throw new Error("check() is only allowed in idle status");
}
hotApplyOnUpdate = apply;
hotSetStatus("check");
return hotDownloadManifest(hotRequestTimeout).then(function(update) {
if(!update) {
if (!update) {
hotSetStatus("idle");
return null;
}
@@ -187,33 +202,39 @@ module.exports = function() {
});
hotUpdate = {};
/*foreachInstalledChunks*/
{ // eslint-disable-line no-lone-blocks
// eslint-disable-next-line no-lone-blocks
{
/*globals chunkId */
hotEnsureUpdateChunk(chunkId);
}
if(hotStatus === "prepare" && hotChunksLoading === 0 && hotWaitingFiles === 0) {
if (
hotStatus === "prepare" &&
hotChunksLoading === 0 &&
hotWaitingFiles === 0
) {
hotUpdateDownloaded();
}
return promise;
});
}
function hotAddUpdateChunk(chunkId, moreModules) { // eslint-disable-line no-unused-vars
if(!hotAvailableFilesMap[chunkId] || !hotRequestedFilesMap[chunkId])
// eslint-disable-next-line no-unused-vars
function hotAddUpdateChunk(chunkId, moreModules) {
if (!hotAvailableFilesMap[chunkId] || !hotRequestedFilesMap[chunkId])
return;
hotRequestedFilesMap[chunkId] = false;
for(var moduleId in moreModules) {
if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
for (var moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
hotUpdate[moduleId] = moreModules[moduleId];
}
}
if(--hotWaitingFiles === 0 && hotChunksLoading === 0) {
if (--hotWaitingFiles === 0 && hotChunksLoading === 0) {
hotUpdateDownloaded();
}
}
function hotEnsureUpdateChunk(chunkId) {
if(!hotAvailableFilesMap[chunkId]) {
if (!hotAvailableFilesMap[chunkId]) {
hotWaitingFilesMap[chunkId] = true;
} else {
hotRequestedFilesMap[chunkId] = true;
@@ -226,25 +247,27 @@ module.exports = function() {
hotSetStatus("ready");
var deferred = hotDeferred;
hotDeferred = null;
if(!deferred) return;
if(hotApplyOnUpdate) {
if (!deferred) return;
if (hotApplyOnUpdate) {
// Wrap deferred object in Promise to mark it as a well-handled Promise to
// avoid triggering uncaught exception warning in Chrome.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=465666
Promise.resolve().then(function() {
return hotApply(hotApplyOnUpdate);
}).then(
function(result) {
deferred.resolve(result);
},
function(err) {
deferred.reject(err);
}
);
Promise.resolve()
.then(function() {
return hotApply(hotApplyOnUpdate);
})
.then(
function(result) {
deferred.resolve(result);
},
function(err) {
deferred.reject(err);
}
);
} else {
var outdatedModules = [];
for(var id in hotUpdate) {
if(Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
for (var id in hotUpdate) {
if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
outdatedModules.push(toModuleId(id));
}
}
@@ -253,7 +276,8 @@ module.exports = function() {
}
function hotApply(options) {
if(hotStatus !== "ready") throw new Error("apply() is only allowed in ready status");
if (hotStatus !== "ready")
throw new Error("apply() is only allowed in ready status");
options = options || {};
var cb;
@@ -272,32 +296,31 @@ module.exports = function() {
id: id
};
});
while(queue.length > 0) {
while (queue.length > 0) {
var queueItem = queue.pop();
var moduleId = queueItem.id;
var chain = queueItem.chain;
module = installedModules[moduleId];
if(!module || module.hot._selfAccepted)
continue;
if(module.hot._selfDeclined) {
if (!module || module.hot._selfAccepted) continue;
if (module.hot._selfDeclined) {
return {
type: "self-declined",
chain: chain,
moduleId: moduleId
};
}
if(module.hot._main) {
if (module.hot._main) {
return {
type: "unaccepted",
chain: chain,
moduleId: moduleId
};
}
for(var i = 0; i < module.parents.length; i++) {
for (var i = 0; i < module.parents.length; i++) {
var parentId = module.parents[i];
var parent = installedModules[parentId];
if(!parent) continue;
if(parent.hot._declinedDependencies[moduleId]) {
if (!parent) continue;
if (parent.hot._declinedDependencies[moduleId]) {
return {
type: "declined",
chain: chain.concat([parentId]),
@@ -305,9 +328,9 @@ module.exports = function() {
parentId: parentId
};
}
if(outdatedModules.indexOf(parentId) >= 0) continue;
if(parent.hot._acceptedDependencies[moduleId]) {
if(!outdatedDependencies[parentId])
if (outdatedModules.indexOf(parentId) !== -1) continue;
if (parent.hot._acceptedDependencies[moduleId]) {
if (!outdatedDependencies[parentId])
outdatedDependencies[parentId] = [];
addAllToSet(outdatedDependencies[parentId], [moduleId]);
continue;
@@ -330,10 +353,9 @@ module.exports = function() {
}
function addAllToSet(a, b) {
for(var i = 0; i < b.length; i++) {
for (var i = 0; i < b.length; i++) {
var item = b[i];
if(a.indexOf(item) < 0)
a.push(item);
if (a.indexOf(item) === -1) a.push(item);
}
}
@@ -344,14 +366,17 @@ module.exports = function() {
var appliedUpdate = {};
var warnUnexpectedRequire = function warnUnexpectedRequire() {
console.warn("[HMR] unexpected require(" + result.moduleId + ") to disposed module");
console.warn(
"[HMR] unexpected require(" + result.moduleId + ") to disposed module"
);
};
for(var id in hotUpdate) {
if(Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
for (var id in hotUpdate) {
if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
moduleId = toModuleId(id);
/** @type {TODO} */
var result;
if(hotUpdate[id]) {
if (hotUpdate[id]) {
result = getAffectedStuff(moduleId);
} else {
result = {
@@ -359,61 +384,77 @@ module.exports = function() {
moduleId: id
};
}
/** @type {Error|false} */
var abortError = false;
var doApply = false;
var doDispose = false;
var chainInfo = "";
if(result.chain) {
if (result.chain) {
chainInfo = "\nUpdate propagation: " + result.chain.join(" -> ");
}
switch(result.type) {
switch (result.type) {
case "self-declined":
if(options.onDeclined)
options.onDeclined(result);
if(!options.ignoreDeclined)
abortError = new Error("Aborted because of self decline: " + result.moduleId + chainInfo);
if (options.onDeclined) options.onDeclined(result);
if (!options.ignoreDeclined)
abortError = new Error(
"Aborted because of self decline: " +
result.moduleId +
chainInfo
);
break;
case "declined":
if(options.onDeclined)
options.onDeclined(result);
if(!options.ignoreDeclined)
abortError = new Error("Aborted because of declined dependency: " + result.moduleId + " in " + result.parentId + chainInfo);
if (options.onDeclined) options.onDeclined(result);
if (!options.ignoreDeclined)
abortError = new Error(
"Aborted because of declined dependency: " +
result.moduleId +
" in " +
result.parentId +
chainInfo
);
break;
case "unaccepted":
if(options.onUnaccepted)
options.onUnaccepted(result);
if(!options.ignoreUnaccepted)
abortError = new Error("Aborted because " + moduleId + " is not accepted" + chainInfo);
if (options.onUnaccepted) options.onUnaccepted(result);
if (!options.ignoreUnaccepted)
abortError = new Error(
"Aborted because " + moduleId + " is not accepted" + chainInfo
);
break;
case "accepted":
if(options.onAccepted)
options.onAccepted(result);
if (options.onAccepted) options.onAccepted(result);
doApply = true;
break;
case "disposed":
if(options.onDisposed)
options.onDisposed(result);
if (options.onDisposed) options.onDisposed(result);
doDispose = true;
break;
default:
throw new Error("Unexception type " + result.type);
}
if(abortError) {
if (abortError) {
hotSetStatus("abort");
return Promise.reject(abortError);
}
if(doApply) {
if (doApply) {
appliedUpdate[moduleId] = hotUpdate[moduleId];
addAllToSet(outdatedModules, result.outdatedModules);
for(moduleId in result.outdatedDependencies) {
if(Object.prototype.hasOwnProperty.call(result.outdatedDependencies, moduleId)) {
if(!outdatedDependencies[moduleId])
for (moduleId in result.outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(
result.outdatedDependencies,
moduleId
)
) {
if (!outdatedDependencies[moduleId])
outdatedDependencies[moduleId] = [];
addAllToSet(outdatedDependencies[moduleId], result.outdatedDependencies[moduleId]);
addAllToSet(
outdatedDependencies[moduleId],
result.outdatedDependencies[moduleId]
);
}
}
}
if(doDispose) {
if (doDispose) {
addAllToSet(outdatedModules, [result.moduleId]);
appliedUpdate[moduleId] = warnUnexpectedRequire;
}
@@ -422,9 +463,12 @@ module.exports = function() {
// Store self accepted outdated modules to require them later by the module system
var outdatedSelfAcceptedModules = [];
for(i = 0; i < outdatedModules.length; i++) {
for (i = 0; i < outdatedModules.length; i++) {
moduleId = outdatedModules[i];
if(installedModules[moduleId] && installedModules[moduleId].hot._selfAccepted)
if (
installedModules[moduleId] &&
installedModules[moduleId].hot._selfAccepted
)
outdatedSelfAcceptedModules.push({
module: moduleId,
errorHandler: installedModules[moduleId].hot._selfAccepted
@@ -434,23 +478,23 @@ module.exports = function() {
// Now in "dispose" phase
hotSetStatus("dispose");
Object.keys(hotAvailableFilesMap).forEach(function(chunkId) {
if(hotAvailableFilesMap[chunkId] === false) {
if (hotAvailableFilesMap[chunkId] === false) {
hotDisposeChunk(chunkId);
}
});
var idx;
var queue = outdatedModules.slice();
while(queue.length > 0) {
while (queue.length > 0) {
moduleId = queue.pop();
module = installedModules[moduleId];
if(!module) continue;
if (!module) continue;
var data = {};
// Call dispose handlers
var disposeHandlers = module.hot._disposeHandlers;
for(j = 0; j < disposeHandlers.length; j++) {
for (j = 0; j < disposeHandlers.length; j++) {
cb = disposeHandlers[j];
cb(data);
}
@@ -466,11 +510,11 @@ module.exports = function() {
delete outdatedDependencies[moduleId];
// remove "parents" references from all children
for(j = 0; j < module.children.length; j++) {
for (j = 0; j < module.children.length; j++) {
var child = installedModules[module.children[j]];
if(!child) continue;
if (!child) continue;
idx = child.parents.indexOf(moduleId);
if(idx >= 0) {
if (idx >= 0) {
child.parents.splice(idx, 1);
}
}
@@ -479,15 +523,17 @@ module.exports = function() {
// remove outdated dependency from module children
var dependency;
var moduleOutdatedDependencies;
for(moduleId in outdatedDependencies) {
if(Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)) {
for (moduleId in outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
) {
module = installedModules[moduleId];
if(module) {
if (module) {
moduleOutdatedDependencies = outdatedDependencies[moduleId];
for(j = 0; j < moduleOutdatedDependencies.length; j++) {
for (j = 0; j < moduleOutdatedDependencies.length; j++) {
dependency = moduleOutdatedDependencies[j];
idx = module.children.indexOf(dependency);
if(idx >= 0) module.children.splice(idx, 1);
if (idx >= 0) module.children.splice(idx, 1);
}
}
}
@@ -499,34 +545,36 @@ module.exports = function() {
hotCurrentHash = hotUpdateNewHash;
// insert new code
for(moduleId in appliedUpdate) {
if(Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
for (moduleId in appliedUpdate) {
if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
modules[moduleId] = appliedUpdate[moduleId];
}
}
// call accept handlers
var error = null;
for(moduleId in outdatedDependencies) {
if(Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)) {
for (moduleId in outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
) {
module = installedModules[moduleId];
if(module) {
if (module) {
moduleOutdatedDependencies = outdatedDependencies[moduleId];
var callbacks = [];
for(i = 0; i < moduleOutdatedDependencies.length; i++) {
for (i = 0; i < moduleOutdatedDependencies.length; i++) {
dependency = moduleOutdatedDependencies[i];
cb = module.hot._acceptedDependencies[dependency];
if(cb) {
if(callbacks.indexOf(cb) >= 0) continue;
if (cb) {
if (callbacks.indexOf(cb) !== -1) continue;
callbacks.push(cb);
}
}
for(i = 0; i < callbacks.length; i++) {
for (i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
try {
cb(moduleOutdatedDependencies);
} catch(err) {
if(options.onErrored) {
} catch (err) {
if (options.onErrored) {
options.onErrored({
type: "accept-errored",
moduleId: moduleId,
@@ -534,9 +582,8 @@ module.exports = function() {
error: err
});
}
if(!options.ignoreErrored) {
if(!error)
error = err;
if (!options.ignoreErrored) {
if (!error) error = err;
}
}
}
@@ -545,51 +592,47 @@ module.exports = function() {
}
// Load self accepted modules
for(i = 0; i < outdatedSelfAcceptedModules.length; i++) {
for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
var item = outdatedSelfAcceptedModules[i];
moduleId = item.module;
hotCurrentParents = [moduleId];
try {
$require$(moduleId);
} catch(err) {
if(typeof item.errorHandler === "function") {
} catch (err) {
if (typeof item.errorHandler === "function") {
try {
item.errorHandler(err);
} catch(err2) {
if(options.onErrored) {
} catch (err2) {
if (options.onErrored) {
options.onErrored({
type: "self-accept-error-handler-errored",
moduleId: moduleId,
error: err2,
orginalError: err, // TODO remove in webpack 4
originalError: err
});
}
if(!options.ignoreErrored) {
if(!error)
error = err2;
if (!options.ignoreErrored) {
if (!error) error = err2;
}
if(!error)
error = err;
if (!error) error = err;
}
} else {
if(options.onErrored) {
if (options.onErrored) {
options.onErrored({
type: "self-accept-errored",
moduleId: moduleId,
error: err
});
}
if(!options.ignoreErrored) {
if(!error)
error = err;
if (!options.ignoreErrored) {
if (!error) error = err;
}
}
}
}
// handle errors in accept handlers and self accepted module load
if(error) {
if (error) {
hotSetStatus("fail");
return Promise.reject(error);
}

View File

@@ -3,10 +3,12 @@
Author Tobias Koppers @sokra
*/
"use strict";
const { SyncBailHook } = require("tapable");
const { RawSource } = require("webpack-sources");
const Template = require("./Template");
const ModuleHotAcceptDependency = require("./dependencies/ModuleHotAcceptDependency");
const ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDependency");
const RawSource = require("webpack-sources").RawSource;
const ConstDependency = require("./dependencies/ConstDependency");
const NullFactory = require("./NullFactory");
const ParserHelpers = require("./ParserHelpers");
@@ -23,232 +25,383 @@ module.exports = class HotModuleReplacementPlugin {
const multiStep = this.multiStep;
const fullBuildTimeout = this.fullBuildTimeout;
const requestTimeout = this.requestTimeout;
const hotUpdateChunkFilename = compiler.options.output.hotUpdateChunkFilename;
const hotUpdateChunkFilename =
compiler.options.output.hotUpdateChunkFilename;
const hotUpdateMainFilename = compiler.options.output.hotUpdateMainFilename;
compiler.plugin("additional-pass", callback => {
if(multiStep)
return setTimeout(callback, fullBuildTimeout);
return callback();
});
compiler.plugin("compilation", (compilation, params) => {
const hotUpdateChunkTemplate = compilation.hotUpdateChunkTemplate;
if(!hotUpdateChunkTemplate) return;
compiler.hooks.additionalPass.tapAsync(
"HotModuleReplacementPlugin",
callback => {
if (multiStep) return setTimeout(callback, fullBuildTimeout);
return callback();
}
);
const normalModuleFactory = params.normalModuleFactory;
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
compilation.dependencyFactories.set(ModuleHotAcceptDependency, normalModuleFactory);
compilation.dependencyTemplates.set(ModuleHotAcceptDependency, new ModuleHotAcceptDependency.Template());
compilation.dependencyFactories.set(ModuleHotDeclineDependency, normalModuleFactory);
compilation.dependencyTemplates.set(ModuleHotDeclineDependency, new ModuleHotDeclineDependency.Template());
compilation.plugin("record", function(compilation, records) {
if(records.hash === this.hash) return;
records.hash = compilation.hash;
records.moduleHashs = {};
this.modules.forEach(module => {
const identifier = module.identifier();
const hash = require("crypto").createHash("md5");
module.updateHash(hash);
records.moduleHashs[identifier] = hash.digest("hex");
});
records.chunkHashs = {};
this.chunks.forEach(chunk => {
records.chunkHashs[chunk.id] = chunk.hash;
});
records.chunkModuleIds = {};
this.chunks.forEach(chunk => {
records.chunkModuleIds[chunk.id] = chunk.mapModules(m => m.id);
});
});
let initialPass = false;
let recompilation = false;
compilation.plugin("after-hash", function() {
let records = this.records;
if(!records) {
initialPass = true;
return;
const addParserPlugins = (parser, parserOptions) => {
parser.hooks.expression
.for("__webpack_hash__")
.tap(
"HotModuleReplacementPlugin",
ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
"__webpack_require__.h()"
)
);
parser.hooks.evaluateTypeof
.for("__webpack_hash__")
.tap(
"HotModuleReplacementPlugin",
ParserHelpers.evaluateToString("string")
);
parser.hooks.evaluateIdentifier.for("module.hot").tap(
{
name: "HotModuleReplacementPlugin",
before: "NodeStuffPlugin"
},
expr => {
return ParserHelpers.evaluateToIdentifier(
"module.hot",
!!parser.state.compilation.hotUpdateChunkTemplate
)(expr);
}
if(!records.hash)
initialPass = true;
const preHash = records.preHash || "x";
const prepreHash = records.prepreHash || "x";
if(preHash === this.hash) {
recompilation = true;
this.modifyHash(prepreHash);
return;
}
records.prepreHash = records.hash || "x";
records.preHash = this.hash;
this.modifyHash(records.prepreHash);
});
compilation.plugin("should-generate-chunk-assets", () => {
if(multiStep && !recompilation && !initialPass)
return false;
});
compilation.plugin("need-additional-pass", () => {
if(multiStep && !recompilation && !initialPass)
return true;
});
compilation.plugin("additional-chunk-assets", function() {
const records = this.records;
if(records.hash === this.hash) return;
if(!records.moduleHashs || !records.chunkHashs || !records.chunkModuleIds) return;
this.modules.forEach(module => {
const identifier = module.identifier();
let hash = require("crypto").createHash("md5");
module.updateHash(hash);
hash = hash.digest("hex");
module.hotUpdate = records.moduleHashs[identifier] !== hash;
});
const hotUpdateMainContent = {
h: this.hash,
c: {},
};
Object.keys(records.chunkHashs).forEach(function(chunkId) {
chunkId = isNaN(+chunkId) ? chunkId : +chunkId;
const currentChunk = this.chunks.find(chunk => chunk.id === chunkId);
if(currentChunk) {
const newModules = currentChunk.getModules().filter(module => module.hotUpdate);
const allModules = {};
currentChunk.forEachModule(module => {
allModules[module.id] = true;
});
const removedModules = records.chunkModuleIds[chunkId].filter(id => !allModules[id]);
if(newModules.length > 0 || removedModules.length > 0) {
const source = hotUpdateChunkTemplate.render(chunkId, newModules, removedModules, this.hash, this.moduleTemplate, this.dependencyTemplates);
const filename = this.getPath(hotUpdateChunkFilename, {
hash: records.hash,
chunk: currentChunk
});
this.additionalChunkAssets.push(filename);
this.assets[filename] = source;
hotUpdateMainContent.c[chunkId] = true;
currentChunk.files.push(filename);
this.applyPlugins("chunk-asset", currentChunk, filename);
}
} else {
hotUpdateMainContent.c[chunkId] = false;
);
// 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.call
.for("module.hot.accept")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
}, this);
const source = new RawSource(JSON.stringify(hotUpdateMainContent));
const filename = this.getPath(hotUpdateMainFilename, {
hash: records.hash
});
this.assets[filename] = source;
});
compilation.mainTemplate.plugin("hash", hash => {
hash.update("HotMainTemplateDecorator");
});
compilation.mainTemplate.plugin("module-require", (_, chunk, hash, varModuleId) => {
return `hotCreateRequire(${varModuleId})`;
});
compilation.mainTemplate.plugin("require-extensions", function(source) {
const buf = [source];
buf.push("");
buf.push("// __webpack_hash__");
buf.push(this.requireFn + ".h = function() { return hotCurrentHash; };");
return this.asString(buf);
});
compilation.mainTemplate.plugin("bootstrap", function(source, chunk, hash) {
source = this.applyPluginsWaterfall("hot-bootstrap", source, chunk, hash);
return this.asString([
source,
"",
hotInitCode
.replace(/\$require\$/g, this.requireFn)
.replace(/\$hash\$/g, JSON.stringify(hash))
.replace(/\$requestTimeout\$/g, requestTimeout)
.replace(/\/\*foreachInstalledChunks\*\//g, chunk.chunks.length > 0 ? "for(var chunkId in installedChunks)" : `var chunkId = ${JSON.stringify(chunk.id)};`)
]);
});
compilation.mainTemplate.plugin("global-hash", () => true);
compilation.mainTemplate.plugin("current-hash", (_, length) => {
if(isFinite(length))
return `hotCurrentHash.substr(0, ${length})`;
else
return "hotCurrentHash";
});
compilation.mainTemplate.plugin("module-obj", function(source, chunk, hash, varModuleId) {
return this.asString([
`${source},`,
`hot: hotCreateModule(${varModuleId}),`,
"parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),",
"children: []"
]);
});
params.normalModuleFactory.plugin("parser", (parser, parserOptions) => {
parser.plugin("expression __webpack_hash__", ParserHelpers.toConstantDependency("__webpack_require__.h()"));
parser.plugin("evaluate typeof __webpack_hash__", ParserHelpers.evaluateToString("string"));
parser.plugin("evaluate Identifier module.hot", function(expr) {
return ParserHelpers.evaluateToIdentifier("module.hot", !!this.state.compilation.hotUpdateChunkTemplate)(expr);
});
parser.plugin("call module.hot.accept", function(expr) {
if(!this.state.compilation.hotUpdateChunkTemplate) return false;
if(expr.arguments.length >= 1) {
const arg = this.evaluateExpression(expr.arguments[0]);
if (expr.arguments.length >= 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
let params = [];
let requests = [];
if(arg.isString()) {
if (arg.isString()) {
params = [arg];
} else if(arg.isArray()) {
} else if (arg.isArray()) {
params = arg.items.filter(param => param.isString());
}
if(params.length > 0) {
if (params.length > 0) {
params.forEach((param, idx) => {
const request = param.string;
const dep = new ModuleHotAcceptDependency(request, param.range);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
this.state.module.addDependency(dep);
parser.state.module.addDependency(dep);
requests.push(request);
});
if(expr.arguments.length > 1) {
this.applyPluginsBailResult("hot accept callback", expr.arguments[1], requests);
if (expr.arguments.length > 1) {
parser.hooks.hotAcceptCallback.call(
expr.arguments[1],
requests
);
parser.walkExpression(expr.arguments[1]); // other args are ignored
return true;
} else {
this.applyPluginsBailResult("hot accept without callback", expr, requests);
parser.hooks.hotAcceptWithoutCallback.call(expr, requests);
return true;
}
return true;
}
}
});
parser.plugin("call module.hot.decline", function(expr) {
if(!this.state.compilation.hotUpdateChunkTemplate) return false;
if(expr.arguments.length === 1) {
const arg = this.evaluateExpression(expr.arguments[0]);
parser.hooks.call
.for("module.hot.decline")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
if (expr.arguments.length === 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
let params = [];
if(arg.isString()) {
if (arg.isString()) {
params = [arg];
} else if(arg.isArray()) {
} else if (arg.isArray()) {
params = arg.items.filter(param => param.isString());
}
params.forEach((param, idx) => {
const dep = new ModuleHotDeclineDependency(param.string, param.range);
const dep = new ModuleHotDeclineDependency(
param.string,
param.range
);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
this.state.module.addDependency(dep);
parser.state.module.addDependency(dep);
});
}
});
parser.plugin("expression module.hot", ParserHelpers.skipTraversal);
});
});
}
parser.hooks.expression
.for("module.hot")
.tap("HotModuleReplacementPlugin", ParserHelpers.skipTraversal);
};
compiler.hooks.compilation.tap(
"HotModuleReplacementPlugin",
(compilation, { normalModuleFactory }) => {
const hotUpdateChunkTemplate = compilation.hotUpdateChunkTemplate;
if (!hotUpdateChunkTemplate) return;
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
compilation.dependencyFactories.set(
ModuleHotAcceptDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ModuleHotAcceptDependency,
new ModuleHotAcceptDependency.Template()
);
compilation.dependencyFactories.set(
ModuleHotDeclineDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ModuleHotDeclineDependency,
new ModuleHotDeclineDependency.Template()
);
compilation.hooks.record.tap(
"HotModuleReplacementPlugin",
(compilation, records) => {
if (records.hash === compilation.hash) return;
records.hash = compilation.hash;
records.moduleHashs = {};
for (const module of compilation.modules) {
const identifier = module.identifier();
records.moduleHashs[identifier] = module.hash;
}
records.chunkHashs = {};
for (const chunk of compilation.chunks) {
records.chunkHashs[chunk.id] = chunk.hash;
}
records.chunkModuleIds = {};
for (const chunk of compilation.chunks) {
records.chunkModuleIds[chunk.id] = Array.from(
chunk.modulesIterable,
m => m.id
);
}
}
);
let initialPass = false;
let recompilation = false;
compilation.hooks.afterHash.tap("HotModuleReplacementPlugin", () => {
let records = compilation.records;
if (!records) {
initialPass = true;
return;
}
if (!records.hash) initialPass = true;
const preHash = records.preHash || "x";
const prepreHash = records.prepreHash || "x";
if (preHash === compilation.hash) {
recompilation = true;
compilation.modifyHash(prepreHash);
return;
}
records.prepreHash = records.hash || "x";
records.preHash = compilation.hash;
compilation.modifyHash(records.prepreHash);
});
compilation.hooks.shouldGenerateChunkAssets.tap(
"HotModuleReplacementPlugin",
() => {
if (multiStep && !recompilation && !initialPass) return false;
}
);
compilation.hooks.needAdditionalPass.tap(
"HotModuleReplacementPlugin",
() => {
if (multiStep && !recompilation && !initialPass) return true;
}
);
compilation.hooks.additionalChunkAssets.tap(
"HotModuleReplacementPlugin",
() => {
const records = compilation.records;
if (records.hash === compilation.hash) return;
if (
!records.moduleHashs ||
!records.chunkHashs ||
!records.chunkModuleIds
)
return;
for (const module of compilation.modules) {
const identifier = module.identifier();
let hash = module.hash;
module.hotUpdate = records.moduleHashs[identifier] !== hash;
}
const hotUpdateMainContent = {
h: compilation.hash,
c: {}
};
for (const key of Object.keys(records.chunkHashs)) {
const chunkId = isNaN(+key) ? key : +key;
const currentChunk = compilation.chunks.find(
chunk => `${chunk.id}` === key
);
if (currentChunk) {
const newModules = currentChunk
.getModules()
.filter(module => module.hotUpdate);
const allModules = new Set();
for (const module of currentChunk.modulesIterable) {
allModules.add(module.id);
}
const removedModules = records.chunkModuleIds[chunkId].filter(
id => !allModules.has(id)
);
if (newModules.length > 0 || removedModules.length > 0) {
const source = hotUpdateChunkTemplate.render(
chunkId,
newModules,
removedModules,
compilation.hash,
compilation.moduleTemplates.javascript,
compilation.dependencyTemplates
);
const filename = compilation.getPath(hotUpdateChunkFilename, {
hash: records.hash,
chunk: currentChunk
});
compilation.additionalChunkAssets.push(filename);
compilation.assets[filename] = source;
hotUpdateMainContent.c[chunkId] = true;
currentChunk.files.push(filename);
compilation.hooks.chunkAsset.call(currentChunk, filename);
}
} else {
hotUpdateMainContent.c[chunkId] = false;
}
}
const source = new RawSource(JSON.stringify(hotUpdateMainContent));
const filename = compilation.getPath(hotUpdateMainFilename, {
hash: records.hash
});
compilation.assets[filename] = source;
}
);
const mainTemplate = compilation.mainTemplate;
mainTemplate.hooks.hash.tap("HotModuleReplacementPlugin", hash => {
hash.update("HotMainTemplateDecorator");
});
mainTemplate.hooks.moduleRequire.tap(
"HotModuleReplacementPlugin",
(_, chunk, hash, varModuleId) => {
return `hotCreateRequire(${varModuleId})`;
}
);
mainTemplate.hooks.requireExtensions.tap(
"HotModuleReplacementPlugin",
source => {
const buf = [source];
buf.push("");
buf.push("// __webpack_hash__");
buf.push(
mainTemplate.requireFn +
".h = function() { return hotCurrentHash; };"
);
return Template.asString(buf);
}
);
const needChunkLoadingCode = chunk => {
for (const chunkGroup of chunk.groupsIterable) {
if (chunkGroup.chunks.length > 1) return true;
if (chunkGroup.getNumberOfChildren() > 0) return true;
}
return false;
};
mainTemplate.hooks.bootstrap.tap(
"HotModuleReplacementPlugin",
(source, chunk, hash) => {
source = mainTemplate.hooks.hotBootstrap.call(source, chunk, hash);
return Template.asString([
source,
"",
hotInitCode
.replace(/\$require\$/g, mainTemplate.requireFn)
.replace(/\$hash\$/g, JSON.stringify(hash))
.replace(/\$requestTimeout\$/g, requestTimeout)
.replace(
/\/\*foreachInstalledChunks\*\//g,
needChunkLoadingCode(chunk)
? "for(var chunkId in installedChunks)"
: `var chunkId = ${JSON.stringify(chunk.id)};`
)
]);
}
);
mainTemplate.hooks.globalHash.tap(
"HotModuleReplacementPlugin",
() => true
);
mainTemplate.hooks.currentHash.tap(
"HotModuleReplacementPlugin",
(_, length) => {
if (isFinite(length)) {
return `hotCurrentHash.substr(0, ${length})`;
} else {
return "hotCurrentHash";
}
}
);
mainTemplate.hooks.moduleObj.tap(
"HotModuleReplacementPlugin",
(source, chunk, hash, varModuleId) => {
return Template.asString([
`${source},`,
`hot: hotCreateModule(${varModuleId}),`,
"parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),",
"children: []"
]);
}
);
// TODO add HMR support for javascript/esm
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("HotModuleReplacementPlugin", addParserPlugins);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("HotModuleReplacementPlugin", addParserPlugins);
compilation.hooks.normalModuleLoader.tap(
"HotModuleReplacementPlugin",
context => {
context.hot = true;
}
);
}
);
}
};
const hotInitCode = Template.getFunctionContent(require("./HotModuleReplacement.runtime.js"));
const hotInitCode = Template.getFunctionContent(
require("./HotModuleReplacement.runtime")
);

View File

@@ -5,27 +5,74 @@
"use strict";
const Template = require("./Template");
const Chunk = require("./Chunk");
const HotUpdateChunk = require("./HotUpdateChunk");
const { Tapable, SyncWaterfallHook, SyncHook } = require("tapable");
module.exports = class HotUpdateChunkTemplate extends Template {
module.exports = class HotUpdateChunkTemplate extends Tapable {
constructor(outputOptions) {
super(outputOptions);
super();
this.outputOptions = outputOptions || {};
this.hooks = {
modules: new SyncWaterfallHook([
"source",
"modules",
"removedModules",
"moduleTemplate",
"dependencyTemplates"
]),
render: new SyncWaterfallHook([
"source",
"modules",
"removedModules",
"hash",
"id",
"moduleTemplate",
"dependencyTemplates"
]),
hash: new SyncHook(["hash"])
};
}
render(id, modules, removedModules, hash, moduleTemplate, dependencyTemplates) {
const hotUpdateChunk = new Chunk();
render(
id,
modules,
removedModules,
hash,
moduleTemplate,
dependencyTemplates
) {
const hotUpdateChunk = new HotUpdateChunk();
hotUpdateChunk.id = id;
hotUpdateChunk.setModules(modules);
hotUpdateChunk.removedModules = removedModules;
const modulesSource = this.renderChunkModules(hotUpdateChunk, moduleTemplate, dependencyTemplates);
const core = this.applyPluginsWaterfall("modules", modulesSource, modules, removedModules, moduleTemplate, dependencyTemplates);
const source = this.applyPluginsWaterfall("render", core, modules, removedModules, hash, id, moduleTemplate, dependencyTemplates);
const modulesSource = Template.renderChunkModules(
hotUpdateChunk,
m => typeof m.source === "function",
moduleTemplate,
dependencyTemplates
);
const core = this.hooks.modules.call(
modulesSource,
modules,
removedModules,
moduleTemplate,
dependencyTemplates
);
const source = this.hooks.render.call(
core,
modules,
removedModules,
hash,
id,
moduleTemplate,
dependencyTemplates
);
return source;
}
updateHash(hash) {
hash.update("HotUpdateChunkTemplate");
hash.update("1");
this.applyPlugins("hash", hash);
this.hooks.hash.call(hash);
}
};

View File

@@ -4,64 +4,86 @@
*/
"use strict";
class IgnorePlugin {
constructor(resourceRegExp, contextRegExp) {
this.resourceRegExp = resourceRegExp;
this.contextRegExp = contextRegExp;
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/IgnorePlugin.json");
/** @typedef {import("../declarations/plugins/IgnorePlugin").IgnorePluginOptions} IgnorePluginOptions */
/** @typedef {import("./Compiler")} Compiler */
class IgnorePlugin {
/**
* @param {IgnorePluginOptions} options IgnorePlugin options
*/
constructor(options) {
// TODO webpack 5 remove this compat-layer
if (arguments.length > 1 || options instanceof RegExp) {
options = {
resourceRegExp: arguments[0],
contextRegExp: arguments[1]
};
}
validateOptions(schema, options, "IgnorePlugin");
this.options = options;
/** @private @type {Function} */
this.checkIgnore = this.checkIgnore.bind(this);
}
/*
* Only returns true if a "resourceRegExp" exists
* and the resource given matches the regexp.
*/
checkResource(resource) {
if(!this.resourceRegExp) {
return false;
}
return this.resourceRegExp.test(resource);
}
/*
* Returns true if contextRegExp does not exist
* or if context matches the given regexp.
*/
checkContext(context) {
if(!this.contextRegExp) {
return true;
}
return this.contextRegExp.test(context);
}
/*
* Returns true if result should be ignored.
* false if it shouldn't.
*
* Not that if "contextRegExp" is given, both the "resourceRegExp"
/**
* Note that if "contextRegExp" is given, both the "resourceRegExp"
* and "contextRegExp" have to match.
*
* @param {TODO} result result
* @returns {TODO|null} returns result or null if result should be ignored
*/
checkResult(result) {
if(!result) {
return true;
checkIgnore(result) {
if (!result) return result;
if (
"checkResource" in this.options &&
this.options.checkResource &&
this.options.checkResource(result.request, result.context)
) {
// TODO webpack 5 remove checkContext, as checkResource already gets context
if ("checkContext" in this.options && this.options.checkContext) {
if (this.options.checkContext(result.context)) {
return null;
}
} else {
return null;
}
}
return this.checkResource(result.request) && this.checkContext(result.context);
}
checkIgnore(result, callback) {
// check if result is ignored
if(this.checkResult(result)) {
return callback();
}
return callback(null, result);
if (
"resourceRegExp" in this.options &&
this.options.resourceRegExp &&
this.options.resourceRegExp.test(result.request)
) {
if ("contextRegExp" in this.options && this.options.contextRegExp) {
// if "contextRegExp" is given,
// both the "resourceRegExp" and "contextRegExp" have to match.
if (this.options.contextRegExp.test(result.context)) {
return null;
}
} else {
return null;
}
}
return result;
}
/**
* @param {Compiler} compiler Webpack Compiler
* @returns {void}
*/
apply(compiler) {
compiler.plugin("normal-module-factory", (nmf) => {
nmf.plugin("before-resolve", this.checkIgnore);
compiler.hooks.normalModuleFactory.tap("IgnorePlugin", nmf => {
nmf.hooks.beforeResolve.tap("IgnorePlugin", this.checkIgnore);
});
compiler.plugin("context-module-factory", (cmf) => {
cmf.plugin("before-resolve", this.checkIgnore);
compiler.hooks.contextModuleFactory.tap("IgnorePlugin", cmf => {
cmf.hooks.beforeResolve.tap("IgnorePlugin", this.checkIgnore);
});
}
}

View File

@@ -1,31 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
class JsonpChunkTemplatePlugin {
apply(chunkTemplate) {
chunkTemplate.plugin("render", function(modules, chunk) {
const jsonpFunction = this.outputOptions.jsonpFunction;
const source = new ConcatSource();
source.add(`${jsonpFunction}(${JSON.stringify(chunk.ids)},`);
source.add(modules);
const entries = [chunk.entryModule].filter(Boolean).map(m => m.id);
if(entries.length > 0) {
source.add(`,${JSON.stringify(entries)}`);
}
source.add(")");
return source;
});
chunkTemplate.plugin("hash", function(hash) {
hash.update("JsonpChunkTemplatePlugin");
hash.update("3");
hash.update(`${this.outputOptions.jsonpFunction}`);
hash.update(`${this.outputOptions.library}`);
});
}
}
module.exports = JsonpChunkTemplatePlugin;

View File

@@ -1,37 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
class JsonpExportMainTemplatePlugin {
constructor(name) {
this.name = name;
}
apply(compilation) {
const mainTemplate = compilation.mainTemplate;
compilation.templatesPlugin("render-with-entry", (source, chunk, hash) => {
const name = mainTemplate.applyPluginsWaterfall("asset-path", this.name || "", {
hash: hash,
chunk: chunk
});
return new ConcatSource(`${name}(`, source, ");");
});
mainTemplate.plugin("global-hash-paths", paths => {
if(this.name) paths.push(this.name);
return paths;
});
mainTemplate.plugin("hash", hash => {
hash.update("jsonp export");
hash.update(`${this.name}`);
});
}
}
module.exports = JsonpExportMainTemplatePlugin;

View File

@@ -1,27 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
class JsonpHotUpdateChunkTemplatePlugin {
apply(hotUpdateChunkTemplate) {
hotUpdateChunkTemplate.plugin("render", function(modulesSource, modules, removedModules, hash, id) {
const source = new ConcatSource();
source.add(`${this.outputOptions.hotUpdateFunction}(${JSON.stringify(id)},`);
source.add(modulesSource);
source.add(")");
return source;
});
hotUpdateChunkTemplate.plugin("hash", function(hash) {
hash.update("JsonpHotUpdateChunkTemplatePlugin");
hash.update("3");
hash.update(`${this.outputOptions.hotUpdateFunction}`);
hash.update(`${this.outputOptions.library}`);
});
}
}
module.exports = JsonpHotUpdateChunkTemplatePlugin;

View File

@@ -1,60 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
/*globals hotAddUpdateChunk parentHotUpdateCallback document XMLHttpRequest $require$ $hotChunkFilename$ $hotMainFilename$ $crossOriginLoading$ */
module.exports = function() {
function webpackHotUpdateCallback(chunkId, moreModules) { // eslint-disable-line no-unused-vars
hotAddUpdateChunk(chunkId, moreModules);
if(parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);
} //$semicolon
function hotDownloadUpdateChunk(chunkId) { // eslint-disable-line no-unused-vars
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.charset = "utf-8";
script.src = $require$.p + $hotChunkFilename$;
$crossOriginLoading$;
head.appendChild(script);
}
function hotDownloadManifest(requestTimeout) { // eslint-disable-line no-unused-vars
requestTimeout = requestTimeout || 10000;
return new Promise(function(resolve, reject) {
if(typeof XMLHttpRequest === "undefined")
return reject(new Error("No browser support"));
try {
var request = new XMLHttpRequest();
var requestPath = $require$.p + $hotMainFilename$;
request.open("GET", requestPath, true);
request.timeout = requestTimeout;
request.send(null);
} catch(err) {
return reject(err);
}
request.onreadystatechange = function() {
if(request.readyState !== 4) return;
if(request.status === 0) {
// timeout
reject(new Error("Manifest request to " + requestPath + " timed out."));
} else if(request.status === 404) {
// no update available
resolve();
} else if(request.status !== 200 && request.status !== 304) {
// other failure
reject(new Error("Manifest request to " + requestPath + " failed."));
} else {
// success
try {
var update = JSON.parse(request.responseText);
} catch(e) {
reject(e);
return;
}
resolve(update);
}
};
});
}
};

View File

@@ -1,212 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const Template = require("./Template");
class JsonpMainTemplatePlugin {
apply(mainTemplate) {
mainTemplate.plugin("local-vars", function(source, chunk) {
if(chunk.chunks.length > 0) {
return this.asString([
source,
"",
"// objects to store loaded and loading chunks",
"var installedChunks = {",
this.indent(
chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n")
),
"};"
]);
}
return source;
});
mainTemplate.plugin("jsonp-script", function(_, chunk, hash) {
const chunkFilename = this.outputOptions.chunkFilename;
const chunkMaps = chunk.getChunkMaps();
const crossOriginLoading = this.outputOptions.crossOriginLoading;
const chunkLoadTimeout = this.outputOptions.chunkLoadTimeout;
const jsonpScriptType = this.outputOptions.jsonpScriptType;
const scriptSrcPath = this.applyPluginsWaterfall("asset-path", JSON.stringify(chunkFilename), {
hash: `" + ${this.renderCurrentHashCode(hash)} + "`,
hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: "\" + chunkId + \"",
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength(length) {
const shortChunkHashMap = Object.create(null);
Object.keys(chunkMaps.hash).forEach(chunkId => {
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 this.asString([
"var script = document.createElement('script');",
`script.type = ${JSON.stringify(jsonpScriptType)};`,
"script.charset = 'utf-8';",
"script.async = true;",
`script.timeout = ${chunkLoadTimeout};`,
crossOriginLoading ? `script.crossOrigin = ${JSON.stringify(crossOriginLoading)};` : "",
`if (${this.requireFn}.nc) {`,
this.indent(`script.setAttribute("nonce", ${this.requireFn}.nc);`),
"}",
`script.src = ${this.requireFn}.p + ${scriptSrcPath};`,
`var timeout = setTimeout(onScriptComplete, ${chunkLoadTimeout});`,
"script.onerror = script.onload = onScriptComplete;",
"function onScriptComplete() {",
this.indent([
"// avoid mem leaks in IE.",
"script.onerror = script.onload = null;",
"clearTimeout(timeout);",
"var chunk = installedChunks[chunkId];",
"if(chunk !== 0) {",
this.indent([
"if(chunk) {",
this.indent("chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));"),
"}",
"installedChunks[chunkId] = undefined;"
]),
"}"
]),
"};",
]);
});
mainTemplate.plugin("require-ensure", function(_, chunk, hash) {
return this.asString([
"var installedChunkData = installedChunks[chunkId];",
"if(installedChunkData === 0) {",
this.indent([
"return new Promise(function(resolve) { resolve(); });"
]),
"}",
"",
"// a Promise means \"currently loading\".",
"if(installedChunkData) {",
this.indent([
"return installedChunkData[2];"
]),
"}",
"",
"// setup Promise in chunk cache",
"var promise = new Promise(function(resolve, reject) {",
this.indent([
"installedChunkData = installedChunks[chunkId] = [resolve, reject];"
]),
"});",
"installedChunkData[2] = promise;",
"",
"// start chunk loading",
"var head = document.getElementsByTagName('head')[0];",
this.applyPluginsWaterfall("jsonp-script", "", chunk, hash),
"head.appendChild(script);",
"",
"return promise;"
]);
});
mainTemplate.plugin("require-extensions", function(source, chunk) {
if(chunk.chunks.length === 0) return source;
return this.asString([
source,
"",
"// on error function for async loading",
`${this.requireFn}.oe = function(err) { console.error(err); throw err; };`
]);
});
mainTemplate.plugin("bootstrap", function(source, chunk, hash) {
if(chunk.chunks.length > 0) {
var jsonpFunction = this.outputOptions.jsonpFunction;
return this.asString([
source,
"",
"// install a JSONP callback for chunk loading",
`var parentJsonpFunction = window[${JSON.stringify(jsonpFunction)}];`,
`window[${JSON.stringify(jsonpFunction)}] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {`,
this.indent([
"// add \"moreModules\" to the modules object,",
"// then flag all \"chunkIds\" as loaded and fire callback",
"var moduleId, chunkId, i = 0, resolves = [], result;",
"for(;i < chunkIds.length; i++) {",
this.indent([
"chunkId = chunkIds[i];",
"if(installedChunks[chunkId]) {",
this.indent("resolves.push(installedChunks[chunkId][0]);"),
"}",
"installedChunks[chunkId] = 0;"
]),
"}",
"for(moduleId in moreModules) {",
this.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")),
"}"
]),
"}",
"if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);",
"while(resolves.length) {",
this.indent("resolves.shift()();"),
"}",
this.entryPointInChildren(chunk) ? [
"if(executeModules) {",
this.indent([
"for(i=0; i < executeModules.length; i++) {",
this.indent(`result = ${this.requireFn}(${this.requireFn}.s = executeModules[i]);`),
"}"
]),
"}",
"return result;",
] : ""
]),
"};"
]);
}
return source;
});
mainTemplate.plugin("hot-bootstrap", function(source, chunk, hash) {
const hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename;
const crossOriginLoading = this.outputOptions.crossOriginLoading;
const hotUpdateFunction = this.outputOptions.hotUpdateFunction;
const currentHotUpdateChunkFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateChunkFilename), {
hash: `" + ${this.renderCurrentHashCode(hash)} + "`,
hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`,
chunk: {
id: "\" + chunkId + \""
}
});
const currentHotUpdateMainFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateMainFilename), {
hash: `" + ${this.renderCurrentHashCode(hash)} + "`,
hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`
});
const runtimeSource = Template.getFunctionContent(require("./JsonpMainTemplate.runtime.js"))
.replace(/\/\/\$semicolon/g, ";")
.replace(/\$require\$/g, this.requireFn)
.replace(/\$crossOriginLoading\$/g, crossOriginLoading ? `script.crossOrigin = ${JSON.stringify(crossOriginLoading)}` : "")
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename)
.replace(/\$hash\$/g, JSON.stringify(hash));
return `${source}
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
var parentHotUpdateCallback = window[${JSON.stringify(hotUpdateFunction)}];
window[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`;
});
mainTemplate.plugin("hash", function(hash) {
hash.update("jsonp");
hash.update("4");
hash.update(`${this.outputOptions.filename}`);
hash.update(`${this.outputOptions.chunkFilename}`);
hash.update(`${this.outputOptions.jsonpFunction}`);
hash.update(`${this.outputOptions.hotUpdateFunction}`);
});
}
}
module.exports = JsonpMainTemplatePlugin;

View File

@@ -1,21 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const JsonpMainTemplatePlugin = require("./JsonpMainTemplatePlugin");
const JsonpChunkTemplatePlugin = require("./JsonpChunkTemplatePlugin");
const JsonpHotUpdateChunkTemplatePlugin = require("./JsonpHotUpdateChunkTemplatePlugin");
class JsonpTemplatePlugin {
apply(compiler) {
compiler.plugin("this-compilation", (compilation) => {
compilation.mainTemplate.apply(new JsonpMainTemplatePlugin());
compilation.chunkTemplate.apply(new JsonpChunkTemplatePlugin());
compilation.hotUpdateChunkTemplate.apply(new JsonpHotUpdateChunkTemplatePlugin());
});
}
}
module.exports = JsonpTemplatePlugin;

View File

@@ -5,7 +5,8 @@
"use strict";
const path = require("path");
const asyncLib = require("async");
const asyncLib = require("neo-async");
const SingleEntryDependency = require("./dependencies/SingleEntryDependency");
class LibManifestPlugin {
constructor(options) {
@@ -13,51 +14,77 @@ class LibManifestPlugin {
}
apply(compiler) {
compiler.plugin("emit", (compilation, callback) => {
asyncLib.forEach(compilation.chunks, (chunk, callback) => {
if(!chunk.isInitial()) {
callback();
return;
}
const targetPath = compilation.getPath(this.options.path, {
hash: compilation.hash,
chunk
});
const name = this.options.name && compilation.getPath(this.options.name, {
hash: compilation.hash,
chunk
});
const manifest = {
name,
type: this.options.type,
content: chunk.mapModules(module => {
if(module.libIdent) {
const ident = module.libIdent({
context: this.options.context || compiler.options.context
});
if(ident) {
return {
ident,
data: {
id: module.id,
meta: module.meta,
exports: Array.isArray(module.providedExports) ? module.providedExports : undefined
}
};
}
compiler.hooks.emit.tapAsync(
"LibManifestPlugin",
(compilation, callback) => {
asyncLib.forEach(
compilation.chunks,
(chunk, callback) => {
if (!chunk.isOnlyInitial()) {
callback();
return;
}
}).filter(Boolean).reduce((obj, item) => {
obj[item.ident] = item.data;
return obj;
}, Object.create(null))
};
const content = new Buffer(JSON.stringify(manifest), "utf8"); //eslint-disable-line
compiler.outputFileSystem.mkdirp(path.dirname(targetPath), err => {
if(err) return callback(err);
compiler.outputFileSystem.writeFile(targetPath, content, callback);
});
}, callback);
});
const targetPath = compilation.getPath(this.options.path, {
hash: compilation.hash,
chunk
});
const name =
this.options.name &&
compilation.getPath(this.options.name, {
hash: compilation.hash,
chunk
});
const manifest = {
name,
type: this.options.type,
content: Array.from(chunk.modulesIterable, module => {
if (
this.options.entryOnly &&
!module.reasons.some(
r => r.dependency instanceof SingleEntryDependency
)
) {
return;
}
if (module.libIdent) {
const ident = module.libIdent({
context: this.options.context || compiler.options.context
});
if (ident) {
return {
ident,
data: {
id: module.id,
buildMeta: module.buildMeta
}
};
}
}
})
.filter(Boolean)
.reduce((obj, item) => {
obj[item.ident] = item.data;
return obj;
}, Object.create(null))
};
// Apply formatting to content if format flag is true;
const manifestContent = this.options.format
? JSON.stringify(manifest, null, 2)
: JSON.stringify(manifest);
const content = Buffer.from(manifestContent, "utf8");
compiler.outputFileSystem.mkdirp(path.dirname(targetPath), err => {
if (err) return callback(err);
compiler.outputFileSystem.writeFile(
targetPath,
content,
callback
);
});
},
callback
);
}
);
}
}
module.exports = LibManifestPlugin;

View File

@@ -6,26 +6,54 @@
const SetVarMainTemplatePlugin = require("./SetVarMainTemplatePlugin");
function accessorToObjectAccess(accessor) {
return accessor.map((a) => {
return `[${JSON.stringify(a)}]`;
}).join("");
}
/** @typedef {import("../declarations/WebpackOptions").LibraryCustomUmdObject} LibraryCustomUmdObject */
/** @typedef {import("./Compiler")} Compiler */
function accessorAccess(base, accessor, joinWith) {
accessor = [].concat(accessor);
return accessor.map((a, idx) => {
a = base ?
base + accessorToObjectAccess(accessor.slice(0, idx + 1)) :
accessor[0] + accessorToObjectAccess(accessor.slice(1, idx + 1));
if(idx === accessor.length - 1) return a;
if(idx === 0 && typeof base === "undefined") return `${a} = typeof ${a} === "object" ? ${a} : {}`;
return `${a} = ${a} || {}`;
}).join(joinWith || "; ");
}
/**
* @param {string[]} accessor the accessor to convert to path
* @returns {string} the path
*/
const accessorToObjectAccess = accessor => {
return accessor.map(a => `[${JSON.stringify(a)}]`).join("");
};
/**
* @param {string=} base the path prefix
* @param {string|string[]|LibraryCustomUmdObject} accessor the accessor
* @param {"amd" | "commonjs" | "root"} umdProperty property used when a custom umd object is provided
* @param {string=} joinWith the element separator
* @returns {string} the path
*/
const accessorAccess = (base, accessor, umdProperty, joinWith = "; ") => {
const normalizedAccessor =
typeof accessor === "object" && !Array.isArray(accessor)
? accessor[umdProperty]
: accessor;
const accessors = Array.isArray(normalizedAccessor)
? normalizedAccessor
: [normalizedAccessor];
return accessors
.map((_, idx) => {
const a = base
? base + accessorToObjectAccess(accessors.slice(0, idx + 1))
: accessors[0] + accessorToObjectAccess(accessors.slice(1, idx + 1));
if (idx === accessors.length - 1) return a;
if (idx === 0 && base === undefined) {
return `${a} = typeof ${a} === "object" ? ${a} : {}`;
}
return `${a} = ${a} || {}`;
})
.join(joinWith);
};
class LibraryTemplatePlugin {
/**
* @param {string|string[]|LibraryCustomUmdObject} name name of library
* @param {string} target type of library
* @param {boolean} umdNamedDefine setting this to true will name the UMD module
* @param {string|TODO} auxiliaryComment comment in the UMD wrapper
* @param {string|string[]} exportProperty which export should be exposed as library
*/
constructor(name, target, umdNamedDefine, auxiliaryComment, exportProperty) {
this.name = name;
this.target = target;
@@ -34,54 +62,113 @@ class LibraryTemplatePlugin {
this.exportProperty = exportProperty;
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.plugin("this-compilation", (compilation) => {
if(this.exportProperty) {
var ExportPropertyMainTemplatePlugin = require("./ExportPropertyMainTemplatePlugin");
compilation.apply(new ExportPropertyMainTemplatePlugin(this.exportProperty));
compiler.hooks.thisCompilation.tap("LibraryTemplatePlugin", compilation => {
if (this.exportProperty) {
const ExportPropertyMainTemplatePlugin = require("./ExportPropertyMainTemplatePlugin");
new ExportPropertyMainTemplatePlugin(this.exportProperty).apply(
compilation
);
}
switch(this.target) {
switch (this.target) {
case "var":
compilation.apply(new SetVarMainTemplatePlugin(`var ${accessorAccess(false, this.name)}`));
if (
!this.name ||
(typeof this.name === "object" && !Array.isArray(this.name))
) {
throw new Error(
"library name must be set and not an UMD custom object for non-UMD target"
);
}
new SetVarMainTemplatePlugin(
`var ${accessorAccess(undefined, this.name, "root")}`,
false
).apply(compilation);
break;
case "assign":
compilation.apply(new SetVarMainTemplatePlugin(accessorAccess(undefined, this.name)));
new SetVarMainTemplatePlugin(
accessorAccess(undefined, this.name, "root"),
false
).apply(compilation);
break;
case "this":
case "self":
case "window":
if (this.name) {
new SetVarMainTemplatePlugin(
accessorAccess(this.target, this.name, "root"),
false
).apply(compilation);
} else {
new SetVarMainTemplatePlugin(this.target, true).apply(compilation);
}
break;
case "global":
if(this.name)
compilation.apply(new SetVarMainTemplatePlugin(accessorAccess(this.target, this.name)));
else
compilation.apply(new SetVarMainTemplatePlugin(this.target, true));
if (this.name) {
new SetVarMainTemplatePlugin(
accessorAccess(
compilation.runtimeTemplate.outputOptions.globalObject,
this.name,
"root"
),
false
).apply(compilation);
} else {
new SetVarMainTemplatePlugin(
compilation.runtimeTemplate.outputOptions.globalObject,
true
).apply(compilation);
}
break;
case "commonjs":
if(this.name)
compilation.apply(new SetVarMainTemplatePlugin(accessorAccess("exports", this.name)));
else
compilation.apply(new SetVarMainTemplatePlugin("exports", true));
if (this.name) {
new SetVarMainTemplatePlugin(
accessorAccess("exports", this.name, "commonjs"),
false
).apply(compilation);
} else {
new SetVarMainTemplatePlugin("exports", true).apply(compilation);
}
break;
case "commonjs2":
case "commonjs-module":
compilation.apply(new SetVarMainTemplatePlugin("module.exports"));
new SetVarMainTemplatePlugin("module.exports", false).apply(
compilation
);
break;
case "amd":
var AmdMainTemplatePlugin = require("./AmdMainTemplatePlugin");
compilation.apply(new AmdMainTemplatePlugin(this.name));
case "amd-require": {
const AmdMainTemplatePlugin = require("./AmdMainTemplatePlugin");
if (this.name && typeof this.name !== "string") {
throw new Error("library name must be a string for amd target");
}
new AmdMainTemplatePlugin({
name: this.name,
requireAsWrapper: this.target === "amd-require"
}).apply(compilation);
break;
}
case "umd":
case "umd2":
var UmdMainTemplatePlugin = require("./UmdMainTemplatePlugin");
compilation.apply(new UmdMainTemplatePlugin(this.name, {
case "umd2": {
const UmdMainTemplatePlugin = require("./UmdMainTemplatePlugin");
new UmdMainTemplatePlugin(this.name, {
optionalAmdExternalAsGlobal: this.target === "umd2",
namedDefine: this.umdNamedDefine,
auxiliaryComment: this.auxiliaryComment
}));
}).apply(compilation);
break;
case "jsonp":
var JsonpExportMainTemplatePlugin = require("./JsonpExportMainTemplatePlugin");
compilation.apply(new JsonpExportMainTemplatePlugin(this.name));
}
case "jsonp": {
const JsonpExportMainTemplatePlugin = require("./web/JsonpExportMainTemplatePlugin");
if (typeof this.name !== "string")
throw new Error("library name must be a string for jsonp target");
new JsonpExportMainTemplatePlugin(this.name).apply(compilation);
break;
}
default:
throw new Error(`${this.target} is not a valid Library target`);
}

View File

@@ -6,29 +6,51 @@
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/LoaderOptionsPlugin.json");
/** @typedef {import("../declarations/plugins/LoaderOptionsPlugin").LoaderOptionsPluginOptions} LoaderOptionsPluginOptions */
class LoaderOptionsPlugin {
/**
* @param {LoaderOptionsPluginOptions} options options object
*/
constructor(options) {
if(typeof options !== "object") options = {};
if(!options.test) options.test = {
test: () => true
};
validateOptions(schema, options || {}, "Loader Options Plugin");
if (typeof options !== "object") options = {};
if (!options.test) {
options.test = {
test: () => true
};
}
this.options = options;
}
apply(compiler) {
const options = this.options;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("normal-module-loader", (context, module) => {
const resource = module.resource;
if(!resource) return;
const i = resource.indexOf("?");
if(ModuleFilenameHelpers.matchObject(options, i < 0 ? resource : resource.substr(0, i))) {
const filterSet = new Set(["include", "exclude", "test"]);
Object.keys(options)
.filter((key) => !filterSet.has(key))
.forEach((key) => context[key] = options[key]);
compiler.hooks.compilation.tap("LoaderOptionsPlugin", compilation => {
compilation.hooks.normalModuleLoader.tap(
"LoaderOptionsPlugin",
(context, module) => {
const resource = module.resource;
if (!resource) return;
const i = resource.indexOf("?");
if (
ModuleFilenameHelpers.matchObject(
options,
i < 0 ? resource : resource.substr(0, i)
)
) {
for (const key of Object.keys(options)) {
if (key === "include" || key === "exclude" || key === "test") {
continue;
}
context[key] = options[key];
}
}
}
});
);
});
}
}

View File

@@ -10,8 +10,13 @@ class LoaderTargetPlugin {
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("normal-module-loader", (loaderContext) => loaderContext.target = this.target);
compiler.hooks.compilation.tap("LoaderTargetPlugin", compilation => {
compilation.hooks.normalModuleLoader.tap(
"LoaderTargetPlugin",
loaderContext => {
loaderContext.target = this.target;
}
);
});
}
}

View File

@@ -4,11 +4,38 @@
*/
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const OriginalSource = require("webpack-sources").OriginalSource;
const PrefixSource = require("webpack-sources").PrefixSource;
const {
ConcatSource,
OriginalSource,
PrefixSource,
RawSource
} = require("webpack-sources");
const {
Tapable,
SyncWaterfallHook,
SyncHook,
SyncBailHook
} = require("tapable");
const Template = require("./Template");
/** @typedef {import("webpack-sources").ConcatSource} ConcatSource */
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./ModuleTemplate")} ModuleTemplate */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Module")} Module} */
/** @typedef {import("./util/createHash").Hash} Hash} */
/** @typedef {import("./Dependency").DependencyTemplate} DependencyTemplate} */
/**
* @typedef {Object} RenderManifestOptions
* @property {Chunk} chunk the chunk used to render
* @property {string} hash
* @property {string} fullHash
* @property {TODO} outputOptions
* @property {{javascript: ModuleTemplate, webassembly: ModuleTemplate}} moduleTemplates
* @property {Map<TODO, TODO>} dependencyTemplates
*/
// require function shortcuts:
// __webpack_require__.s = the module id of the entry point
// __webpack_require__.c = the module cache
@@ -16,72 +43,180 @@ const Template = require("./Template");
// __webpack_require__.p = the bundle public path
// __webpack_require__.i = the identity function used for harmony imports
// __webpack_require__.e = the chunk ensure function
// __webpack_require__.d = the exported propery define getter function
// __webpack_require__.d = the exported property define getter function
// __webpack_require__.o = Object.prototype.hasOwnProperty.call
// __webpack_require__.r = define compatibility on export
// __webpack_require__.t = create a fake namespace object
// __webpack_require__.n = compatibility get default export
// __webpack_require__.h = the webpack hash
// __webpack_require__.oe = the uncatched error handler for the webpack runtime
// __webpack_require__.w = an object containing all installed WebAssembly.Instance export objects keyed by module id
// __webpack_require__.oe = the uncaught error handler for the webpack runtime
// __webpack_require__.nc = the script nonce
module.exports = class MainTemplate extends Template {
module.exports = class MainTemplate extends Tapable {
/**
*
* @param {TODO=} outputOptions output options for the MainTemplate
*/
constructor(outputOptions) {
super(outputOptions);
this.plugin("startup", (source, chunk, hash) => {
super();
/** @type {TODO?} */
this.outputOptions = outputOptions || {};
this.hooks = {
/** @type {SyncWaterfallHook<TODO[], RenderManifestOptions>} */
renderManifest: new SyncWaterfallHook(["result", "options"]),
modules: new SyncWaterfallHook([
"modules",
"chunk",
"hash",
"moduleTemplate",
"dependencyTemplates"
]),
moduleObj: new SyncWaterfallHook([
"source",
"chunk",
"hash",
"moduleIdExpression"
]),
requireEnsure: new SyncWaterfallHook([
"source",
"chunk",
"hash",
"chunkIdExpression"
]),
bootstrap: new SyncWaterfallHook([
"source",
"chunk",
"hash",
"moduleTemplate",
"dependencyTemplates"
]),
localVars: new SyncWaterfallHook(["source", "chunk", "hash"]),
require: new SyncWaterfallHook(["source", "chunk", "hash"]),
requireExtensions: new SyncWaterfallHook(["source", "chunk", "hash"]),
/** @type {SyncWaterfallHook<string, Chunk, string>} */
beforeStartup: new SyncWaterfallHook(["source", "chunk", "hash"]),
/** @type {SyncWaterfallHook<string, Chunk, string>} */
startup: new SyncWaterfallHook(["source", "chunk", "hash"]),
render: new SyncWaterfallHook([
"source",
"chunk",
"hash",
"moduleTemplate",
"dependencyTemplates"
]),
renderWithEntry: new SyncWaterfallHook(["source", "chunk", "hash"]),
moduleRequire: new SyncWaterfallHook([
"source",
"chunk",
"hash",
"moduleIdExpression"
]),
addModule: new SyncWaterfallHook([
"source",
"chunk",
"hash",
"moduleIdExpression",
"moduleExpression"
]),
currentHash: new SyncWaterfallHook(["source", "requestedLength"]),
assetPath: new SyncWaterfallHook(["path", "options"]),
hash: new SyncHook(["hash"]),
hashForChunk: new SyncHook(["hash", "chunk"]),
globalHashPaths: new SyncWaterfallHook(["paths"]),
globalHash: new SyncBailHook(["chunk", "paths"]),
// TODO this should be moved somewhere else
// It's weird here
hotBootstrap: new SyncWaterfallHook(["source", "chunk", "hash"])
};
this.hooks.startup.tap("MainTemplate", (source, chunk, hash) => {
/** @type {string[]} */
const buf = [];
if(chunk.entryModule) {
if (chunk.entryModule) {
buf.push("// Load entry module and return exports");
buf.push(`return ${this.renderRequireFunctionForModule(hash, chunk, JSON.stringify(chunk.entryModule.id))}(${this.requireFn}.s = ${JSON.stringify(chunk.entryModule.id)});`);
buf.push(
`return ${this.renderRequireFunctionForModule(
hash,
chunk,
JSON.stringify(chunk.entryModule.id)
)}(${this.requireFn}.s = ${JSON.stringify(chunk.entryModule.id)});`
);
}
return this.asString(buf);
return Template.asString(buf);
});
this.plugin("render", (bootstrapSource, chunk, hash, moduleTemplate, dependencyTemplates) => {
const source = new ConcatSource();
source.add("/******/ (function(modules) { // webpackBootstrap\n");
source.add(new PrefixSource("/******/", bootstrapSource));
source.add("/******/ })\n");
source.add("/************************************************************************/\n");
source.add("/******/ (");
const modules = this.renderChunkModules(chunk, moduleTemplate, dependencyTemplates, "/******/ ");
source.add(this.applyPluginsWaterfall("modules", modules, chunk, hash, moduleTemplate, dependencyTemplates));
source.add(")");
return source;
});
this.plugin("local-vars", (source, chunk, hash) => {
return this.asString([
this.hooks.render.tap(
"MainTemplate",
(bootstrapSource, chunk, hash, moduleTemplate, dependencyTemplates) => {
const source = new ConcatSource();
source.add("/******/ (function(modules) { // webpackBootstrap\n");
source.add(new PrefixSource("/******/", bootstrapSource));
source.add("/******/ })\n");
source.add(
"/************************************************************************/\n"
);
source.add("/******/ (");
source.add(
this.hooks.modules.call(
new RawSource(""),
chunk,
hash,
moduleTemplate,
dependencyTemplates
)
);
source.add(")");
return source;
}
);
this.hooks.localVars.tap("MainTemplate", (source, chunk, hash) => {
return Template.asString([
source,
"// The module cache",
"var installedModules = {};"
]);
});
this.plugin("require", (source, chunk, hash) => {
return this.asString([
this.hooks.require.tap("MainTemplate", (source, chunk, hash) => {
return Template.asString([
source,
"// Check if module is in cache",
"if(installedModules[moduleId]) {",
this.indent("return installedModules[moduleId].exports;"),
Template.indent("return installedModules[moduleId].exports;"),
"}",
"// Create a new module (and put it into the cache)",
"var module = installedModules[moduleId] = {",
this.indent(this.applyPluginsWaterfall("module-obj", "", chunk, hash, "moduleId")),
Template.indent(this.hooks.moduleObj.call("", chunk, hash, "moduleId")),
"};",
"",
this.asString(outputOptions.strictModuleExceptionHandling ? [
"// Execute the module function",
"var threw = true;",
"try {",
this.indent([
`modules[moduleId].call(module.exports, module, module.exports, ${this.renderRequireFunctionForModule(hash, chunk, "moduleId")});`,
"threw = false;"
]),
"} finally {",
this.indent([
"if(threw) delete installedModules[moduleId];"
]),
"}"
] : [
"// Execute the module function",
`modules[moduleId].call(module.exports, module, module.exports, ${this.renderRequireFunctionForModule(hash, chunk, "moduleId")});`,
]),
Template.asString(
outputOptions.strictModuleExceptionHandling
? [
"// Execute the module function",
"var threw = true;",
"try {",
Template.indent([
`modules[moduleId].call(module.exports, module, module.exports, ${this.renderRequireFunctionForModule(
hash,
chunk,
"moduleId"
)});`,
"threw = false;"
]),
"} finally {",
Template.indent([
"if(threw) delete installedModules[moduleId];"
]),
"}"
]
: [
"// Execute the module function",
`modules[moduleId].call(module.exports, module, module.exports, ${this.renderRequireFunctionForModule(
hash,
chunk,
"moduleId"
)});`
]
),
"",
"// Flag the module as loaded",
"module.l = true;",
@@ -90,22 +225,40 @@ module.exports = class MainTemplate extends Template {
"return module.exports;"
]);
});
this.plugin("module-obj", (source, chunk, hash, varModuleId) => {
return this.asString([
"i: moduleId,",
"l: false,",
"exports: {}"
]);
});
this.plugin("require-extensions", (source, chunk, hash) => {
this.hooks.moduleObj.tap(
"MainTemplate",
(source, chunk, hash, varModuleId) => {
return Template.asString(["i: moduleId,", "l: false,", "exports: {}"]);
}
);
this.hooks.requireExtensions.tap("MainTemplate", (source, chunk, hash) => {
const buf = [];
const chunkMaps = chunk.getChunkMaps();
// Check if there are non initial chunks which need to be imported using require-ensure
if(Object.keys(chunkMaps.hash).length) {
if (Object.keys(chunkMaps.hash).length) {
buf.push("// This file contains only the entry chunk.");
buf.push("// The chunk loading function for additional chunks");
buf.push(`${this.requireFn}.e = function requireEnsure(chunkId) {`);
buf.push(this.indent(this.applyPluginsWaterfall("require-ensure", "throw new Error('Not chunk loading available');", chunk, hash, "chunkId")));
buf.push(Template.indent("var promises = [];"));
buf.push(
Template.indent(
this.hooks.requireEnsure.call("", chunk, hash, "chunkId")
)
);
buf.push(Template.indent("return Promise.all(promises);"));
buf.push("};");
} else if (
chunk.hasModuleInGraph(m =>
m.blocks.some(b => b.chunkGroup && b.chunkGroup.chunks.length > 0)
)
) {
// There async blocks in the graph, so we need to add an empty requireEnsure
// function anyway. This can happen with multiple entrypoints.
buf.push("// The chunk loading function for additional chunks");
buf.push("// Since all referenced chunks are already included");
buf.push("// in this file, this function is empty here.");
buf.push(`${this.requireFn}.e = function requireEnsure() {`);
buf.push(Template.indent("return Promise.resolve();"));
buf.push("};");
}
buf.push("");
@@ -119,38 +272,81 @@ module.exports = class MainTemplate extends Template {
buf.push("");
buf.push("// define getter function for harmony exports");
buf.push(`${this.requireFn}.d = function(exports, name, getter) {`);
buf.push(this.indent([
`if(!${this.requireFn}.o(exports, name)) {`,
this.indent([
"Object.defineProperty(exports, name, {",
this.indent([
"configurable: false,",
"enumerable: true,",
"get: getter"
buf.push(
Template.indent([
`if(!${this.requireFn}.o(exports, name)) {`,
Template.indent([
"Object.defineProperty(exports, name, { enumerable: true, get: getter });"
]),
"});"
]),
"}"
]));
"}"
])
);
buf.push("};");
buf.push("");
buf.push("// getDefaultExport function for compatibility with non-harmony modules");
buf.push("// define __esModule on exports");
buf.push(`${this.requireFn}.r = function(exports) {`);
buf.push(
Template.indent([
"if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {",
Template.indent([
"Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });"
]),
"}",
"Object.defineProperty(exports, '__esModule', { value: true });"
])
);
buf.push("};");
buf.push("");
buf.push("// create a fake namespace object");
buf.push("// mode & 1: value is a module id, require it");
buf.push("// mode & 2: merge all properties of value into the ns");
buf.push("// mode & 4: return value when already ns object");
buf.push("// mode & 8|1: behave like require");
buf.push(`${this.requireFn}.t = function(value, mode) {`);
buf.push(
Template.indent([
`if(mode & 1) value = ${this.requireFn}(value);`,
`if(mode & 8) return value;`,
"if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;",
"var ns = Object.create(null);",
`${this.requireFn}.r(ns);`,
"Object.defineProperty(ns, 'default', { enumerable: true, value: value });",
"if(mode & 2 && typeof value != 'string') for(var key in value) " +
`${this.requireFn}.d(ns, key, function(key) { ` +
"return value[key]; " +
"}.bind(null, key));",
"return ns;"
])
);
buf.push("};");
buf.push("");
buf.push(
"// getDefaultExport function for compatibility with non-harmony modules"
);
buf.push(this.requireFn + ".n = function(module) {");
buf.push(this.indent([
"var getter = module && module.__esModule ?",
this.indent([
"function getDefault() { return module['default']; } :",
"function getModuleExports() { return module; };"
]),
`${this.requireFn}.d(getter, 'a', getter);`,
"return getter;"
]));
buf.push(
Template.indent([
"var getter = module && module.__esModule ?",
Template.indent([
"function getDefault() { return module['default']; } :",
"function getModuleExports() { return module; };"
]),
`${this.requireFn}.d(getter, 'a', getter);`,
"return getter;"
])
);
buf.push("};");
buf.push("");
buf.push("// Object.prototype.hasOwnProperty.call");
buf.push(`${this.requireFn}.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };`);
buf.push(
`${
this.requireFn
}.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };`
);
const publicPath = this.getPublicPath({
hash: hash
@@ -158,76 +354,195 @@ module.exports = class MainTemplate extends Template {
buf.push("");
buf.push("// __webpack_public_path__");
buf.push(`${this.requireFn}.p = ${JSON.stringify(publicPath)};`);
return this.asString(buf);
return Template.asString(buf);
});
this.requireFn = "__webpack_require__";
}
render(hash, chunk, moduleTemplate, dependencyTemplates) {
/**
*
* @param {RenderManifestOptions} options render manifest options
* @returns {TODO[]} returns render manifest
*/
getRenderManifest(options) {
const result = [];
this.hooks.renderManifest.call(result, options);
return result;
}
/**
* TODO webpack 5: remove moduleTemplate and dependencyTemplates
* @param {string} hash hash to be used for render call
* @param {Chunk} chunk Chunk instance
* @param {ModuleTemplate} moduleTemplate ModuleTemplate instance for render
* @param {Map<Function, DependencyTemplate>} dependencyTemplates dependency templates
* @returns {string[]} the generated source of the bootstrap code
*/
renderBootstrap(hash, chunk, moduleTemplate, dependencyTemplates) {
const buf = [];
buf.push(this.applyPluginsWaterfall("bootstrap", "", chunk, hash, moduleTemplate, dependencyTemplates));
buf.push(this.applyPluginsWaterfall("local-vars", "", chunk, hash));
buf.push(
this.hooks.bootstrap.call(
"",
chunk,
hash,
moduleTemplate,
dependencyTemplates
)
);
buf.push(this.hooks.localVars.call("", chunk, hash));
buf.push("");
buf.push("// The require function");
buf.push(`function ${this.requireFn}(moduleId) {`);
buf.push(this.indent(this.applyPluginsWaterfall("require", "", chunk, hash)));
buf.push(Template.indent(this.hooks.require.call("", chunk, hash)));
buf.push("}");
buf.push("");
buf.push(this.asString(this.applyPluginsWaterfall("require-extensions", "", chunk, hash)));
buf.push(
Template.asString(this.hooks.requireExtensions.call("", chunk, hash))
);
buf.push("");
buf.push(this.asString(this.applyPluginsWaterfall("startup", "", chunk, hash)));
let source = this.applyPluginsWaterfall("render", new OriginalSource(this.prefix(buf, " \t") + "\n", `webpack/bootstrap ${hash}`), chunk, hash, moduleTemplate, dependencyTemplates);
if(chunk.hasEntryModule()) {
source = this.applyPluginsWaterfall("render-with-entry", source, chunk, hash);
buf.push(Template.asString(this.hooks.beforeStartup.call("", chunk, hash)));
buf.push(Template.asString(this.hooks.startup.call("", chunk, hash)));
return buf;
}
/**
* @param {string} hash hash to be used for render call
* @param {Chunk} chunk Chunk instance
* @param {ModuleTemplate} moduleTemplate ModuleTemplate instance for render
* @param {Map<Function, DependencyTemplate>} dependencyTemplates dependency templates
* @returns {ConcatSource} the newly generated source from rendering
*/
render(hash, chunk, moduleTemplate, dependencyTemplates) {
const buf = this.renderBootstrap(
hash,
chunk,
moduleTemplate,
dependencyTemplates
);
let source = this.hooks.render.call(
new OriginalSource(
Template.prefix(buf, " \t") + "\n",
"webpack/bootstrap"
),
chunk,
hash,
moduleTemplate,
dependencyTemplates
);
if (chunk.hasEntryModule()) {
source = this.hooks.renderWithEntry.call(source, chunk, hash);
}
if (!source) {
throw new Error(
"Compiler error: MainTemplate plugin 'render' should return something"
);
}
if(!source) throw new Error("Compiler error: MainTemplate plugin 'render' should return something");
chunk.rendered = true;
return new ConcatSource(source, ";");
}
/**
*
* @param {string} hash hash for render fn
* @param {Chunk} chunk Chunk instance for require
* @param {(number|string)=} varModuleId module id
* @returns {TODO} the moduleRequire hook call return signature
*/
renderRequireFunctionForModule(hash, chunk, varModuleId) {
return this.applyPluginsWaterfall("module-require", this.requireFn, chunk, hash, varModuleId);
return this.hooks.moduleRequire.call(
this.requireFn,
chunk,
hash,
varModuleId
);
}
/**
*
* @param {string} hash hash for render add fn
* @param {Chunk} chunk Chunk instance for require add fn
* @param {(string|number)=} varModuleId module id
* @param {Module} varModule Module instance
* @returns {TODO} renderAddModule call
*/
renderAddModule(hash, chunk, varModuleId, varModule) {
return this.applyPluginsWaterfall("add-module", `modules[${varModuleId}] = ${varModule};`, chunk, hash, varModuleId, varModule);
return this.hooks.addModule.call(
`modules[${varModuleId}] = ${varModule};`,
chunk,
hash,
varModuleId,
varModule
);
}
/**
*
* @param {string} hash string hash
* @param {number=} length length
* @returns {string} call hook return
*/
renderCurrentHashCode(hash, length) {
length = length || Infinity;
return this.applyPluginsWaterfall("current-hash", JSON.stringify(hash.substr(0, length)), length);
}
entryPointInChildren(chunk) {
const checkChildren = (chunk, alreadyCheckedChunks) => {
return chunk.chunks.some((child) => {
if(alreadyCheckedChunks.indexOf(child) >= 0) return;
alreadyCheckedChunks.push(child);
return child.hasEntryModule() || checkChildren(child, alreadyCheckedChunks);
});
};
return checkChildren(chunk, []);
return this.hooks.currentHash.call(
JSON.stringify(hash.substr(0, length)),
length
);
}
/**
*
* @param {object} options get public path options
* @returns {string} hook call
*/
getPublicPath(options) {
return this.applyPluginsWaterfall("asset-path", this.outputOptions.publicPath || "", options);
return this.hooks.assetPath.call(
this.outputOptions.publicPath || "",
options
);
}
getAssetPath(path, options) {
return this.hooks.assetPath.call(path, options);
}
/**
* Updates hash with information from this template
* @param {Hash} hash the hash to update
* @returns {void}
*/
updateHash(hash) {
hash.update("maintemplate");
hash.update("3");
hash.update(this.outputOptions.publicPath + "");
this.applyPlugins("hash", hash);
this.hooks.hash.call(hash);
}
updateHashForChunk(hash, chunk) {
/**
* TODO webpack 5: remove moduleTemplate and dependencyTemplates
* Updates hash with chunk-specific information from this template
* @param {Hash} hash the hash to update
* @param {Chunk} chunk the chunk
* @param {ModuleTemplate} moduleTemplate ModuleTemplate instance for render
* @param {Map<Function, DependencyTemplate>} dependencyTemplates dependency templates
* @returns {void}
*/
updateHashForChunk(hash, chunk, moduleTemplate, dependencyTemplates) {
this.updateHash(hash);
this.applyPlugins("hash-for-chunk", hash, chunk);
this.hooks.hashForChunk.call(hash, chunk);
for (const line of this.renderBootstrap(
"0000",
chunk,
moduleTemplate,
dependencyTemplates
)) {
hash.update(line);
}
}
useChunkHash(chunk) {
const paths = this.applyPluginsWaterfall("global-hash-paths", []);
return !this.applyPluginsBailResult("global-hash", chunk, paths);
const paths = this.hooks.globalHashPaths.call([]);
return !this.hooks.globalHash.call(chunk, paths);
}
};

349
node_modules/webpack/lib/Module.js generated vendored
View File

@@ -11,6 +11,13 @@ const ModuleReason = require("./ModuleReason");
const SortableSet = require("./util/SortableSet");
const Template = require("./Template");
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./RequestShortener")} RequestShortener */
/** @typedef {import("./WebpackError")} WebpackError */
/** @typedef {import("./util/createHash").Hash} Hash */
const EMPTY_RESOLVE_OPTIONS = {};
let debugId = 1000;
const sortById = (a, b) => {
@@ -21,43 +28,112 @@ const sortByDebugId = (a, b) => {
return a.debugId - b.debugId;
};
class Module extends DependenciesBlock {
/** @typedef {(requestShortener: RequestShortener) => string} OptimizationBailoutFunction */
constructor() {
class Module extends DependenciesBlock {
constructor(type, context = null) {
super();
this.context = null;
this.reasons = [];
/** @type {string} */
this.type = type;
/** @type {string} */
this.context = context;
// Unique Id
/** @type {number} */
this.debugId = debugId++;
this.id = null;
this.portableId = null;
this.index = null;
this.index2 = null;
this.depth = null;
this.used = null;
this.usedExports = null;
this.providedExports = null;
this._chunks = new SortableSet(undefined, sortById);
this._chunksDebugIdent = undefined;
// Hash
/** @type {string} */
this.hash = undefined;
/** @type {string} */
this.renderedHash = undefined;
// Info from Factory
/** @type {TODO} */
this.resolveOptions = EMPTY_RESOLVE_OPTIONS;
/** @type {object} */
this.factoryMeta = {};
// Info from Build
/** @type {WebpackError[]} */
this.warnings = [];
this.dependenciesWarnings = [];
/** @type {WebpackError[]} */
this.errors = [];
this.dependenciesErrors = [];
this.strict = false;
this.meta = {};
/** @type {object} */
this.buildMeta = undefined;
/** @type {object} */
this.buildInfo = undefined;
// Graph (per Compilation)
/** @type {ModuleReason[]} */
this.reasons = [];
/** @type {SortableSet<Chunk>} */
this._chunks = new SortableSet(undefined, sortById);
// Info from Compilation (per Compilation)
/** @type {number|string} */
this.id = null;
/** @type {number} */
this.index = null;
/** @type {number} */
this.index2 = null;
/** @type {number} */
this.depth = null;
/** @type {Module} */
this.issuer = null;
/** @type {undefined | object} */
this.profile = undefined;
/** @type {boolean} */
this.prefetched = false;
/** @type {boolean} */
this.built = false;
// Info from Optimization (per Compilation)
/** @type {null | boolean} */
this.used = null;
/** @type {false | true | string[]} */
this.usedExports = null;
/** @type {(string | OptimizationBailoutFunction)[]} */
this.optimizationBailout = [];
// delayed operations
/** @type {undefined | {oldChunk: Chunk, newChunks: Chunk[]}[] } */
this._rewriteChunkInReasons = undefined;
/** @type {boolean} */
this.useSourceMap = false;
// info from build
this._source = null;
}
get exportsArgument() {
return (this.buildInfo && this.buildInfo.exportsArgument) || "exports";
}
get moduleArgument() {
return (this.buildInfo && this.buildInfo.moduleArgument) || "module";
}
disconnect() {
this.hash = undefined;
this.renderedHash = undefined;
this.reasons.length = 0;
this._rewriteChunkInReasons = undefined;
this._chunks.clear();
this.id = null;
this.index = null;
this.index2 = null;
this.depth = null;
this.issuer = null;
this.profile = undefined;
this.prefetched = false;
this.built = false;
this.used = null;
this.usedExports = null;
this.providedExports = null;
this._chunks.clear();
this._chunksDebugIdent = undefined;
this.optimizationBailout.length = 0;
super.disconnect();
}
@@ -68,23 +144,21 @@ class Module extends DependenciesBlock {
this.index2 = null;
this.depth = null;
this._chunks.clear();
this._chunksDebugIdent = undefined;
super.unseal();
}
setChunks(chunks) {
this._chunks = new SortableSet(chunks, sortById);
this._chunksDebugIdent = undefined;
}
addChunk(chunk) {
if (this._chunks.has(chunk)) return false;
this._chunks.add(chunk);
this._chunksDebugIdent = undefined;
return true;
}
removeChunk(chunk) {
if(this._chunks.delete(chunk)) {
this._chunksDebugIdent = undefined;
if (this._chunks.delete(chunk)) {
chunk.removeModule(this);
return true;
}
@@ -95,32 +169,23 @@ class Module extends DependenciesBlock {
return this._chunks.has(chunk);
}
getChunkIdsIdent() {
if(this._chunksDebugIdent !== undefined) return this._chunksDebugIdent;
this._chunks.sortWith(sortByDebugId);
const chunks = this._chunks;
const list = [];
for(const chunk of chunks) {
const debugId = chunk.debugId;
if(typeof debugId !== "number") {
return this._chunksDebugIdent = null;
}
list.push(debugId);
isEntryModule() {
for (const chunk of this._chunks) {
if (chunk.entryModule === this) return true;
}
return this._chunksDebugIdent = list.join(",");
return false;
}
forEachChunk(fn) {
this._chunks.forEach(fn);
}
mapChunks(fn) {
return Array.from(this._chunks, fn);
get optional() {
return (
this.reasons.length > 0 &&
this.reasons.every(r => r.dependency && r.dependency.optional)
);
}
/**
* @returns {Chunk[]} all chunks which contain the module
*/
getChunks() {
return Array.from(this._chunks);
}
@@ -129,28 +194,33 @@ class Module extends DependenciesBlock {
return this._chunks.size;
}
get chunksIterable() {
return this._chunks;
}
hasEqualsChunks(otherModule) {
if(this._chunks.size !== otherModule._chunks.size) return false;
if (this._chunks.size !== otherModule._chunks.size) return false;
this._chunks.sortWith(sortByDebugId);
otherModule._chunks.sortWith(sortByDebugId);
const a = this._chunks[Symbol.iterator]();
const b = otherModule._chunks[Symbol.iterator]();
while(true) { // eslint-disable-line
// eslint-disable-next-line no-constant-condition
while (true) {
const aItem = a.next();
const bItem = b.next();
if(aItem.done) return true;
if(aItem.value !== bItem.value) return false;
if (aItem.done) return true;
if (aItem.value !== bItem.value) return false;
}
}
addReason(module, dependency) {
this.reasons.push(new ModuleReason(module, dependency));
addReason(module, dependency, explanation) {
this.reasons.push(new ModuleReason(module, dependency, explanation));
}
removeReason(module, dependency) {
for(let i = 0; i < this.reasons.length; i++) {
for (let i = 0; i < this.reasons.length; i++) {
let r = this.reasons[i];
if(r.module === module && r.dependency === dependency) {
if (r.module === module && r.dependency === dependency) {
this.reasons.splice(i, 1);
return true;
}
@@ -159,36 +229,70 @@ class Module extends DependenciesBlock {
}
hasReasonForChunk(chunk) {
for(let i = 0; i < this.reasons.length; i++) {
if(this.reasons[i].hasChunk(chunk))
return true;
if (this._rewriteChunkInReasons) {
for (const operation of this._rewriteChunkInReasons) {
this._doRewriteChunkInReasons(operation.oldChunk, operation.newChunks);
}
this._rewriteChunkInReasons = undefined;
}
for (let i = 0; i < this.reasons.length; i++) {
if (this.reasons[i].hasChunk(chunk)) return true;
}
return false;
}
hasReasons() {
return this.reasons.length > 0;
}
rewriteChunkInReasons(oldChunk, newChunks) {
for(let i = 0; i < this.reasons.length; i++) {
// This is expensive. Delay operation until we really need the data
if (this._rewriteChunkInReasons === undefined) {
this._rewriteChunkInReasons = [];
}
this._rewriteChunkInReasons.push({
oldChunk,
newChunks
});
}
_doRewriteChunkInReasons(oldChunk, newChunks) {
for (let i = 0; i < this.reasons.length; i++) {
this.reasons[i].rewriteChunks(oldChunk, newChunks);
}
}
/**
* @param {string=} exportName the name of the export
* @returns {boolean|string} false if the export isn't used, true if no exportName is provided and the module is used, or the name to access it if the export is used
*/
isUsed(exportName) {
if(this.used === null) return exportName;
if(!exportName) return !!this.used;
if(!this.used) return false;
if(!this.usedExports) return false;
if(this.usedExports === true) return exportName;
if (!exportName) return this.used !== false;
if (this.used === null || this.usedExports === null) return exportName;
if (!this.used) return false;
if (!this.usedExports) return false;
if (this.usedExports === true) return exportName;
let idx = this.usedExports.indexOf(exportName);
if(idx < 0) return false;
if(this.isProvided(exportName))
return Template.numberToIdentifer(idx);
if (idx < 0) return false;
// Mangle export name if possible
if (this.isProvided(exportName)) {
if (this.buildMeta.exportsType === "namespace") {
return Template.numberToIdentifer(idx);
}
if (
this.buildMeta.exportsType === "named" &&
!this.usedExports.includes("default")
) {
return Template.numberToIdentifer(idx);
}
}
return exportName;
}
isProvided(exportName) {
if(!Array.isArray(this.providedExports))
return null;
return this.providedExports.indexOf(exportName) >= 0;
if (!Array.isArray(this.buildMeta.providedExports)) return null;
return this.buildMeta.providedExports.includes(exportName);
}
toString() {
@@ -199,27 +303,83 @@ class Module extends DependenciesBlock {
return true;
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
hash.update(this.id + "" + this.used);
hash.update(`${this.id}`);
hash.update(JSON.stringify(this.usedExports));
super.updateHash(hash);
}
sortItems(sortChunks) {
super.sortItems();
if(sortChunks)
this._chunks.sort();
this.reasons.sort((a, b) => sortById(a.module, b.module));
if(Array.isArray(this.usedExports)) {
if (sortChunks) this._chunks.sort();
this.reasons.sort((a, b) => {
if (a.module === b.module) return 0;
if (!a.module) return -1;
if (!b.module) return 1;
return sortById(a.module, b.module);
});
if (Array.isArray(this.usedExports)) {
this.usedExports.sort();
}
}
unbuild() {
this.dependencies.length = 0;
this.blocks.length = 0;
this.variables.length = 0;
this.buildMeta = undefined;
this.buildInfo = undefined;
this.disconnect();
}
get arguments() {
throw new Error("Module.arguments was removed, there is no replacement.");
}
set arguments(value) {
throw new Error("Module.arguments was removed, there is no replacement.");
}
}
// TODO remove in webpack 5
Object.defineProperty(Module.prototype, "forEachChunk", {
configurable: false,
value: util.deprecate(
/**
* @deprecated
* @param {function(any, any, Set<any>): void} fn callback function
* @returns {void}
* @this {Module}
*/
function(fn) {
this._chunks.forEach(fn);
},
"Module.forEachChunk: Use for(const chunk of module.chunksIterable) instead"
)
});
// TODO remove in webpack 5
Object.defineProperty(Module.prototype, "mapChunks", {
configurable: false,
value: util.deprecate(
/**
* @deprecated
* @param {function(any, any): void} fn Mapper function
* @returns {Array<TODO>} Array of chunks mapped
* @this {Module}
*/
function(fn) {
return Array.from(this._chunks, fn);
},
"Module.mapChunks: Use Array.from(module.chunksIterable, fn) instead"
)
});
// TODO remove in webpack 5
Object.defineProperty(Module.prototype, "entry", {
configurable: false,
get() {
@@ -230,21 +390,46 @@ Object.defineProperty(Module.prototype, "entry", {
}
});
Object.defineProperty(Module.prototype, "chunks", {
// TODO remove in webpack 5
Object.defineProperty(Module.prototype, "meta", {
configurable: false,
get: util.deprecate(function() {
return Array.from(this._chunks);
}, "Module.chunks: Use Module.forEachChunk/mapChunks/getNumberOfChunks/isInChunk/addChunk/removeChunk instead"),
set() {
throw new Error("Readonly. Use Module.addChunk/removeChunk to modify chunks.");
}
get: util.deprecate(
/**
* @deprecated
* @returns {void}
* @this {Module}
*/
function() {
return this.buildMeta;
},
"Module.meta was renamed to Module.buildMeta"
),
set: util.deprecate(
/**
* @deprecated
* @param {TODO} value Value
* @returns {void}
* @this {Module}
*/
function(value) {
this.buildMeta = value;
},
"Module.meta was renamed to Module.buildMeta"
)
});
/** @type {function(): string} */
Module.prototype.identifier = null;
/** @type {function(RequestShortener): string} */
Module.prototype.readableIdentifier = null;
Module.prototype.build = null;
Module.prototype.source = null;
Module.prototype.size = null;
Module.prototype.nameForCondition = null;
/** @type {null | function(Chunk): boolean} */
Module.prototype.chunkCondition = null;
Module.prototype.updateCacheModule = null;
module.exports = Module;

View File

@@ -5,33 +5,43 @@
"use strict";
const WebpackError = require("./WebpackError");
const cutOffLoaderExecution = require("./ErrorHelpers").cutOffLoaderExecution;
const { cutOffLoaderExecution } = require("./ErrorHelpers");
class ModuleBuildError extends WebpackError {
constructor(module, err) {
super();
this.name = "ModuleBuildError";
this.message = "Module build failed: ";
if(err !== null && typeof err === "object") {
if(typeof err.stack === "string" && err.stack) {
var stack = cutOffLoaderExecution(err.stack);
if(!err.hideStack) {
this.message += stack;
constructor(module, err, { from = null } = {}) {
let message = "Module build failed";
let details = undefined;
if (from) {
message += ` (from ${from}):\n`;
} else {
message += ": ";
}
if (err !== null && typeof err === "object") {
if (typeof err.stack === "string" && err.stack) {
const stack = cutOffLoaderExecution(err.stack);
if (!err.hideStack) {
message += stack;
} else {
this.details = stack;
if(typeof err.message === "string" && err.message) {
this.message += err.message;
details = stack;
if (typeof err.message === "string" && err.message) {
message += err.message;
} else {
this.message += err;
message += err;
}
}
} else if(typeof err.message === "string" && err.message) {
this.message += err.message;
} else if (typeof err.message === "string" && err.message) {
message += err.message;
} else {
this.message += err;
message += err;
}
} else {
message = err;
}
super(message);
this.name = "ModuleBuildError";
this.details = details;
this.module = module;
this.error = err;

View File

@@ -5,18 +5,31 @@
"use strict";
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
module.exports = class ModuleDependencyError extends WebpackError {
/** @typedef {import("./Module")} Module */
class ModuleDependencyError extends WebpackError {
/**
* Creates an instance of ModuleDependencyError.
* @param {Module} module module tied to dependency
* @param {Error} err error thrown
* @param {TODO} loc location of dependency
*/
constructor(module, err, loc) {
super();
super(err.message);
this.name = "ModuleDependencyError";
this.message = `${formatLocation(loc)} ${err.message}`;
this.details = err.stack.split("\n").slice(1).join("\n");
this.origin = this.module = module;
this.details = err.stack
.split("\n")
.slice(1)
.join("\n");
this.module = module;
this.loc = loc;
this.error = err;
this.origin = module.issuer;
Error.captureStackTrace(this, this.constructor);
}
};
}
module.exports = ModuleDependencyError;

View File

@@ -5,17 +5,20 @@
"use strict";
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
module.exports = class ModuleDependencyWarning extends WebpackError {
constructor(module, err, loc) {
super();
super(err.message);
this.name = "ModuleDependencyWarning";
this.message = `${formatLocation(loc)} ${err.message}`;
this.details = err.stack.split("\n").slice(1).join("\n");
this.origin = this.module = module;
this.details = err.stack
.split("\n")
.slice(1)
.join("\n");
this.module = module;
this.loc = loc;
this.error = err;
this.origin = module.issuer;
Error.captureStackTrace(this, this.constructor);
}

View File

@@ -5,17 +5,29 @@
"use strict";
const WebpackError = require("./WebpackError");
const cleanUp = require("./ErrorHelpers").cleanUp;
const { cleanUp } = require("./ErrorHelpers");
class ModuleError extends WebpackError {
constructor(module, err) {
super();
constructor(module, err, { from = null } = {}) {
let message = "Module Error";
if (from) {
message += ` (from ${from}):\n`;
} else {
message += ": ";
}
if (err && typeof err === "object" && err.message) {
message += err.message;
} else if (err) {
message += err;
}
super(message);
this.name = "ModuleError";
this.module = module;
this.message = err && typeof err === "object" && err.message ? err.message : err;
this.error = err;
this.details = err && typeof err === "object" && err.stack ? cleanUp(err.stack, this.message) : undefined;
this.details =
err && typeof err === "object" && err.stack
? cleanUp(err.stack, this.message)
: undefined;
Error.captureStackTrace(this, this.constructor);
}

View File

@@ -4,6 +4,8 @@
*/
"use strict";
const createHash = require("./util/createHash");
const ModuleFilenameHelpers = exports;
ModuleFilenameHelpers.ALL_LOADERS_RESOURCE = "[all-loaders][resource]";
@@ -26,36 +28,52 @@ ModuleFilenameHelpers.ID = "[id]";
ModuleFilenameHelpers.REGEXP_ID = /\[id\]/gi;
ModuleFilenameHelpers.HASH = "[hash]";
ModuleFilenameHelpers.REGEXP_HASH = /\[hash\]/gi;
ModuleFilenameHelpers.NAMESPACE = "[namespace]";
ModuleFilenameHelpers.REGEXP_NAMESPACE = /\[namespace\]/gi;
function getAfter(str, token) {
const getAfter = (str, token) => {
const idx = str.indexOf(token);
return idx < 0 ? "" : str.substr(idx);
}
};
function getBefore(str, token) {
const getBefore = (str, token) => {
const idx = str.lastIndexOf(token);
return idx < 0 ? "" : str.substr(0, idx);
}
};
function getHash(str) {
const hash = require("crypto").createHash("md5");
const getHash = str => {
const hash = createHash("md4");
hash.update(str);
return hash.digest("hex").substr(0, 4);
}
};
function asRegExp(test) {
if(typeof test === "string") test = new RegExp("^" + test.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"));
const asRegExp = test => {
if (typeof test === "string") {
test = new RegExp("^" + test.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"));
}
return test;
}
};
ModuleFilenameHelpers.createFilename = (module, options, requestShortener) => {
const opts = Object.assign(
{
namespace: "",
moduleFilenameTemplate: ""
},
typeof options === "object"
? options
: {
moduleFilenameTemplate: options
}
);
ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFilenameTemplate, requestShortener) {
let absoluteResourcePath;
let hash;
let identifier;
let moduleId;
let shortIdentifier;
if(module === undefined) module = "";
if(typeof module === "string") {
if (module === undefined) module = "";
if (typeof module === "string") {
shortIdentifier = requestShortener.shorten(module);
identifier = shortIdentifier;
moduleId = "";
@@ -65,7 +83,10 @@ ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFil
shortIdentifier = module.readableIdentifier(requestShortener);
identifier = requestShortener.shorten(module.identifier());
moduleId = module.id;
absoluteResourcePath = module.identifier().split("!").pop();
absoluteResourcePath = module
.identifier()
.split("!")
.pop();
hash = getHash(identifier);
}
const resource = shortIdentifier.split("!").pop();
@@ -73,8 +94,8 @@ ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFil
const allLoaders = getBefore(identifier, "!");
const query = getAfter(resource, "?");
const resourcePath = resource.substr(0, resource.length - query.length);
if(typeof moduleFilenameTemplate === "function") {
return moduleFilenameTemplate({
if (typeof opts.moduleFilenameTemplate === "function") {
return opts.moduleFilenameTemplate({
identifier: identifier,
shortIdentifier: shortIdentifier,
resource: resource,
@@ -83,80 +104,75 @@ ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFil
allLoaders: allLoaders,
query: query,
moduleId: moduleId,
hash: hash
hash: hash,
namespace: opts.namespace
});
}
return moduleFilenameTemplate
return opts.moduleFilenameTemplate
.replace(ModuleFilenameHelpers.REGEXP_ALL_LOADERS_RESOURCE, identifier)
.replace(ModuleFilenameHelpers.REGEXP_LOADERS_RESOURCE, shortIdentifier)
.replace(ModuleFilenameHelpers.REGEXP_RESOURCE, resource)
.replace(ModuleFilenameHelpers.REGEXP_RESOURCE_PATH, resourcePath)
.replace(ModuleFilenameHelpers.REGEXP_ABSOLUTE_RESOURCE_PATH, absoluteResourcePath)
.replace(
ModuleFilenameHelpers.REGEXP_ABSOLUTE_RESOURCE_PATH,
absoluteResourcePath
)
.replace(ModuleFilenameHelpers.REGEXP_ALL_LOADERS, allLoaders)
.replace(ModuleFilenameHelpers.REGEXP_LOADERS, loaders)
.replace(ModuleFilenameHelpers.REGEXP_QUERY, query)
.replace(ModuleFilenameHelpers.REGEXP_ID, moduleId)
.replace(ModuleFilenameHelpers.REGEXP_HASH, hash);
.replace(ModuleFilenameHelpers.REGEXP_HASH, hash)
.replace(ModuleFilenameHelpers.REGEXP_NAMESPACE, opts.namespace);
};
ModuleFilenameHelpers.createFooter = function createFooter(module, requestShortener) {
if(!module) module = "";
if(typeof module === "string") {
return [
"// WEBPACK FOOTER //",
`// ${requestShortener.shorten(module)}`
].join("\n");
} else {
return [
"//////////////////",
"// WEBPACK FOOTER",
`// ${module.readableIdentifier(requestShortener)}`,
`// module id = ${module.id}`,
`// module chunks = ${module.mapChunks(c => c.id).join(" ")}`
].join("\n");
}
};
ModuleFilenameHelpers.replaceDuplicates = function replaceDuplicates(array, fn, comparator) {
ModuleFilenameHelpers.replaceDuplicates = (array, fn, comparator) => {
const countMap = Object.create(null);
const posMap = Object.create(null);
array.forEach((item, idx) => {
countMap[item] = (countMap[item] || []);
countMap[item] = countMap[item] || [];
countMap[item].push(idx);
posMap[item] = 0;
});
if(comparator) {
if (comparator) {
Object.keys(countMap).forEach(item => {
countMap[item].sort(comparator);
});
}
return array.map((item, i) => {
if(countMap[item].length > 1) {
if(comparator && countMap[item][0] === i)
return item;
if (countMap[item].length > 1) {
if (comparator && countMap[item][0] === i) return item;
return fn(item, i, posMap[item]++);
} else return item;
} else {
return item;
}
});
};
ModuleFilenameHelpers.matchPart = function matchPart(str, test) {
if(!test) return true;
ModuleFilenameHelpers.matchPart = (str, test) => {
if (!test) return true;
test = asRegExp(test);
if(Array.isArray(test)) {
return test.map(asRegExp).filter(function(regExp) {
return regExp.test(str);
}).length > 0;
if (Array.isArray(test)) {
return test.map(asRegExp).some(regExp => regExp.test(str));
} else {
return test.test(str);
}
};
ModuleFilenameHelpers.matchObject = function matchObject(obj, str) {
if(obj.test)
if(!ModuleFilenameHelpers.matchPart(str, obj.test)) return false;
if(obj.include)
if(!ModuleFilenameHelpers.matchPart(str, obj.include)) return false;
if(obj.exclude)
if(ModuleFilenameHelpers.matchPart(str, obj.exclude)) return false;
ModuleFilenameHelpers.matchObject = (obj, str) => {
if (obj.test) {
if (!ModuleFilenameHelpers.matchPart(str, obj.test)) {
return false;
}
}
if (obj.include) {
if (!ModuleFilenameHelpers.matchPart(str, obj.include)) {
return false;
}
}
if (obj.exclude) {
if (ModuleFilenameHelpers.matchPart(str, obj.exclude)) {
return false;
}
}
return true;
};

View File

@@ -7,16 +7,13 @@
const WebpackError = require("./WebpackError");
class ModuleNotFoundError extends WebpackError {
constructor(module, err, dependencies) {
super();
constructor(module, err) {
super("Module not found: " + err);
this.name = "ModuleNotFoundError";
this.message = "Module not found: " + err;
this.details = err.details;
this.missing = err.missing;
this.module = module;
this.origin = module;
this.dependencies = dependencies;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@@ -6,25 +6,48 @@
const WebpackError = require("./WebpackError");
/** @typedef {import("./Module")} Module */
class ModuleParseError extends WebpackError {
/**
* @param {Module} module the errored module
* @param {string} source source code
* @param {Error&any} err the parse error
*/
constructor(module, source, err) {
super();
let message = "Module parse failed: " + err.message;
let loc = undefined;
message += "\nYou may need an appropriate loader to handle this file type.";
if (
err.loc &&
typeof err.loc === "object" &&
typeof err.loc.line === "number"
) {
var lineNumber = err.loc.line;
if (/[\0\u0001\u0002\u0003\u0004\u0005\u0006\u0007]/.test(source)) {
// binary file
message += "\n(Source code omitted for this binary file)";
} else {
const sourceLines = source.split("\n");
const start = Math.max(0, lineNumber - 3);
const linesBefore = sourceLines.slice(start, lineNumber - 1);
const theLine = sourceLines[lineNumber - 1];
const linesAfter = sourceLines.slice(lineNumber, lineNumber + 2);
message +=
linesBefore.map(l => `\n| ${l}`).join("") +
`\n> ${theLine}` +
linesAfter.map(l => `\n| ${l}`).join("");
}
loc = err.loc;
} else {
message += "\n" + err.stack;
}
super(message);
this.name = "ModuleParseError";
this.message = "Module parse failed: " + err.message;
this.message += "\nYou may need an appropriate loader to handle this file type.";
if(err.loc && typeof err.loc === "object" && typeof err.loc.line === "number") {
var lineNumber = err.loc.line;
if(/[\0\u0001\u0002\u0003\u0004\u0005\u0006\u0007]/.test(source)) { // binary file
this.message += "\n(Source code omitted for this binary file)";
} else {
source = source.split("\n");
this.message += "\n| " + source.slice(Math.max(0, lineNumber - 3), lineNumber + 2).join("\n| ");
}
} else {
this.message += "\n" + err.stack;
}
this.module = module;
this.loc = loc;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@@ -4,47 +4,45 @@
*/
"use strict";
const util = require("util");
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Dependency")} Dependency */
class ModuleReason {
constructor(module, dependency) {
/**
* @param {Module} module the referencing module
* @param {Dependency} dependency the referencing dependency
* @param {string=} explanation some extra detail
*/
constructor(module, dependency, explanation) {
this.module = module;
this.dependency = dependency;
this.explanation = explanation;
this._chunks = null;
}
hasChunk(chunk) {
if(this._chunks) {
if(this._chunks.has(chunk))
return true;
} else if(this.module._chunks.has(chunk))
return true;
if (this._chunks) {
if (this._chunks.has(chunk)) return true;
} else if (this.module && this.module._chunks.has(chunk)) return true;
return false;
}
rewriteChunks(oldChunk, newChunks) {
if(!this._chunks) {
if(!this.module._chunks.has(oldChunk))
return;
this._chunks = new Set(this.module._chunks);
if (!this._chunks) {
if (this.module) {
if (!this.module._chunks.has(oldChunk)) return;
this._chunks = new Set(this.module._chunks);
} else {
this._chunks = new Set();
}
}
if(this._chunks.has(oldChunk)) {
if (this._chunks.has(oldChunk)) {
this._chunks.delete(oldChunk);
for(let i = 0; i < newChunks.length; i++) {
for (let i = 0; i < newChunks.length; i++) {
this._chunks.add(newChunks[i]);
}
}
}
}
Object.defineProperty(ModuleReason.prototype, "chunks", {
configurable: false,
get: util.deprecate(function() {
return this._chunks ? Array.from(this._chunks) : null;
}, "ModuleReason.chunks: Use ModuleReason.hasChunk/rewriteChunks instead"),
set() {
throw new Error("Readonly. Use ModuleReason.rewriteChunks to modify chunks.");
}
});
module.exports = ModuleReason;

View File

@@ -4,20 +4,90 @@
*/
"use strict";
const Template = require("./Template");
const { Tapable, SyncWaterfallHook, SyncHook } = require("tapable");
module.exports = class ModuleTemplate extends Template {
constructor(outputOptions) {
super(outputOptions);
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("./Module")} Module */
module.exports = class ModuleTemplate extends Tapable {
constructor(runtimeTemplate, type) {
super();
this.runtimeTemplate = runtimeTemplate;
this.type = type;
this.hooks = {
content: new SyncWaterfallHook([
"source",
"module",
"options",
"dependencyTemplates"
]),
module: new SyncWaterfallHook([
"source",
"module",
"options",
"dependencyTemplates"
]),
render: new SyncWaterfallHook([
"source",
"module",
"options",
"dependencyTemplates"
]),
package: new SyncWaterfallHook([
"source",
"module",
"options",
"dependencyTemplates"
]),
hash: new SyncHook(["hash"])
};
}
render(module, dependencyTemplates, chunk) {
const moduleSource = module.source(dependencyTemplates, this.outputOptions, this.requestShortener);
const moduleSourcePostModule = this.applyPluginsWaterfall("module", moduleSource, module, chunk, dependencyTemplates);
const moduleSourcePostRender = this.applyPluginsWaterfall("render", moduleSourcePostModule, module, chunk, dependencyTemplates);
return this.applyPluginsWaterfall("package", moduleSourcePostRender, module, chunk, dependencyTemplates);
/**
* @param {Module} module the module
* @param {TODO} dependencyTemplates templates for dependencies
* @param {TODO} options render options
* @returns {Source} the source
*/
render(module, dependencyTemplates, options) {
try {
const moduleSource = module.source(
dependencyTemplates,
this.runtimeTemplate,
this.type
);
const moduleSourcePostContent = this.hooks.content.call(
moduleSource,
module,
options,
dependencyTemplates
);
const moduleSourcePostModule = this.hooks.module.call(
moduleSourcePostContent,
module,
options,
dependencyTemplates
);
const moduleSourcePostRender = this.hooks.render.call(
moduleSourcePostModule,
module,
options,
dependencyTemplates
);
return this.hooks.package.call(
moduleSourcePostRender,
module,
options,
dependencyTemplates
);
} catch (e) {
e.message = `${module.identifier()}\n${e.message}`;
throw e;
}
}
updateHash(hash) {
hash.update("1");
this.applyPlugins("hash", hash);
this.hooks.hash.call(hash);
}
};

View File

@@ -5,17 +5,29 @@
"use strict";
const WebpackError = require("./WebpackError");
const cleanUp = require("./ErrorHelpers").cleanUp;
const { cleanUp } = require("./ErrorHelpers");
class ModuleWarning extends WebpackError {
constructor(module, warning) {
super();
constructor(module, warning, { from = null } = {}) {
let message = "Module Warning";
if (from) {
message += ` (from ${from}):\n`;
} else {
message += ": ";
}
if (warning && typeof warning === "object" && warning.message) {
message += warning.message;
} else if (warning) {
message += warning;
}
super(message);
this.name = "ModuleWarning";
this.module = module;
this.message = warning && typeof warning === "object" && warning.message ? warning.message : warning;
this.warning = warning;
this.details = warning && typeof warning === "object" && warning.stack ? cleanUp(warning.stack, this.message) : undefined;
this.details =
warning && typeof warning === "object" && warning.stack
? cleanUp(warning.stack, this.message)
: undefined;
Error.captureStackTrace(this, this.constructor);
}

View File

@@ -1,21 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
module.exports = class MovedToPluginWarningPlugin {
constructor(optionName, pluginName) {
this.optionName = optionName;
this.pluginName = pluginName;
}
apply(compiler) {
const optionName = this.optionName;
const pluginName = this.pluginName;
compiler.plugin("compilation", (compilation) => {
compilation.warnings.push(new Error `webpack options:
DEPRECATED option ${optionName} will be moved to the ${pluginName}.
Use this instead.
For more info about the usage of the ${pluginName} see https://webpack.js.org/plugins/`);
});
}
};

View File

@@ -4,16 +4,24 @@
*/
"use strict";
const Tapable = require("tapable");
const asyncLib = require("async");
const { Tapable, SyncHook, MultiHook } = require("tapable");
const asyncLib = require("neo-async");
const MultiWatching = require("./MultiWatching");
const MultiStats = require("./MultiStats");
const ConcurrentCompilationError = require("./ConcurrentCompilationError");
module.exports = class MultiCompiler extends Tapable {
constructor(compilers) {
super();
if(!Array.isArray(compilers)) {
compilers = Object.keys(compilers).map((name) => {
this.hooks = {
done: new SyncHook(["stats"]),
invalid: new MultiHook(compilers.map(c => c.hooks.invalid)),
run: new MultiHook(compilers.map(c => c.hooks.run)),
watchClose: new SyncHook([]),
watchRun: new MultiHook(compilers.map(c => c.hooks.watchRun))
};
if (!Array.isArray(compilers)) {
compilers = Object.keys(compilers).map(name => {
compilers[name].name = name;
return compilers[name];
});
@@ -21,37 +29,44 @@ module.exports = class MultiCompiler extends Tapable {
this.compilers = compilers;
let doneCompilers = 0;
let compilerStats = [];
this.compilers.forEach((compiler, idx) => {
let index = 0;
for (const compiler of this.compilers) {
let compilerDone = false;
compiler.plugin("done", stats => {
if(!compilerDone) {
const compilerIndex = index++;
// eslint-disable-next-line no-loop-func
compiler.hooks.done.tap("MultiCompiler", stats => {
if (!compilerDone) {
compilerDone = true;
doneCompilers++;
}
compilerStats[idx] = stats;
if(doneCompilers === this.compilers.length) {
this.applyPlugins("done", new MultiStats(compilerStats));
compilerStats[compilerIndex] = stats;
if (doneCompilers === this.compilers.length) {
this.hooks.done.call(new MultiStats(compilerStats));
}
});
compiler.plugin("invalid", () => {
if(compilerDone) {
// eslint-disable-next-line no-loop-func
compiler.hooks.invalid.tap("MultiCompiler", () => {
if (compilerDone) {
compilerDone = false;
doneCompilers--;
}
this.applyPlugins("invalid");
});
}, this);
}
this.running = false;
}
get outputPath() {
let commonPath = this.compilers[0].outputPath;
for(const compiler of this.compilers) {
while(compiler.outputPath.indexOf(commonPath) !== 0 && /[/\\]/.test(commonPath)) {
for (const compiler of this.compilers) {
while (
compiler.outputPath.indexOf(commonPath) !== 0 &&
/[/\\]/.test(commonPath)
) {
commonPath = commonPath.replace(/[/\\][^/\\]*$/, "");
}
}
if(!commonPath && this.compilers[0].outputPath[0] === "/") return "/";
if (!commonPath && this.compilers[0].outputPath[0] === "/") return "/";
return commonPath;
}
@@ -64,101 +79,205 @@ module.exports = class MultiCompiler extends Tapable {
}
set inputFileSystem(value) {
this.compilers.forEach(compiler => {
for (const compiler of this.compilers) {
compiler.inputFileSystem = value;
});
}
}
set outputFileSystem(value) {
this.compilers.forEach(compiler => {
for (const compiler of this.compilers) {
compiler.outputFileSystem = value;
});
}
}
validateDependencies(callback) {
const edges = new Set();
const missing = [];
const targetFound = compiler => {
for (const edge of edges) {
if (edge.target === compiler) {
return true;
}
}
return false;
};
const sortEdges = (e1, e2) => {
return (
e1.source.name.localeCompare(e2.source.name) ||
e1.target.name.localeCompare(e2.target.name)
);
};
for (const source of this.compilers) {
if (source.dependencies) {
for (const dep of source.dependencies) {
const target = this.compilers.find(c => c.name === dep);
if (!target) {
missing.push(dep);
} else {
edges.add({
source,
target
});
}
}
}
}
const errors = missing.map(m => `Compiler dependency \`${m}\` not found.`);
const stack = this.compilers.filter(c => !targetFound(c));
while (stack.length > 0) {
const current = stack.pop();
for (const edge of edges) {
if (edge.source === current) {
edges.delete(edge);
const target = edge.target;
if (!targetFound(target)) {
stack.push(target);
}
}
}
}
if (edges.size > 0) {
const lines = Array.from(edges)
.sort(sortEdges)
.map(edge => `${edge.source.name} -> ${edge.target.name}`);
lines.unshift("Circular dependency found in compiler dependencies.");
errors.unshift(lines.join("\n"));
}
if (errors.length > 0) {
const message = errors.join("\n");
callback(new Error(message));
return false;
}
return true;
}
runWithDependencies(compilers, fn, callback) {
let fulfilledNames = {};
const fulfilledNames = new Set();
let remainingCompilers = compilers;
const isDependencyFulfilled = (d) => fulfilledNames[d];
const isDependencyFulfilled = d => fulfilledNames.has(d);
const getReadyCompilers = () => {
let readyCompilers = [];
let list = remainingCompilers;
remainingCompilers = [];
for(const c of list) {
const ready = !c.dependencies || c.dependencies.every(isDependencyFulfilled);
if(ready)
for (const c of list) {
const ready =
!c.dependencies || c.dependencies.every(isDependencyFulfilled);
if (ready) {
readyCompilers.push(c);
else
} else {
remainingCompilers.push(c);
}
}
return readyCompilers;
};
const runCompilers = (callback) => {
if(remainingCompilers.length === 0) return callback();
asyncLib.map(getReadyCompilers(), (compiler, callback) => {
fn(compiler, (err) => {
if(err) return callback(err);
fulfilledNames[compiler.name] = true;
runCompilers(callback);
});
}, callback);
const runCompilers = callback => {
if (remainingCompilers.length === 0) return callback();
asyncLib.map(
getReadyCompilers(),
(compiler, callback) => {
fn(compiler, err => {
if (err) return callback(err);
fulfilledNames.add(compiler.name);
runCompilers(callback);
});
},
callback
);
};
runCompilers(callback);
}
watch(watchOptions, handler) {
if (this.running) return handler(new ConcurrentCompilationError());
let watchings = [];
let allStats = this.compilers.map(() => null);
let compilerStatus = this.compilers.map(() => false);
this.runWithDependencies(this.compilers, (compiler, callback) => {
const compilerIdx = this.compilers.indexOf(compiler);
let firstRun = true;
let watching = compiler.watch(Array.isArray(watchOptions) ? watchOptions[compilerIdx] : watchOptions, (err, stats) => {
if(err)
handler(err);
if(stats) {
allStats[compilerIdx] = stats;
compilerStatus[compilerIdx] = "new";
if(compilerStatus.every(Boolean)) {
const freshStats = allStats.filter((s, idx) => {
return compilerStatus[idx] === "new";
});
compilerStatus.fill(true);
const multiStats = new MultiStats(freshStats);
handler(null, multiStats);
}
if (this.validateDependencies(handler)) {
this.running = true;
this.runWithDependencies(
this.compilers,
(compiler, callback) => {
const compilerIdx = this.compilers.indexOf(compiler);
let firstRun = true;
let watching = compiler.watch(
Array.isArray(watchOptions)
? watchOptions[compilerIdx]
: watchOptions,
(err, stats) => {
if (err) handler(err);
if (stats) {
allStats[compilerIdx] = stats;
compilerStatus[compilerIdx] = "new";
if (compilerStatus.every(Boolean)) {
const freshStats = allStats.filter((s, idx) => {
return compilerStatus[idx] === "new";
});
compilerStatus.fill(true);
const multiStats = new MultiStats(freshStats);
handler(null, multiStats);
}
}
if (firstRun && !err) {
firstRun = false;
callback();
}
}
);
watchings.push(watching);
},
() => {
// ignore
}
if(firstRun && !err) {
firstRun = false;
callback();
}
});
watchings.push(watching);
}, () => {
// ignore
});
);
}
return new MultiWatching(watchings, this);
}
run(callback) {
if (this.running) {
return callback(new ConcurrentCompilationError());
}
const finalCallback = (err, stats) => {
this.running = false;
if (callback !== undefined) {
return callback(err, stats);
}
};
const allStats = this.compilers.map(() => null);
this.runWithDependencies(this.compilers, ((compiler, callback) => {
const compilerIdx = this.compilers.indexOf(compiler);
compiler.run((err, stats) => {
if(err) return callback(err);
allStats[compilerIdx] = stats;
callback();
});
}), (err) => {
if(err) return callback(err);
callback(null, new MultiStats(allStats));
});
if (this.validateDependencies(callback)) {
this.running = true;
this.runWithDependencies(
this.compilers,
(compiler, callback) => {
const compilerIdx = this.compilers.indexOf(compiler);
compiler.run((err, stats) => {
if (err) {
return callback(err);
}
allStats[compilerIdx] = stats;
callback();
});
},
err => {
if (err) {
return finalCallback(err);
}
finalCallback(null, new MultiStats(allStats));
}
);
}
}
purgeInputFileSystem() {
this.compilers.forEach((compiler) => {
if(compiler.inputFileSystem && compiler.inputFileSystem.purge)
for (const compiler of this.compilers) {
if (compiler.inputFileSystem && compiler.inputFileSystem.purge) {
compiler.inputFileSystem.purge();
});
}
}
}
};

View File

@@ -8,32 +8,73 @@ const MultiEntryDependency = require("./dependencies/MultiEntryDependency");
const SingleEntryDependency = require("./dependencies/SingleEntryDependency");
const MultiModuleFactory = require("./MultiModuleFactory");
module.exports = class MultiEntryPlugin {
/** @typedef {import("./Compiler")} Compiler */
class MultiEntryPlugin {
/**
* The MultiEntryPlugin is invoked whenever this.options.entry value is an array of paths
* @param {string} context context path
* @param {string[]} entries array of entry paths
* @param {string} name entry key name
*/
constructor(context, entries, name) {
this.context = context;
this.entries = entries;
this.name = name;
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
const multiModuleFactory = new MultiModuleFactory();
const normalModuleFactory = params.normalModuleFactory;
compiler.hooks.compilation.tap(
"MultiEntryPlugin",
(compilation, { normalModuleFactory }) => {
const multiModuleFactory = new MultiModuleFactory();
compilation.dependencyFactories.set(MultiEntryDependency, multiModuleFactory);
compilation.dependencyFactories.set(SingleEntryDependency, normalModuleFactory);
});
compiler.plugin("make", (compilation, callback) => {
const dep = MultiEntryPlugin.createDependency(this.entries, this.name);
compilation.addEntry(this.context, dep, this.name, callback);
});
compilation.dependencyFactories.set(
MultiEntryDependency,
multiModuleFactory
);
compilation.dependencyFactories.set(
SingleEntryDependency,
normalModuleFactory
);
}
);
compiler.hooks.make.tapAsync(
"MultiEntryPlugin",
(compilation, callback) => {
const { context, entries, name } = this;
const dep = MultiEntryPlugin.createDependency(entries, name);
compilation.addEntry(context, dep, name, callback);
}
);
}
/**
* @param {string[]} entries each entry path string
* @param {string} name name of the entry
* @returns {MultiEntryDependency} returns a constructed Dependency
*/
static createDependency(entries, name) {
return new MultiEntryDependency(entries.map((e, idx) => {
const dep = new SingleEntryDependency(e);
dep.loc = name + ":" + (100000 + idx);
return dep;
}), name);
return new MultiEntryDependency(
entries.map((e, idx) => {
const dep = new SingleEntryDependency(e);
// Because entrypoints are not dependencies found in an
// existing module, we give it a synthetic id
dep.loc = {
name,
index: idx
};
return dep;
}),
name
);
}
};
}
module.exports = MultiEntryPlugin;

View File

@@ -5,34 +5,37 @@
"use strict";
const Module = require("./Module");
const RawSource = require("webpack-sources").RawSource;
const Template = require("./Template");
const { RawSource } = require("webpack-sources");
/** @typedef {import("./util/createHash").Hash} Hash */
class MultiModule extends Module {
constructor(context, dependencies, name) {
super();
this.context = context;
super("javascript/dynamic", context);
// Info from Factory
this.dependencies = dependencies;
this.name = name;
this.built = false;
this.cacheable = true;
this._identifier = `multi ${this.dependencies
.map(d => d.request)
.join(" ")}`;
}
identifier() {
return `multi ${this.dependencies.map((d) => d.request).join(" ")}`;
return this._identifier;
}
readableIdentifier(requestShortener) {
return `multi ${this.dependencies.map((d) => requestShortener.shorten(d.request)).join(" ")}`;
}
disconnect() {
this.built = false;
super.disconnect();
return `multi ${this.dependencies
.map(d => requestShortener.shorten(d.request))
.join(" ")}`;
}
build(options, compilation, resolver, fs, callback) {
this.built = true;
this.buildMeta = {};
this.buildInfo = {};
return callback();
}
@@ -44,30 +47,39 @@ class MultiModule extends Module {
return 16 + this.dependencies.length * 12;
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
hash.update("multi module");
hash.update(this.name || "");
super.updateHash(hash);
}
source(dependencyTemplates, outputOptions) {
source(dependencyTemplates, runtimeTemplate) {
const str = [];
this.dependencies.forEach(function(dep, idx) {
if(dep.module) {
if(idx === this.dependencies.length - 1)
let idx = 0;
for (const dep of this.dependencies) {
if (dep.module) {
if (idx === this.dependencies.length - 1) {
str.push("module.exports = ");
}
str.push("__webpack_require__(");
if(outputOptions.pathinfo)
str.push(`/*! ${dep.request} */`);
if (runtimeTemplate.outputOptions.pathinfo) {
str.push(Template.toComment(dep.request));
}
str.push(`${JSON.stringify(dep.module.id)}`);
str.push(")");
} else {
str.push("(function webpackMissingModule() { throw new Error(");
str.push(JSON.stringify(`Cannot find module "${dep.request}"`));
str.push("); }())");
const content = require("./dependencies/WebpackMissingModule").module(
dep.request
);
str.push(content);
}
str.push(";\n");
}, this);
idx++;
}
return new RawSource(str.join(""));
}
}

View File

@@ -4,16 +4,20 @@
*/
"use strict";
const Tapable = require("tapable");
const { Tapable } = require("tapable");
const MultiModule = require("./MultiModule");
module.exports = class MultiModuleFactory extends Tapable {
constructor() {
super();
this.hooks = {};
}
create(data, callback) {
const dependency = data.dependencies[0];
callback(null, new MultiModule(data.context, dependency.dependencies, dependency.name));
callback(
null,
new MultiModule(data.context, dependency.dependencies, dependency.name)
);
}
};

View File

@@ -6,7 +6,8 @@
const Stats = require("./Stats");
const optionOrFallback = (optionValue, fallbackValue) => optionValue !== undefined ? optionValue : fallbackValue;
const optionOrFallback = (optionValue, fallbackValue) =>
optionValue !== undefined ? optionValue : fallbackValue;
class MultiStats {
constructor(stats) {
@@ -15,17 +16,21 @@ class MultiStats {
}
hasErrors() {
return this.stats.map((stat) => stat.hasErrors()).reduce((a, b) => a || b, false);
return this.stats
.map(stat => stat.hasErrors())
.reduce((a, b) => a || b, false);
}
hasWarnings() {
return this.stats.map((stat) => stat.hasWarnings()).reduce((a, b) => a || b, false);
return this.stats
.map(stat => stat.hasWarnings())
.reduce((a, b) => a || b, false);
}
toJson(options, forToString) {
if(typeof options === "boolean" || typeof options === "string") {
if (typeof options === "boolean" || typeof options === "string") {
options = Stats.presetToOptions(options);
} else if(!options) {
} else if (!options) {
options = {};
}
const jsons = this.stats.map((stat, idx) => {
@@ -34,37 +39,45 @@ class MultiStats {
obj.name = stat.compilation && stat.compilation.name;
return obj;
});
const showVersion = typeof options.version === "undefined" ? jsons.every(j => j.version) : options.version !== false;
const showHash = typeof options.hash === "undefined" ? jsons.every(j => j.hash) : options.hash !== false;
jsons.forEach(j => {
if(showVersion)
const showVersion =
options.version === undefined
? jsons.every(j => j.version)
: options.version !== false;
const showHash =
options.hash === undefined
? jsons.every(j => j.hash)
: options.hash !== false;
if (showVersion) {
for (const j of jsons) {
delete j.version;
});
}
}
const obj = {
errors: jsons.reduce((arr, j) => {
return arr.concat(j.errors.map(msg => {
return `(${j.name}) ${msg}`;
}));
return arr.concat(
j.errors.map(msg => {
return `(${j.name}) ${msg}`;
})
);
}, []),
warnings: jsons.reduce((arr, j) => {
return arr.concat(j.warnings.map(msg => {
return `(${j.name}) ${msg}`;
}));
return arr.concat(
j.warnings.map(msg => {
return `(${j.name}) ${msg}`;
})
);
}, [])
};
if(showVersion)
obj.version = require("../package.json").version;
if(showHash)
obj.hash = this.hash;
if(options.children !== false)
obj.children = jsons;
if (showVersion) obj.version = require("../package.json").version;
if (showHash) obj.hash = this.hash;
if (options.children !== false) obj.children = jsons;
return obj;
}
toString(options) {
if(typeof options === "boolean" || typeof options === "string") {
if (typeof options === "boolean" || typeof options === "string") {
options = Stats.presetToOptions(options);
} else if(!options) {
} else if (!options) {
options = {};
}

View File

@@ -4,7 +4,7 @@
*/
"use strict";
const asyncLib = require("async");
const asyncLib = require("neo-async");
class MultiWatching {
constructor(watchings, compiler) {
@@ -13,19 +13,25 @@ class MultiWatching {
}
invalidate() {
this.watchings.forEach((watching) => watching.invalidate());
for (const watching of this.watchings) {
watching.invalidate();
}
}
close(callback) {
if(callback === undefined) callback = () => { /*do nothing*/ };
asyncLib.forEach(this.watchings, (watching, finishedCallback) => {
watching.close(finishedCallback);
}, err => {
this.compiler.applyPlugins("watch-close");
callback(err);
});
asyncLib.forEach(
this.watchings,
(watching, finishedCallback) => {
watching.close(finishedCallback);
},
err => {
this.compiler.hooks.watchClose.call();
if (typeof callback === "function") {
this.compiler.running = false;
callback(err);
}
}
);
}
}

View File

@@ -5,7 +5,6 @@
"use strict";
class NamedChunksPlugin {
static defaultNameResolver(chunk) {
return chunk.name || null;
}
@@ -15,13 +14,13 @@ class NamedChunksPlugin {
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("before-chunk-ids", (chunks) => {
chunks.forEach((chunk) => {
if(chunk.id === null) {
compiler.hooks.compilation.tap("NamedChunksPlugin", compilation => {
compilation.hooks.beforeChunkIds.tap("NamedChunksPlugin", chunks => {
for (const chunk of chunks) {
if (chunk.id === null) {
chunk.id = this.nameResolver(chunk);
}
});
}
});
});
}

View File

@@ -4,21 +4,51 @@
*/
"use strict";
const createHash = require("./util/createHash");
const RequestShortener = require("./RequestShortener");
const getHash = str => {
const hash = createHash("md4");
hash.update(str);
return hash.digest("hex").substr(0, 4);
};
class NamedModulesPlugin {
constructor(options) {
this.options = options || {};
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("before-module-ids", (modules) => {
modules.forEach((module) => {
if(module.id === null && module.libIdent) {
module.id = module.libIdent({
context: this.options.context || compiler.options.context
});
compiler.hooks.compilation.tap("NamedModulesPlugin", compilation => {
compilation.hooks.beforeModuleIds.tap("NamedModulesPlugin", modules => {
const namedModules = new Map();
const context = this.options.context || compiler.options.context;
for (const module of modules) {
if (module.id === null && module.libIdent) {
module.id = module.libIdent({ context });
}
});
if (module.id !== null) {
const namedModule = namedModules.get(module.id);
if (namedModule !== undefined) {
namedModule.push(module);
} else {
namedModules.set(module.id, [module]);
}
}
}
for (const namedModule of namedModules.values()) {
if (namedModule.length > 1) {
for (const module of namedModule) {
const requestShortener = new RequestShortener(context);
module.id = `${module.id}?${getHash(
requestShortener.shorten(module.identifier())
)}`;
}
}
}
});
});
}

View File

@@ -1,15 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class NewWatchingPlugin {
apply(compiler) {
compiler.plugin("compilation", function(compilation) {
compilation.warnings.push(new Error("The 'NewWatchingPlugin' is no longer necessary (now default)"));
});
}
}
module.exports = NewWatchingPlugin;

View File

@@ -6,14 +6,12 @@
class NoEmitOnErrorsPlugin {
apply(compiler) {
compiler.plugin("should-emit", (compilation) => {
if(compilation.getStats().hasErrors())
return false;
compiler.hooks.shouldEmit.tap("NoEmitOnErrorsPlugin", compilation => {
if (compilation.getStats().hasErrors()) return false;
});
compiler.plugin("compilation", (compilation) => {
compilation.plugin("should-record", () => {
if(compilation.getStats().hasErrors())
return false;
compiler.hooks.compilation.tap("NoEmitOnErrorsPlugin", compilation => {
compilation.hooks.shouldRecord.tap("NoEmitOnErrorsPlugin", () => {
if (compilation.getStats().hasErrors()) return false;
});
});
}

View File

@@ -1,29 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
let deprecationReported = false;
class NoErrorsPlugin {
apply(compiler) {
compiler.plugin("should-emit", (compilation) => {
if(!deprecationReported) {
compilation.warnings.push("webpack: Using NoErrorsPlugin is deprecated.\n" +
"Use NoEmitOnErrorsPlugin instead.\n");
deprecationReported = true;
}
if(compilation.errors.length > 0)
return false;
});
compiler.plugin("compilation", (compilation) => {
compilation.plugin("should-record", () => {
if(compilation.errors.length > 0)
return false;
});
});
}
}
module.exports = NoErrorsPlugin;

View File

@@ -17,81 +17,181 @@ class NodeStuffPlugin {
apply(compiler) {
const options = this.options;
compiler.plugin("compilation", (compilation, params) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(ConstDependency, new ConstDependency.Template());
params.normalModuleFactory.plugin("parser", (parser, parserOptions) => {
if(parserOptions.node === false)
return;
let localOptions = options;
if(parserOptions.node)
localOptions = Object.assign({}, localOptions, parserOptions.node);
function setConstant(expressionName, value) {
parser.plugin(`expression ${expressionName}`, function() {
this.state.current.addVariable(expressionName, JSON.stringify(value));
return true;
});
}
function setModuleConstant(expressionName, fn) {
parser.plugin(`expression ${expressionName}`, function() {
this.state.current.addVariable(expressionName, JSON.stringify(fn(this.state.module)));
return true;
});
}
const context = compiler.context;
if(localOptions.__filename === "mock") {
setConstant("__filename", "/index.js");
} else if(localOptions.__filename) {
setModuleConstant("__filename", module => path.relative(context, module.resource));
}
parser.plugin("evaluate Identifier __filename", function(expr) {
if(!this.state.module) return;
const resource = this.state.module.resource;
const i = resource.indexOf("?");
return ParserHelpers.evaluateToString(i < 0 ? resource : resource.substr(0, i))(expr);
});
if(localOptions.__dirname === "mock") {
setConstant("__dirname", "/");
} else if(localOptions.__dirname) {
setModuleConstant("__dirname", module => path.relative(context, module.context));
}
parser.plugin("evaluate Identifier __dirname", function(expr) {
if(!this.state.module) return;
return ParserHelpers.evaluateToString(this.state.module.context)(expr);
});
parser.plugin("expression require.main", ParserHelpers.toConstantDependency("__webpack_require__.c[__webpack_require__.s]"));
parser.plugin(
"expression require.extensions",
ParserHelpers.expressionIsUnsupported("require.extensions is not supported by webpack. Use a loader instead.")
compiler.hooks.compilation.tap(
"NodeStuffPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(ConstDependency, new NullFactory());
compilation.dependencyTemplates.set(
ConstDependency,
new ConstDependency.Template()
);
parser.plugin("expression module.loaded", ParserHelpers.toConstantDependency("module.l"));
parser.plugin("expression module.id", ParserHelpers.toConstantDependency("module.i"));
parser.plugin("expression module.exports", function() {
const module = this.state.module;
const isHarmony = module.meta && module.meta.harmonyModule;
if(!isHarmony)
return true;
});
parser.plugin("evaluate Identifier module.hot", ParserHelpers.evaluateToIdentifier("module.hot", false));
parser.plugin("expression module", function() {
const module = this.state.module;
const isHarmony = module.meta && module.meta.harmonyModule;
let moduleJsPath = path.join(__dirname, "..", "buildin", isHarmony ? "harmony-module.js" : "module.js");
if(module.context) {
moduleJsPath = path.relative(this.state.module.context, moduleJsPath);
if(!/^[A-Z]:/i.test(moduleJsPath)) {
moduleJsPath = `./${moduleJsPath.replace(/\\/g, "/")}`;
}
const handler = (parser, parserOptions) => {
if (parserOptions.node === false) return;
let localOptions = options;
if (parserOptions.node) {
localOptions = Object.assign({}, localOptions, parserOptions.node);
}
return ParserHelpers.addParsedVariableToModule(this, "module", `require(${JSON.stringify(moduleJsPath)})(module)`);
});
});
});
const setConstant = (expressionName, value) => {
parser.hooks.expression
.for(expressionName)
.tap("NodeStuffPlugin", () => {
parser.state.current.addVariable(
expressionName,
JSON.stringify(value)
);
return true;
});
};
const setModuleConstant = (expressionName, fn) => {
parser.hooks.expression
.for(expressionName)
.tap("NodeStuffPlugin", () => {
parser.state.current.addVariable(
expressionName,
JSON.stringify(fn(parser.state.module))
);
return true;
});
};
const context = compiler.context;
if (localOptions.__filename === "mock") {
setConstant("__filename", "/index.js");
} else if (localOptions.__filename) {
setModuleConstant("__filename", module =>
path.relative(context, module.resource)
);
}
parser.hooks.evaluateIdentifier
.for("__filename")
.tap("NodeStuffPlugin", expr => {
if (!parser.state.module) return;
const resource = parser.state.module.resource;
const i = resource.indexOf("?");
return ParserHelpers.evaluateToString(
i < 0 ? resource : resource.substr(0, i)
)(expr);
});
if (localOptions.__dirname === "mock") {
setConstant("__dirname", "/");
} else if (localOptions.__dirname) {
setModuleConstant("__dirname", module =>
path.relative(context, module.context)
);
}
parser.hooks.evaluateIdentifier
.for("__dirname")
.tap("NodeStuffPlugin", expr => {
if (!parser.state.module) return;
return ParserHelpers.evaluateToString(
parser.state.module.context
)(expr);
});
parser.hooks.expression
.for("require.main")
.tap(
"NodeStuffPlugin",
ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
"__webpack_require__.c[__webpack_require__.s]"
)
);
parser.hooks.expression
.for("require.extensions")
.tap(
"NodeStuffPlugin",
ParserHelpers.expressionIsUnsupported(
parser,
"require.extensions is not supported by webpack. Use a loader instead."
)
);
parser.hooks.expression
.for("require.main.require")
.tap(
"NodeStuffPlugin",
ParserHelpers.expressionIsUnsupported(
parser,
"require.main.require is not supported by webpack."
)
);
parser.hooks.expression
.for("module.parent.require")
.tap(
"NodeStuffPlugin",
ParserHelpers.expressionIsUnsupported(
parser,
"module.parent.require is not supported by webpack."
)
);
parser.hooks.expression
.for("module.loaded")
.tap("NodeStuffPlugin", expr => {
parser.state.module.buildMeta.moduleConcatenationBailout =
"module.loaded";
return ParserHelpers.toConstantDependency(parser, "module.l")(
expr
);
});
parser.hooks.expression
.for("module.id")
.tap("NodeStuffPlugin", expr => {
parser.state.module.buildMeta.moduleConcatenationBailout =
"module.id";
return ParserHelpers.toConstantDependency(parser, "module.i")(
expr
);
});
parser.hooks.expression
.for("module.exports")
.tap("NodeStuffPlugin", () => {
const module = parser.state.module;
const isHarmony =
module.buildMeta && module.buildMeta.exportsType;
if (!isHarmony) return true;
});
parser.hooks.evaluateIdentifier
.for("module.hot")
.tap(
"NodeStuffPlugin",
ParserHelpers.evaluateToIdentifier("module.hot", false)
);
parser.hooks.expression.for("module").tap("NodeStuffPlugin", () => {
const module = parser.state.module;
const isHarmony = module.buildMeta && module.buildMeta.exportsType;
let moduleJsPath = path.join(
__dirname,
"..",
"buildin",
isHarmony ? "harmony-module.js" : "module.js"
);
if (module.context) {
moduleJsPath = path.relative(
parser.state.module.context,
moduleJsPath
);
if (!/^[A-Z]:/i.test(moduleJsPath)) {
moduleJsPath = `./${moduleJsPath.replace(/\\/g, "/")}`;
}
}
return ParserHelpers.addParsedVariableToModule(
parser,
"module",
`require(${JSON.stringify(moduleJsPath)})(module)`
);
});
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("NodeStuffPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("NodeStuffPlugin", handler);
}
);
}
}
module.exports = NodeStuffPlugin;

View File

@@ -4,16 +4,16 @@
*/
"use strict";
const path = require("path");
const NativeModule = require("module");
const crypto = require("crypto");
const SourceMapSource = require("webpack-sources").SourceMapSource;
const OriginalSource = require("webpack-sources").OriginalSource;
const RawSource = require("webpack-sources").RawSource;
const ReplaceSource = require("webpack-sources").ReplaceSource;
const CachedSource = require("webpack-sources").CachedSource;
const LineToLineMappedSource = require("webpack-sources").LineToLineMappedSource;
const {
CachedSource,
LineToLineMappedSource,
OriginalSource,
RawSource,
SourceMapSource
} = require("webpack-sources");
const { getContext, runLoaders } = require("loader-runner");
const WebpackError = require("./WebpackError");
const Module = require("./Module");
@@ -21,28 +21,24 @@ const ModuleParseError = require("./ModuleParseError");
const ModuleBuildError = require("./ModuleBuildError");
const ModuleError = require("./ModuleError");
const ModuleWarning = require("./ModuleWarning");
const createHash = require("./util/createHash");
const contextify = require("./util/identifier").contextify;
const runLoaders = require("loader-runner").runLoaders;
const getContext = require("loader-runner").getContext;
/** @typedef {import("./util/createHash").Hash} Hash */
function asString(buf) {
if(Buffer.isBuffer(buf)) {
const asString = buf => {
if (Buffer.isBuffer(buf)) {
return buf.toString("utf-8");
}
return buf;
}
};
function contextify(context, request) {
return request.split("!").map(function(r) {
const splitPath = r.split("?");
splitPath[0] = path.relative(context, splitPath[0]);
if(path.sep === "\\")
splitPath[0] = splitPath[0].replace(/\\/g, "/");
if(splitPath[0].indexOf("../") !== 0)
splitPath[0] = "./" + splitPath[0];
return splitPath.join("?");
}).join("!");
}
const asBuffer = str => {
if (!Buffer.isBuffer(str)) {
return Buffer.from(str, "utf-8");
}
return str;
};
class NonErrorEmittedError extends WebpackError {
constructor(error) {
@@ -55,28 +51,54 @@ class NonErrorEmittedError extends WebpackError {
}
}
const dependencyTemplatesHashMap = new WeakMap();
/**
* @typedef {Object} CachedSourceEntry
* @property {TODO} source the generated source
* @property {string} hash the hash value
*/
class NormalModule extends Module {
constructor({
type,
request,
userRequest,
rawRequest,
loaders,
resource,
matchResource,
parser,
generator,
resolveOptions
}) {
super(type, getContext(resource));
constructor(request, userRequest, rawRequest, loaders, resource, parser) {
super();
// Info from Factory
this.request = request;
this.userRequest = userRequest;
this.rawRequest = rawRequest;
this.binary = type.startsWith("webassembly");
this.parser = parser;
this.generator = generator;
this.resource = resource;
this.context = getContext(resource);
this.matchResource = matchResource;
this.loaders = loaders;
this.fileDependencies = [];
this.contextDependencies = [];
this.warnings = [];
this.errors = [];
if (resolveOptions !== undefined) this.resolveOptions = resolveOptions;
// Info from Build
this.error = null;
this._source = null;
this.assets = {};
this.built = false;
this._cachedSource = null;
this._buildHash = "";
this.buildTimestamp = undefined;
/** @private @type {Map<string, CachedSourceEntry>} */
this._cachedSources = new Map();
// Options for the NormalModule set by plugins
// TODO refactor this -> options object filled from Factory
this.useSourceMap = false;
this.lineToLine = false;
// Cache
this._lastSuccessfulBuildMeta = {};
}
identifier() {
@@ -92,17 +114,31 @@ class NormalModule extends Module {
}
nameForCondition() {
const idx = this.resource.indexOf("?");
if(idx >= 0) return this.resource.substr(0, idx);
return this.resource;
const resource = this.matchResource || this.resource;
const idx = resource.indexOf("?");
if (idx >= 0) return resource.substr(0, idx);
return resource;
}
updateCacheModule(module) {
this.type = module.type;
this.request = module.request;
this.userRequest = module.userRequest;
this.rawRequest = module.rawRequest;
this.parser = module.parser;
this.generator = module.generator;
this.resource = module.resource;
this.matchResource = module.matchResource;
this.loaders = module.loaders;
this.resolveOptions = module.resolveOptions;
}
createSourceForAsset(name, content, sourceMap) {
if(!sourceMap) {
if (!sourceMap) {
return new RawSource(content);
}
if(typeof sourceMap === "string") {
if (typeof sourceMap === "string") {
return new OriginalSource(content, sourceMap);
}
@@ -110,125 +146,223 @@ class NormalModule extends Module {
}
createLoaderContext(resolver, options, compilation, fs) {
const requestShortener = compilation.runtimeTemplate.requestShortener;
const loaderContext = {
version: 2,
emitWarning: (warning) => {
if(!(warning instanceof Error))
emitWarning: warning => {
if (!(warning instanceof Error)) {
warning = new NonErrorEmittedError(warning);
this.warnings.push(new ModuleWarning(this, warning));
}
const currentLoader = this.getCurrentLoader(loaderContext);
this.warnings.push(
new ModuleWarning(this, warning, {
from: requestShortener.shorten(currentLoader.loader)
})
);
},
emitError: (error) => {
if(!(error instanceof Error))
emitError: error => {
if (!(error instanceof Error)) {
error = new NonErrorEmittedError(error);
this.errors.push(new ModuleError(this, error));
}
const currentLoader = this.getCurrentLoader(loaderContext);
this.errors.push(
new ModuleError(this, error, {
from: requestShortener.shorten(currentLoader.loader)
})
);
},
// TODO remove in webpack 5
exec: (code, filename) => {
// @ts-ignore Argument of type 'this' is not assignable to parameter of type 'Module'.
const module = new NativeModule(filename, this);
// @ts-ignore _nodeModulePaths is deprecated and undocumented Node.js API
module.paths = NativeModule._nodeModulePaths(this.context);
module.filename = filename;
module._compile(code, filename);
return module.exports;
},
resolve(context, request, callback) {
resolver.resolve({}, context, request, callback);
resolver.resolve({}, context, request, {}, callback);
},
resolveSync(context, request) {
return resolver.resolveSync({}, context, request);
getResolve(options) {
const child = options ? resolver.withOptions(options) : resolver;
return (context, request, callback) => {
if (callback) {
child.resolve({}, context, request, {}, callback);
} else {
return new Promise((resolve, reject) => {
child.resolve({}, context, request, {}, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
};
},
emitFile: (name, content, sourceMap) => {
this.assets[name] = this.createSourceForAsset(name, content, sourceMap);
if (!this.buildInfo.assets) {
this.buildInfo.assets = Object.create(null);
}
this.buildInfo.assets[name] = this.createSourceForAsset(
name,
content,
sourceMap
);
},
options: options,
rootContext: options.context,
webpack: true,
sourceMap: !!this.useSourceMap,
_module: this,
_compilation: compilation,
_compiler: compilation.compiler,
fs: fs,
fs: fs
};
compilation.applyPlugins("normal-module-loader", loaderContext, this);
if(options.loader)
compilation.hooks.normalModuleLoader.call(loaderContext, this);
if (options.loader) {
Object.assign(loaderContext, options.loader);
}
return loaderContext;
}
getCurrentLoader(loaderContext, index = loaderContext.loaderIndex) {
if (
this.loaders &&
this.loaders.length &&
index < this.loaders.length &&
index >= 0 &&
this.loaders[index]
) {
return this.loaders[index];
}
return null;
}
createSource(source, resourceBuffer, sourceMap) {
// if there is no identifier return raw source
if(!this.identifier) {
if (!this.identifier) {
return new RawSource(source);
}
// from here on we assume we have an identifier
const identifier = this.identifier();
if(this.lineToLine && resourceBuffer) {
if (this.lineToLine && resourceBuffer) {
return new LineToLineMappedSource(
source, identifier, asString(resourceBuffer));
source,
identifier,
asString(resourceBuffer)
);
}
if(this.useSourceMap && sourceMap) {
if (this.useSourceMap && sourceMap) {
return new SourceMapSource(source, identifier, sourceMap);
}
if (Buffer.isBuffer(source)) {
// @ts-ignore
// TODO We need to fix @types/webpack-sources to allow RawSource to take a Buffer | string
return new RawSource(source);
}
return new OriginalSource(source, identifier);
}
doBuild(options, compilation, resolver, fs, callback) {
this.cacheable = false;
const loaderContext = this.createLoaderContext(resolver, options, compilation, fs);
const loaderContext = this.createLoaderContext(
resolver,
options,
compilation,
fs
);
runLoaders({
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs)
}, (err, result) => {
if(result) {
this.cacheable = result.cacheable;
this.fileDependencies = result.fileDependencies;
this.contextDependencies = result.contextDependencies;
runLoaders(
{
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs)
},
(err, result) => {
if (result) {
this.buildInfo.cacheable = result.cacheable;
this.buildInfo.fileDependencies = new Set(result.fileDependencies);
this.buildInfo.contextDependencies = new Set(
result.contextDependencies
);
}
if (err) {
if (!(err instanceof Error)) {
err = new NonErrorEmittedError(err);
}
const currentLoader = this.getCurrentLoader(loaderContext);
const error = new ModuleBuildError(this, err, {
from:
currentLoader &&
compilation.runtimeTemplate.requestShortener.shorten(
currentLoader.loader
)
});
return callback(error);
}
const resourceBuffer = result.resourceBuffer;
const source = result.result[0];
const sourceMap = result.result.length >= 1 ? result.result[1] : null;
const extraInfo = result.result.length >= 2 ? result.result[2] : null;
if (!Buffer.isBuffer(source) && typeof source !== "string") {
const currentLoader = this.getCurrentLoader(loaderContext, 0);
const err = new Error(
`Final loader (${
currentLoader
? compilation.runtimeTemplate.requestShortener.shorten(
currentLoader.loader
)
: "unknown"
}) didn't return a Buffer or String`
);
const error = new ModuleBuildError(this, err);
return callback(error);
}
this._source = this.createSource(
this.binary ? asBuffer(source) : asString(source),
resourceBuffer,
sourceMap
);
this._ast =
typeof extraInfo === "object" &&
extraInfo !== null &&
extraInfo.webpackAST !== undefined
? extraInfo.webpackAST
: null;
return callback();
}
if(err) {
const error = new ModuleBuildError(this, err);
return callback(error);
}
const resourceBuffer = result.resourceBuffer;
const source = result.result[0];
const sourceMap = result.result[1];
if(!Buffer.isBuffer(source) && typeof source !== "string") {
const error = new ModuleBuildError(this, new Error("Final loader didn't return a Buffer or String"));
return callback(error);
}
this._source = this.createSource(asString(source), resourceBuffer, sourceMap);
return callback();
});
}
disconnect() {
this.built = false;
super.disconnect();
);
}
markModuleAsErrored(error) {
this.meta = null;
// Restore build meta from successful build to keep importing state
this.buildMeta = Object.assign({}, this._lastSuccessfulBuildMeta);
this.error = error;
this.errors.push(this.error);
this._source = new RawSource("throw new Error(" + JSON.stringify(this.error.message) + ");");
this._source = new RawSource(
"throw new Error(" + JSON.stringify(this.error.message) + ");"
);
this._ast = null;
}
applyNoParseRule(rule, content) {
// must start with "rule" if rule is a string
if(typeof rule === "string") {
if (typeof rule === "string") {
return content.indexOf(rule) === 0;
}
if(typeof rule === "function") {
if (typeof rule === "function") {
return rule(content);
}
// we assume rule is a regexp
@@ -241,21 +375,21 @@ class NormalModule extends Module {
shouldPreventParsing(noParseRule, request) {
// if no noParseRule exists, return false
// the module !must! be parsed.
if(!noParseRule) {
if (!noParseRule) {
return false;
}
// we only have one rule to check
if(!Array.isArray(noParseRule)) {
if (!Array.isArray(noParseRule)) {
// returns "true" if the module is !not! to be parsed
return this.applyNoParseRule(noParseRule, request);
}
for(let i = 0; i < noParseRule.length; i++) {
for (let i = 0; i < noParseRule.length; i++) {
const rule = noParseRule[i];
// early exit on first truthy match
// this module is !not! to be parsed
if(this.applyNoParseRule(rule, request)) {
if (this.applyNoParseRule(rule, request)) {
return true;
}
}
@@ -263,294 +397,161 @@ class NormalModule extends Module {
return false;
}
_initBuildHash(compilation) {
const hash = createHash(compilation.outputOptions.hashFunction);
if (this._source) {
hash.update("source");
this._source.updateHash(hash);
}
hash.update("meta");
hash.update(JSON.stringify(this.buildMeta));
this._buildHash = hash.digest("hex");
}
build(options, compilation, resolver, fs, callback) {
this.buildTimestamp = Date.now();
this.built = true;
this._source = null;
this._ast = null;
this._buildHash = "";
this.error = null;
this.errors.length = 0;
this.warnings.length = 0;
this.meta = {};
this.buildMeta = {};
this.buildInfo = {
cacheable: false,
fileDependencies: new Set(),
contextDependencies: new Set()
};
return this.doBuild(options, compilation, resolver, fs, (err) => {
this.dependencies.length = 0;
this.variables.length = 0;
this.blocks.length = 0;
this._cachedSource = null;
return this.doBuild(options, compilation, resolver, fs, err => {
this._cachedSources.clear();
// if we have an error mark module as failed and exit
if(err) {
if (err) {
this.markModuleAsErrored(err);
this._initBuildHash(compilation);
return callback();
}
// check if this module should !not! be parsed.
// if so, exit here;
const noParseRule = options.module && options.module.noParse;
if(this.shouldPreventParsing(noParseRule, this.request)) {
if (this.shouldPreventParsing(noParseRule, this.request)) {
this._initBuildHash(compilation);
return callback();
}
try {
this.parser.parse(this._source.source(), {
current: this,
module: this,
compilation: compilation,
options: options
});
} catch(e) {
const handleParseError = e => {
const source = this._source.source();
const error = new ModuleParseError(this, source, e);
this.markModuleAsErrored(error);
this._initBuildHash(compilation);
return callback();
};
const handleParseResult = result => {
this._lastSuccessfulBuildMeta = this.buildMeta;
this._initBuildHash(compilation);
return callback();
};
try {
const result = this.parser.parse(
this._ast || this._source.source(),
{
current: this,
module: this,
compilation: compilation,
options: options
},
(err, result) => {
if (err) {
handleParseError(err);
} else {
handleParseResult(result);
}
}
);
if (result !== undefined) {
// parse is sync
handleParseResult(result);
}
} catch (e) {
handleParseError(e);
}
return callback();
});
}
getHashDigest(dependencyTemplates) {
let dtHash = dependencyTemplatesHashMap.get("hash");
const hash = crypto.createHash("md5");
this.updateHash(hash);
hash.update(`${dtHash}`);
return hash.digest("hex");
// TODO webpack 5 refactor
let dtHash = dependencyTemplates.get("hash");
return `${this.hash}-${dtHash}`;
}
sourceDependency(dependency, dependencyTemplates, source, outputOptions, requestShortener) {
const template = dependencyTemplates.get(dependency.constructor);
if(!template) throw new Error("No template for dependency: " + dependency.constructor.name);
template.apply(dependency, source, outputOptions, requestShortener, dependencyTemplates);
}
sourceVariables(variable, availableVars, dependencyTemplates, outputOptions, requestShortener) {
const name = variable.name;
const expr = variable.expressionSource(dependencyTemplates, outputOptions, requestShortener);
if(availableVars.some(v => v.name === name && v.expression.source() === expr.source())) {
return;
}
return {
name: name,
expression: expr
};
}
/*
* creates the start part of a IIFE around the module to inject a variable name
* (function(...){ <- this part
* }.call(...))
*/
variableInjectionFunctionWrapperStartCode(varNames) {
const args = varNames.join(", ");
return `/* WEBPACK VAR INJECTION */(function(${args}) {`;
}
contextArgument(block) {
if(this === block) {
return this.exportsArgument || "exports";
}
return "this";
}
/*
* creates the end part of a IIFE around the module to inject a variable name
* (function(...){
* }.call(...)) <- this part
*/
variableInjectionFunctionWrapperEndCode(varExpressions, block) {
const firstParam = this.contextArgument(block);
const furtherParams = varExpressions.map(e => e.source()).join(", ");
return `}.call(${firstParam}, ${furtherParams}))`;
}
splitVariablesInUniqueNamedChunks(vars) {
const startState = [
[]
];
return vars.reduce((chunks, variable) => {
const current = chunks[chunks.length - 1];
// check if variable with same name exists already
// if so create a new chunk of variables.
const variableNameAlreadyExists = current.some(v => v.name === variable.name);
if(variableNameAlreadyExists) {
// start new chunk with current variable
chunks.push([variable]);
} else {
// else add it to current chunk
current.push(variable);
}
return chunks;
}, startState);
}
sourceBlock(block, availableVars, dependencyTemplates, source, outputOptions, requestShortener) {
block.dependencies.forEach((dependency) => this.sourceDependency(
dependency, dependencyTemplates, source, outputOptions, requestShortener));
/**
* Get the variables of all blocks that we need to inject.
* These will contain the variable name and its expression.
* The name will be added as a paramter in a IIFE the expression as its value.
*/
const vars = block.variables.reduce((result, value) => {
const variable = this.sourceVariables(
value, availableVars, dependencyTemplates, outputOptions, requestShortener);
if(variable) {
result.push(variable);
}
return result;
}, []);
/**
* if we actually have variables
* this is important as how #splitVariablesInUniqueNamedChunks works
* it will always return an array in an array which would lead to a IIFE wrapper around
* a module if we do this with an empty vars array.
*/
if(vars.length > 0) {
/**
* Split all variables up into chunks of unique names.
* e.g. imagine you have the following variable names that need to be injected:
* [foo, bar, baz, foo, some, more]
* we can not inject "foo" twice, therefore we just make two IIFEs like so:
* (function(foo, bar, baz){
* (function(foo, some, more){
* ...
* }(...));
* }(...));
*
* "splitVariablesInUniqueNamedChunks" splits the variables shown above up to this:
* [[foo, bar, baz], [foo, some, more]]
*/
const injectionVariableChunks = this.splitVariablesInUniqueNamedChunks(vars);
// create all the beginnings of IIFEs
const functionWrapperStarts = injectionVariableChunks.map((variableChunk) => {
return this.variableInjectionFunctionWrapperStartCode(
variableChunk.map(variable => variable.name)
);
});
// and all the ends
const functionWrapperEnds = injectionVariableChunks.map((variableChunk) => {
return this.variableInjectionFunctionWrapperEndCode(
variableChunk.map(variable => variable.expression), block
);
});
// join them to one big string
const varStartCode = functionWrapperStarts.join("");
// reverse the ends first before joining them, as the last added must be the inner most
const varEndCode = functionWrapperEnds.reverse().join("");
// if we have anything, add it to the source
if(varStartCode && varEndCode) {
const start = block.range ? block.range[0] : -10;
const end = block.range ? block.range[1] : (this._source.size() + 1);
source.insert(start + 0.5, varStartCode);
source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode);
}
}
block.blocks.forEach((block) =>
this.sourceBlock(
block,
availableVars.concat(vars),
dependencyTemplates,
source,
outputOptions,
requestShortener
)
);
}
source(dependencyTemplates, outputOptions, requestShortener) {
source(dependencyTemplates, runtimeTemplate, type = "javascript") {
const hashDigest = this.getHashDigest(dependencyTemplates);
if(this._cachedSource && this._cachedSource.hash === hashDigest) {
return this._cachedSource.source;
const cacheEntry = this._cachedSources.get(type);
if (cacheEntry !== undefined && cacheEntry.hash === hashDigest) {
// We can reuse the cached source
return cacheEntry.source;
}
if(!this._source) {
return new RawSource("throw new Error('No source available');");
}
const source = this.generator.generate(
this,
dependencyTemplates,
runtimeTemplate,
type
);
const source = new ReplaceSource(this._source);
this._cachedSource = {
source: source,
const cachedSource = new CachedSource(source);
this._cachedSources.set(type, {
source: cachedSource,
hash: hashDigest
};
this.sourceBlock(this, [], dependencyTemplates, source, outputOptions, requestShortener);
return new CachedSource(source);
});
return cachedSource;
}
originalSource() {
return this._source;
}
getHighestTimestamp(keys, timestampsByKey) {
let highestTimestamp = 0;
for(let i = 0; i < keys.length; i++) {
const key = keys[i];
const timestamp = timestampsByKey[key];
// if there is no timestamp yet, early return with Infinity
if(!timestamp) return Infinity;
highestTimestamp = Math.max(highestTimestamp, timestamp);
}
return highestTimestamp;
}
needRebuild(fileTimestamps, contextTimestamps) {
const highestFileDepTimestamp = this.getHighestTimestamp(
this.fileDependencies, fileTimestamps);
// if the hightest is Infinity, we need a rebuild
// exit early here.
if(highestFileDepTimestamp === Infinity) {
return true;
// always try to rebuild in case of an error
if (this.error) return true;
// always rebuild when module is not cacheable
if (!this.buildInfo.cacheable) return true;
// Check timestamps of all dependencies
// Missing timestamp -> need rebuild
// Timestamp bigger than buildTimestamp -> need rebuild
for (const file of this.buildInfo.fileDependencies) {
const timestamp = fileTimestamps.get(file);
if (!timestamp) return true;
if (timestamp >= this.buildTimestamp) return true;
}
const highestContextDepTimestamp = this.getHighestTimestamp(
this.contextDependencies, contextTimestamps);
// Again if the hightest is Infinity, we need a rebuild
// exit early here.
if(highestContextDepTimestamp === Infinity) {
return true;
for (const file of this.buildInfo.contextDependencies) {
const timestamp = contextTimestamps.get(file);
if (!timestamp) return true;
if (timestamp >= this.buildTimestamp) return true;
}
// else take the highest of file and context timestamps and compare
// to last build timestamp
return Math.max(highestContextDepTimestamp, highestFileDepTimestamp) >= this.buildTimestamp;
// elsewise -> no rebuild needed
return false;
}
size() {
return this._source ? this._source.size() : -1;
}
updateHashWithSource(hash) {
if(!this._source) {
hash.update("null");
return;
}
hash.update("source");
this._source.updateHash(hash);
}
updateHashWithMeta(hash) {
hash.update("meta");
hash.update(JSON.stringify(this.meta));
}
/**
* @param {Hash} hash the hash used to track dependencies
* @returns {void}
*/
updateHash(hash) {
this.updateHashWithSource(hash);
this.updateHashWithMeta(hash);
hash.update(this._buildHash);
super.updateHash(hash);
}
}
module.exports = NormalModule;

View File

@@ -4,291 +4,523 @@
*/
"use strict";
const asyncLib = require("async");
const Tapable = require("tapable");
const path = require("path");
const asyncLib = require("neo-async");
const {
Tapable,
AsyncSeriesWaterfallHook,
SyncWaterfallHook,
SyncBailHook,
SyncHook,
HookMap
} = require("tapable");
const NormalModule = require("./NormalModule");
const RawModule = require("./RawModule");
const Parser = require("./Parser");
const RuleSet = require("./RuleSet");
const cachedMerge = require("./util/cachedMerge");
function loaderToIdent(data) {
if(!data.options)
const EMPTY_RESOLVE_OPTIONS = {};
const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
const loaderToIdent = data => {
if (!data.options) {
return data.loader;
if(typeof data.options === "string")
}
if (typeof data.options === "string") {
return data.loader + "?" + data.options;
if(typeof data.options !== "object")
}
if (typeof data.options !== "object") {
throw new Error("loader options must be string or object");
if(data.ident)
}
if (data.ident) {
return data.loader + "??" + data.ident;
}
return data.loader + "?" + JSON.stringify(data.options);
}
};
function identToLoaderRequest(resultString) {
const identToLoaderRequest = resultString => {
const idx = resultString.indexOf("?");
let options;
if(idx >= 0) {
options = resultString.substr(idx + 1);
resultString = resultString.substr(0, idx);
if (idx >= 0) {
const loader = resultString.substr(0, idx);
const options = resultString.substr(idx + 1);
return {
loader: resultString,
loader,
options
};
} else {
return {
loader: resultString
loader: resultString,
options: undefined
};
}
}
};
const dependencyCache = new WeakMap();
class NormalModuleFactory extends Tapable {
constructor(context, resolvers, options) {
constructor(context, resolverFactory, options) {
super();
this.resolvers = resolvers;
this.ruleSet = new RuleSet(options.rules || options.loaders);
this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);
this.hooks = {
resolver: new SyncWaterfallHook(["resolver"]),
factory: new SyncWaterfallHook(["factory"]),
beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
afterResolve: new AsyncSeriesWaterfallHook(["data"]),
createModule: new SyncBailHook(["data"]),
module: new SyncWaterfallHook(["module", "data"]),
createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
createGenerator: new HookMap(
() => new SyncBailHook(["generatorOptions"])
),
generator: new HookMap(
() => new SyncHook(["generator", "generatorOptions"])
)
};
this._pluginCompat.tap("NormalModuleFactory", options => {
switch (options.name) {
case "before-resolve":
case "after-resolve":
options.async = true;
break;
case "parser":
this.hooks.parser
.for("javascript/auto")
.tap(options.fn.name || "unnamed compat plugin", options.fn);
return true;
}
let match;
match = /^parser (.+)$/.exec(options.name);
if (match) {
this.hooks.parser
.for(match[1])
.tap(
options.fn.name || "unnamed compat plugin",
options.fn.bind(this)
);
return true;
}
match = /^create-parser (.+)$/.exec(options.name);
if (match) {
this.hooks.createParser
.for(match[1])
.tap(
options.fn.name || "unnamed compat plugin",
options.fn.bind(this)
);
return true;
}
});
this.resolverFactory = resolverFactory;
this.ruleSet = new RuleSet(options.defaultRules.concat(options.rules));
this.cachePredicate =
typeof options.unsafeCache === "function"
? options.unsafeCache
: Boolean.bind(null, options.unsafeCache);
this.context = context || "";
this.parserCache = {};
this.plugin("factory", () => (result, callback) => {
let resolver = this.applyPluginsWaterfall0("resolver", null);
this.parserCache = Object.create(null);
this.generatorCache = Object.create(null);
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
let resolver = this.hooks.resolver.call(null);
// Ignored
if(!resolver) return callback();
if (!resolver) return callback();
resolver(result, (err, data) => {
if(err) return callback(err);
if (err) return callback(err);
// Ignored
if(!data) return callback();
if (!data) return callback();
// direct module
if(typeof data.source === "function")
return callback(null, data);
if (typeof data.source === "function") return callback(null, data);
this.applyPluginsAsyncWaterfall("after-resolve", data, (err, result) => {
if(err) return callback(err);
this.hooks.afterResolve.callAsync(data, (err, result) => {
if (err) return callback(err);
// Ignored
if(!result) return callback();
if (!result) return callback();
let createdModule = this.applyPluginsBailResult("create-module", result);
if(!createdModule) {
if(!result.request) {
let createdModule = this.hooks.createModule.call(result);
if (!createdModule) {
if (!result.request) {
return callback(new Error("Empty dependency (no request)"));
}
createdModule = new NormalModule(
result.request,
result.userRequest,
result.rawRequest,
result.loaders,
result.resource,
result.parser
);
createdModule = new NormalModule(result);
}
createdModule = this.applyPluginsWaterfall0("module", createdModule);
createdModule = this.hooks.module.call(createdModule, result);
return callback(null, createdModule);
});
});
});
this.plugin("resolver", () => (data, callback) => {
this.hooks.resolver.tap("NormalModuleFactory", () => (data, callback) => {
const contextInfo = data.contextInfo;
const context = data.context;
const request = data.request;
const noAutoLoaders = /^-?!/.test(request);
const noPrePostAutoLoaders = /^!!/.test(request);
const noPostAutoLoaders = /^-!/.test(request);
let elements = request.replace(/^-?!+/, "").replace(/!!+/g, "!").split("!");
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver("normal", data.resolveOptions);
let matchResource = undefined;
let requestWithoutMatchResource = request;
const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
if (matchResourceMatch) {
matchResource = matchResourceMatch[1];
if (/^\.\.?\//.test(matchResource)) {
matchResource = path.join(context, matchResource);
}
requestWithoutMatchResource = request.substr(
matchResourceMatch[0].length
);
}
const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!");
const noAutoLoaders =
noPreAutoLoaders || requestWithoutMatchResource.startsWith("!");
const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith("!!");
let elements = requestWithoutMatchResource
.replace(/^-?!+/, "")
.replace(/!!+/g, "!")
.split("!");
let resource = elements.pop();
elements = elements.map(identToLoaderRequest);
asyncLib.parallel([
callback => this.resolveRequestArray(contextInfo, context, elements, this.resolvers.loader, callback),
callback => {
if(resource === "" || resource[0] === "?")
return callback(null, {
resource
});
this.resolvers.normal.resolve(contextInfo, context, resource, (err, resource, resourceResolveData) => {
if(err) return callback(err);
callback(null, {
resourceResolveData,
resource
});
});
}
], (err, results) => {
if(err) return callback(err);
let loaders = results[0];
const resourceResolveData = results[1].resourceResolveData;
resource = results[1].resource;
// translate option idents
try {
loaders.forEach(item => {
if(typeof item.options === "string" && /^\?/.test(item.options)) {
const ident = item.options.substr(1);
item.options = this.ruleSet.findOptionsByIdent(ident);
item.ident = ident;
asyncLib.parallel(
[
callback =>
this.resolveRequestArray(
contextInfo,
context,
elements,
loaderResolver,
callback
),
callback => {
if (resource === "" || resource[0] === "?") {
return callback(null, {
resource
});
}
});
} catch(e) {
return callback(e);
}
if(resource === false) {
// ignored
return callback(null,
new RawModule(
"/* (ignored) */",
`ignored ${context} ${request}`,
`${request} (ignored)`
)
normalResolver.resolve(
contextInfo,
context,
resource,
{},
(err, resource, resourceResolveData) => {
if (err) return callback(err);
callback(null, {
resourceResolveData,
resource
});
}
);
}
],
(err, results) => {
if (err) return callback(err);
let loaders = results[0];
const resourceResolveData = results[1].resourceResolveData;
resource = results[1].resource;
// translate option idents
try {
for (const item of loaders) {
if (typeof item.options === "string" && item.options[0] === "?") {
const ident = item.options.substr(1);
item.options = this.ruleSet.findOptionsByIdent(ident);
item.ident = ident;
}
}
} catch (e) {
return callback(e);
}
if (resource === false) {
// ignored
return callback(
null,
new RawModule(
"/* (ignored) */",
`ignored ${context} ${request}`,
`${request} (ignored)`
)
);
}
const userRequest =
(matchResource !== undefined ? `${matchResource}!=!` : "") +
loaders
.map(loaderToIdent)
.concat([resource])
.join("!");
let resourcePath =
matchResource !== undefined ? matchResource : resource;
let resourceQuery = "";
const queryIndex = resourcePath.indexOf("?");
if (queryIndex >= 0) {
resourceQuery = resourcePath.substr(queryIndex);
resourcePath = resourcePath.substr(0, queryIndex);
}
const result = this.ruleSet.exec({
resource: resourcePath,
realResource:
matchResource !== undefined
? resource.replace(/\?.*/, "")
: resourcePath,
resourceQuery,
issuer: contextInfo.issuer,
compiler: contextInfo.compiler
});
const settings = {};
const useLoadersPost = [];
const useLoaders = [];
const useLoadersPre = [];
for (const r of result) {
if (r.type === "use") {
if (r.enforce === "post" && !noPrePostAutoLoaders) {
useLoadersPost.push(r.value);
} else if (
r.enforce === "pre" &&
!noPreAutoLoaders &&
!noPrePostAutoLoaders
) {
useLoadersPre.push(r.value);
} else if (
!r.enforce &&
!noAutoLoaders &&
!noPrePostAutoLoaders
) {
useLoaders.push(r.value);
}
} else if (
typeof r.value === "object" &&
r.value !== null &&
typeof settings[r.type] === "object" &&
settings[r.type] !== null
) {
settings[r.type] = cachedMerge(settings[r.type], r.value);
} else {
settings[r.type] = r.value;
}
}
asyncLib.parallel(
[
this.resolveRequestArray.bind(
this,
contextInfo,
this.context,
useLoadersPost,
loaderResolver
),
this.resolveRequestArray.bind(
this,
contextInfo,
this.context,
useLoaders,
loaderResolver
),
this.resolveRequestArray.bind(
this,
contextInfo,
this.context,
useLoadersPre,
loaderResolver
)
],
(err, results) => {
if (err) return callback(err);
loaders = results[0].concat(loaders, results[1], results[2]);
process.nextTick(() => {
const type = settings.type;
const resolveOptions = settings.resolve;
callback(null, {
context: context,
request: loaders
.map(loaderToIdent)
.concat([resource])
.join("!"),
dependencies: data.dependencies,
userRequest,
rawRequest: request,
loaders,
resource,
matchResource,
resourceResolveData,
settings,
type,
parser: this.getParser(type, settings.parser),
generator: this.getGenerator(type, settings.generator),
resolveOptions
});
});
}
);
}
const userRequest = loaders.map(loaderToIdent).concat([resource]).join("!");
let resourcePath = resource;
let resourceQuery = "";
const queryIndex = resourcePath.indexOf("?");
if(queryIndex >= 0) {
resourceQuery = resourcePath.substr(queryIndex);
resourcePath = resourcePath.substr(0, queryIndex);
}
const result = this.ruleSet.exec({
resource: resourcePath,
resourceQuery,
issuer: contextInfo.issuer,
compiler: contextInfo.compiler
});
const settings = {};
const useLoadersPost = [];
const useLoaders = [];
const useLoadersPre = [];
result.forEach(r => {
if(r.type === "use") {
if(r.enforce === "post" && !noPostAutoLoaders && !noPrePostAutoLoaders)
useLoadersPost.push(r.value);
else if(r.enforce === "pre" && !noPrePostAutoLoaders)
useLoadersPre.push(r.value);
else if(!r.enforce && !noAutoLoaders && !noPrePostAutoLoaders)
useLoaders.push(r.value);
} else {
settings[r.type] = r.value;
}
});
asyncLib.parallel([
this.resolveRequestArray.bind(this, contextInfo, this.context, useLoadersPost, this.resolvers.loader),
this.resolveRequestArray.bind(this, contextInfo, this.context, useLoaders, this.resolvers.loader),
this.resolveRequestArray.bind(this, contextInfo, this.context, useLoadersPre, this.resolvers.loader)
], (err, results) => {
if(err) return callback(err);
loaders = results[0].concat(loaders, results[1], results[2]);
process.nextTick(() => {
callback(null, {
context: context,
request: loaders.map(loaderToIdent).concat([resource]).join("!"),
dependencies: data.dependencies,
userRequest,
rawRequest: request,
loaders,
resource,
resourceResolveData,
parser: this.getParser(settings.parser)
});
});
});
});
);
});
}
create(data, callback) {
const dependencies = data.dependencies;
const cacheEntry = dependencies[0].__NormalModuleFactoryCache;
if(cacheEntry) return callback(null, cacheEntry);
const cacheEntry = dependencyCache.get(dependencies[0]);
if (cacheEntry) return callback(null, cacheEntry);
const context = data.context || this.context;
const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
const request = dependencies[0].request;
const contextInfo = data.contextInfo || {};
this.applyPluginsAsyncWaterfall("before-resolve", {
contextInfo,
context,
request,
dependencies
}, (err, result) => {
if(err) return callback(err);
this.hooks.beforeResolve.callAsync(
{
contextInfo,
resolveOptions,
context,
request,
dependencies
},
(err, result) => {
if (err) return callback(err);
// Ignored
if(!result) return callback();
// Ignored
if (!result) return callback();
const factory = this.applyPluginsWaterfall0("factory", null);
const factory = this.hooks.factory.call(null);
// Ignored
if(!factory) return callback();
// Ignored
if (!factory) return callback();
factory(result, (err, module) => {
if(err) return callback(err);
factory(result, (err, module) => {
if (err) return callback(err);
if(module && this.cachePredicate(module)) {
dependencies.forEach(d => d.__NormalModuleFactoryCache = module);
}
if (module && this.cachePredicate(module)) {
for (const d of dependencies) {
dependencyCache.set(d, module);
}
}
callback(null, module);
});
});
callback(null, module);
});
}
);
}
resolveRequestArray(contextInfo, context, array, resolver, callback) {
if(array.length === 0) return callback(null, []);
asyncLib.map(array, (item, callback) => {
resolver.resolve(contextInfo, context, item.loader, (err, result) => {
if(err && /^[^/]*$/.test(item.loader) && !/-loader$/.test(item.loader)) {
return resolver.resolve(contextInfo, context, item.loader + "-loader", err2 => {
if(!err2) {
err.message = err.message + "\n" +
"BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
` You need to specify '${item.loader}-loader' instead of '${item.loader}',\n` +
" see https://webpack.js.org/guides/migrating/#automatic-loader-module-name-extension-removed";
if (array.length === 0) return callback(null, []);
asyncLib.map(
array,
(item, callback) => {
resolver.resolve(
contextInfo,
context,
item.loader,
{},
(err, result) => {
if (
err &&
/^[^/]*$/.test(item.loader) &&
!/-loader$/.test(item.loader)
) {
return resolver.resolve(
contextInfo,
context,
item.loader + "-loader",
{},
err2 => {
if (!err2) {
err.message =
err.message +
"\n" +
"BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
` You need to specify '${
item.loader
}-loader' instead of '${item.loader}',\n` +
" see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
}
callback(err);
}
);
}
callback(err);
});
}
if(err) return callback(err);
if (err) return callback(err);
const optionsOnly = item.options ? {
options: item.options
} : undefined;
return callback(null, Object.assign({}, item, identToLoaderRequest(result), optionsOnly));
});
}, callback);
const optionsOnly = item.options
? {
options: item.options
}
: undefined;
return callback(
null,
Object.assign({}, item, identToLoaderRequest(result), optionsOnly)
);
}
);
},
callback
);
}
getParser(parserOptions) {
let ident = "null";
if(parserOptions) {
if(parserOptions.ident)
ident = parserOptions.ident;
else
ident = JSON.stringify(parserOptions);
getParser(type, parserOptions) {
let ident = type;
if (parserOptions) {
if (parserOptions.ident) {
ident = `${type}|${parserOptions.ident}`;
} else {
ident = JSON.stringify([type, parserOptions]);
}
}
const parser = this.parserCache[ident];
if(parser)
return parser;
return this.parserCache[ident] = this.createParser(parserOptions);
if (ident in this.parserCache) {
return this.parserCache[ident];
}
return (this.parserCache[ident] = this.createParser(type, parserOptions));
}
createParser(parserOptions) {
const parser = new Parser();
this.applyPlugins2("parser", parser, parserOptions || {});
createParser(type, parserOptions = {}) {
const parser = this.hooks.createParser.for(type).call(parserOptions);
if (!parser) {
throw new Error(`No parser registered for ${type}`);
}
this.hooks.parser.for(type).call(parser, parserOptions);
return parser;
}
getGenerator(type, generatorOptions) {
let ident = type;
if (generatorOptions) {
if (generatorOptions.ident) {
ident = `${type}|${generatorOptions.ident}`;
} else {
ident = JSON.stringify([type, generatorOptions]);
}
}
if (ident in this.generatorCache) {
return this.generatorCache[ident];
}
return (this.generatorCache[ident] = this.createGenerator(
type,
generatorOptions
));
}
createGenerator(type, generatorOptions = {}) {
const generator = this.hooks.createGenerator
.for(type)
.call(generatorOptions);
if (!generator) {
throw new Error(`No generator registered for ${type}`);
}
this.hooks.generator.for(type).call(generator, generatorOptions);
return generator;
}
getResolver(type, resolveOptions) {
return this.resolverFactory.get(
type,
resolveOptions || EMPTY_RESOLVE_OPTIONS
);
}
}
module.exports = NormalModuleFactory;

View File

@@ -15,30 +15,36 @@ class NormalModuleReplacementPlugin {
apply(compiler) {
const resourceRegExp = this.resourceRegExp;
const newResource = this.newResource;
compiler.plugin("normal-module-factory", (nmf) => {
nmf.plugin("before-resolve", (result, callback) => {
if(!result) return callback();
if(resourceRegExp.test(result.request)) {
if(typeof newResource === "function") {
newResource(result);
} else {
result.request = newResource;
compiler.hooks.normalModuleFactory.tap(
"NormalModuleReplacementPlugin",
nmf => {
nmf.hooks.beforeResolve.tap("NormalModuleReplacementPlugin", result => {
if (!result) return;
if (resourceRegExp.test(result.request)) {
if (typeof newResource === "function") {
newResource(result);
} else {
result.request = newResource;
}
}
}
return callback(null, result);
});
nmf.plugin("after-resolve", (result, callback) => {
if(!result) return callback();
if(resourceRegExp.test(result.resource)) {
if(typeof newResource === "function") {
newResource(result);
} else {
result.resource = path.resolve(path.dirname(result.resource), newResource);
return result;
});
nmf.hooks.afterResolve.tap("NormalModuleReplacementPlugin", result => {
if (!result) return;
if (resourceRegExp.test(result.resource)) {
if (typeof newResource === "function") {
newResource(result);
} else {
result.resource = path.resolve(
path.dirname(result.resource),
newResource
);
}
}
}
return callback(null, result);
});
});
return result;
});
}
);
}
}

View File

@@ -4,24 +4,25 @@
*/
"use strict";
function getProperty(obj, name) {
const getProperty = (obj, name) => {
name = name.split(".");
for(let i = 0; i < name.length - 1; i++) {
for (let i = 0; i < name.length - 1; i++) {
obj = obj[name[i]];
if(typeof obj !== "object" || !obj) return;
if (typeof obj !== "object" || !obj || Array.isArray(obj)) return;
}
return obj[name.pop()];
}
};
function setProperty(obj, name, value) {
const setProperty = (obj, name, value) => {
name = name.split(".");
for(let i = 0; i < name.length - 1; i++) {
if(typeof obj[name[i]] !== "object" && typeof obj[name[i]] !== "undefined") return;
if(!obj[name[i]]) obj[name[i]] = {};
for (let i = 0; i < name.length - 1; i++) {
if (typeof obj[name[i]] !== "object" && obj[name[i]] !== undefined) return;
if (Array.isArray(obj[name[i]])) return;
if (!obj[name[i]]) obj[name[i]] = {};
obj = obj[name[i]];
}
obj[name.pop()] = value;
}
};
class OptionsDefaulter {
constructor() {
@@ -30,37 +31,46 @@ class OptionsDefaulter {
}
process(options) {
// TODO: change this for webpack 4: options = Object.assign({}, options);
for(let name in this.defaults) {
switch(this.config[name]) {
options = Object.assign({}, options);
for (let name in this.defaults) {
switch (this.config[name]) {
case undefined:
if(getProperty(options, name) === undefined)
if (getProperty(options, name) === undefined) {
setProperty(options, name, this.defaults[name]);
}
break;
case "call":
setProperty(options, name, this.defaults[name].call(this, getProperty(options, name), options), options);
setProperty(
options,
name,
this.defaults[name].call(this, getProperty(options, name), options)
);
break;
case "make":
if(getProperty(options, name) === undefined)
setProperty(options, name, this.defaults[name].call(this, options), options);
break;
case "append":
{
let oldValue = getProperty(options, name);
if(!Array.isArray(oldValue)) oldValue = [];
oldValue.push.apply(oldValue, this.defaults[name]);
setProperty(options, name, oldValue);
break;
if (getProperty(options, name) === undefined) {
setProperty(options, name, this.defaults[name].call(this, options));
}
break;
case "append": {
let oldValue = getProperty(options, name);
if (!Array.isArray(oldValue)) {
oldValue = [];
}
oldValue.push(...this.defaults[name]);
setProperty(options, name, oldValue);
break;
}
default:
throw new Error("OptionsDefaulter cannot process " + this.config[name]);
throw new Error(
"OptionsDefaulter cannot process " + this.config[name]
);
}
}
// TODO: change this for webpack 4: return options;
return options;
}
set(name, config, def) {
if(arguments.length === 3) {
if (def !== undefined) {
this.defaults[name] = def;
this.config[name] = config;
} else {

2234
node_modules/webpack/lib/Parser.js generated vendored

File diff suppressed because it is too large Load Diff

View File

@@ -11,12 +11,12 @@ const UnsupportedFeatureWarning = require("./UnsupportedFeatureWarning");
const ParserHelpers = exports;
ParserHelpers.addParsedVariableToModule = function(parser, name, expression) {
if(!parser.state.current.addVariable) return false;
ParserHelpers.addParsedVariableToModule = (parser, name, expression) => {
if (!parser.state.current.addVariable) return false;
var deps = [];
parser.parse(expression, {
current: {
addDependency: function(dep) {
addDependency: dep => {
dep.userRequest = name;
deps.push(dep);
}
@@ -27,51 +27,69 @@ ParserHelpers.addParsedVariableToModule = function(parser, name, expression) {
return true;
};
ParserHelpers.requireFileAsExpression = function(context, pathToModule) {
ParserHelpers.requireFileAsExpression = (context, pathToModule) => {
var moduleJsPath = path.relative(context, pathToModule);
if(!/^[A-Z]:/i.test(moduleJsPath)) {
if (!/^[A-Z]:/i.test(moduleJsPath)) {
moduleJsPath = "./" + moduleJsPath.replace(/\\/g, "/");
}
return "require(" + JSON.stringify(moduleJsPath) + ")";
};
ParserHelpers.toConstantDependency = function(value) {
ParserHelpers.toConstantDependency = (parser, value) => {
return function constDependency(expr) {
var dep = new ConstDependency(value, expr.range);
var dep = new ConstDependency(value, expr.range, false);
dep.loc = expr.loc;
this.state.current.addDependency(dep);
parser.state.current.addDependency(dep);
return true;
};
};
ParserHelpers.evaluateToString = function(value) {
ParserHelpers.toConstantDependencyWithWebpackRequire = (parser, value) => {
return function constDependencyWithWebpackRequire(expr) {
var dep = new ConstDependency(value, expr.range, true);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
return true;
};
};
ParserHelpers.evaluateToString = value => {
return function stringExpression(expr) {
return new BasicEvaluatedExpression().setString(value).setRange(expr.range);
};
};
ParserHelpers.evaluateToBoolean = function(value) {
ParserHelpers.evaluateToBoolean = value => {
return function booleanExpression(expr) {
return new BasicEvaluatedExpression().setBoolean(value).setRange(expr.range);
return new BasicEvaluatedExpression()
.setBoolean(value)
.setRange(expr.range);
};
};
ParserHelpers.evaluateToIdentifier = function(identifier, truthy) {
ParserHelpers.evaluateToIdentifier = (identifier, truthy) => {
return function identifierExpression(expr) {
let evex = new BasicEvaluatedExpression().setIdentifier(identifier).setRange(expr.range);
if(truthy === true) evex = evex.setTruthy();
else if(truthy === false) evex = evex.setFalsy();
let evex = new BasicEvaluatedExpression()
.setIdentifier(identifier)
.setRange(expr.range);
if (truthy === true) {
evex = evex.setTruthy();
} else if (truthy === false) {
evex = evex.setFalsy();
}
return evex;
};
};
ParserHelpers.expressionIsUnsupported = function(message) {
ParserHelpers.expressionIsUnsupported = (parser, message) => {
return function unsupportedExpression(expr) {
var dep = new ConstDependency("(void 0)", expr.range);
var dep = new ConstDependency("(void 0)", expr.range, false);
dep.loc = expr.loc;
this.state.current.addDependency(dep);
if(!this.state.module) return;
this.state.module.warnings.push(new UnsupportedFeatureWarning(this.state.module, message));
parser.state.current.addDependency(dep);
if (!parser.state.module) return;
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(parser.state.module, message, expr.loc)
);
return true;
};
};

View File

@@ -6,9 +6,8 @@
const PrefetchDependency = require("./dependencies/PrefetchDependency");
class PrefetchPlugin {
constructor(context, request) {
if(!request) {
if (!request) {
this.request = context;
} else {
this.context = context;
@@ -17,15 +16,22 @@ class PrefetchPlugin {
}
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
const normalModuleFactory = params.normalModuleFactory;
compilation.dependencyFactories.set(PrefetchDependency, normalModuleFactory);
});
compiler.plugin("make", (compilation, callback) => {
compilation.prefetch(this.context || compiler.context, new PrefetchDependency(this.request), callback);
compiler.hooks.compilation.tap(
"PrefetchPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
PrefetchDependency,
normalModuleFactory
);
}
);
compiler.hooks.make.tapAsync("PrefetchPlugin", (compilation, callback) => {
compilation.prefetch(
this.context || compiler.context,
new PrefetchDependency(this.request),
callback
);
});
}
}
module.exports = PrefetchPlugin;

View File

@@ -4,189 +4,328 @@
*/
"use strict";
class ProgressPlugin {
const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/ProgressPlugin.json");
/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginArgument} ProgressPluginArgument */
/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginOptions} ProgressPluginOptions */
const createDefaultHandler = profile => {
let lineCaretPosition = 0;
let lastMessage = "";
let lastState;
let lastStateTime;
const defaultHandler = (percentage, msg, ...args) => {
let state = msg;
const details = args;
if (percentage < 1) {
percentage = Math.floor(percentage * 100);
msg = `${percentage}% ${msg}`;
if (percentage < 100) {
msg = ` ${msg}`;
}
if (percentage < 10) {
msg = ` ${msg}`;
}
for (let detail of details) {
if (!detail) continue;
if (detail.length > 40) {
detail = `...${detail.substr(detail.length - 39)}`;
}
msg += ` ${detail}`;
}
}
if (profile) {
state = state.replace(/^\d+\/\d+\s+/, "");
if (percentage === 0) {
lastState = null;
lastStateTime = Date.now();
} else if (state !== lastState || percentage === 1) {
const now = Date.now();
if (lastState) {
const stateMsg = `${now - lastStateTime}ms ${lastState}`;
goToLineStart(stateMsg);
process.stderr.write(stateMsg + "\n");
lineCaretPosition = 0;
}
lastState = state;
lastStateTime = now;
}
}
if (lastMessage !== msg) {
goToLineStart(msg);
process.stderr.write(msg);
lastMessage = msg;
}
};
const goToLineStart = nextMessage => {
let str = "";
for (; lineCaretPosition > nextMessage.length; lineCaretPosition--) {
str += "\b \b";
}
for (var i = 0; i < lineCaretPosition; i++) {
str += "\b";
}
lineCaretPosition = nextMessage.length;
if (str) process.stderr.write(str);
};
return defaultHandler;
};
class ProgressPlugin {
/**
* @param {ProgressPluginArgument} options options
*/
constructor(options) {
if(typeof options === "function") {
if (typeof options === "function") {
options = {
handler: options
};
}
options = options || {};
validateOptions(schema, options, "Progress Plugin");
options = Object.assign({}, ProgressPlugin.defaultOptions, options);
this.profile = options.profile;
this.handler = options.handler;
this.modulesCount = options.modulesCount;
this.showEntries = options.entries;
this.showModules = options.modules;
this.showActiveModules = options.activeModules;
}
apply(compiler) {
const handler = this.handler || defaultHandler;
const profile = this.profile;
if(compiler.compilers) {
const { modulesCount } = this;
const handler = this.handler || createDefaultHandler(this.profile);
const showEntries = this.showEntries;
const showModules = this.showModules;
const showActiveModules = this.showActiveModules;
if (compiler.compilers) {
const states = new Array(compiler.compilers.length);
compiler.compilers.forEach(function(compiler, idx) {
compiler.apply(new ProgressPlugin(function(p, msg) {
states[idx] = Array.prototype.slice.apply(arguments);
handler.apply(null, [
states.map(state => state && state[0] || 0).reduce((a, b) => a + b) / states.length,
`[${idx}] ${msg}`
].concat(Array.prototype.slice.call(arguments, 2)));
}));
compiler.compilers.forEach((compiler, idx) => {
new ProgressPlugin((p, msg, ...args) => {
states[idx] = [p, msg, ...args];
handler(
states
.map(state => (state && state[0]) || 0)
.reduce((a, b) => a + b) / states.length,
`[${idx}] ${msg}`,
...args
);
}).apply(compiler);
});
} else {
let lastModulesCount = 0;
let moduleCount = 500;
let lastEntriesCount = 0;
let moduleCount = modulesCount;
let entriesCount = 1;
let doneModules = 0;
const activeModules = [];
let doneEntries = 0;
const activeModules = new Set();
let lastActiveModule = "";
const update = function update(module) {
handler(
0.1 + (doneModules / Math.max(lastModulesCount, moduleCount)) * 0.6,
"building modules",
`${doneModules}/${moduleCount} modules`,
`${activeModules.length} active`,
activeModules[activeModules.length - 1]
);
const update = () => {
const percentByModules =
doneModules / Math.max(lastModulesCount, moduleCount);
const percentByEntries =
doneEntries / Math.max(lastEntriesCount, entriesCount);
const items = [
0.1 + Math.max(percentByModules, percentByEntries) * 0.6,
"building"
];
if (showEntries) {
items.push(`${doneEntries}/${entriesCount} entries`);
}
if (showModules) {
items.push(`${doneModules}/${moduleCount} modules`);
}
if (showActiveModules) {
items.push(`${activeModules.size} active`);
items.push(lastActiveModule);
}
handler(...items);
};
const moduleDone = function moduleDone(module) {
doneModules++;
const ident = module.identifier();
if(ident) {
const idx = activeModules.indexOf(ident);
if(idx >= 0) activeModules.splice(idx, 1);
const moduleAdd = module => {
moduleCount++;
if (showActiveModules) {
const ident = module.identifier();
if (ident) {
activeModules.add(ident);
lastActiveModule = ident;
}
}
update();
};
compiler.plugin("compilation", function(compilation) {
if(compilation.compiler.isChild()) return;
lastModulesCount = moduleCount;
moduleCount = 0;
doneModules = 0;
handler(0, "compiling");
compilation.plugin("build-module", function(module) {
moduleCount++;
const entryAdd = (entry, name) => {
entriesCount++;
update();
};
const moduleDone = module => {
doneModules++;
if (showActiveModules) {
const ident = module.identifier();
if(ident) {
activeModules.push(ident);
if (ident) {
activeModules.delete(ident);
if (lastActiveModule === ident) {
lastActiveModule = "";
for (const m of activeModules) {
lastActiveModule = m;
}
}
}
update();
});
compilation.plugin("failed-module", moduleDone);
compilation.plugin("succeed-module", moduleDone);
const syncHooks = {
"seal": [0.71, "sealing"],
"optimize": [0.72, "optimizing"],
"optimize-modules-basic": [0.73, "basic module optimization"],
"optimize-modules": [0.74, "module optimization"],
"optimize-modules-advanced": [0.75, "advanced module optimization"],
"optimize-chunks-basic": [0.76, "basic chunk optimization"],
"optimize-chunks": [0.77, "chunk optimization"],
"optimize-chunks-advanced": [0.78, "advanced chunk optimization"],
// optimize-tree
"optimize-chunk-modules": [0.80, "chunk modules optimization"],
"optimize-chunk-modules-advanced": [0.81, "advanced chunk modules optimization"],
"revive-modules": [0.82, "module reviving"],
"optimize-module-order": [0.83, "module order optimization"],
"optimize-module-ids": [0.84, "module id optimization"],
"revive-chunks": [0.85, "chunk reviving"],
"optimize-chunk-order": [0.86, "chunk order optimization"],
"optimize-chunk-ids": [0.87, "chunk id optimization"],
"before-hash": [0.88, "hashing"],
"before-module-assets": [0.89, "module assets processing"],
"before-chunk-assets": [0.90, "chunk assets processing"],
"additional-chunk-assets": [0.91, "additional chunk assets processing"],
"record": [0.92, "recording"]
}
update();
};
const entryDone = (entry, name) => {
doneEntries++;
update();
};
compiler.hooks.compilation.tap("ProgressPlugin", compilation => {
if (compilation.compiler.isChild()) return;
lastModulesCount = moduleCount;
lastEntriesCount = entriesCount;
moduleCount = entriesCount = 0;
doneModules = doneEntries = 0;
handler(0, "compiling");
compilation.hooks.buildModule.tap("ProgressPlugin", moduleAdd);
compilation.hooks.failedModule.tap("ProgressPlugin", moduleDone);
compilation.hooks.succeedModule.tap("ProgressPlugin", moduleDone);
compilation.hooks.addEntry.tap("ProgressPlugin", entryAdd);
compilation.hooks.failedEntry.tap("ProgressPlugin", entryDone);
compilation.hooks.succeedEntry.tap("ProgressPlugin", entryDone);
const hooks = {
finishModules: "finish module graph",
seal: "sealing",
beforeChunks: "chunk graph",
afterChunks: "after chunk graph",
optimizeDependenciesBasic: "basic dependencies optimization",
optimizeDependencies: "dependencies optimization",
optimizeDependenciesAdvanced: "advanced dependencies optimization",
afterOptimizeDependencies: "after dependencies optimization",
optimize: "optimizing",
optimizeModulesBasic: "basic module optimization",
optimizeModules: "module optimization",
optimizeModulesAdvanced: "advanced module optimization",
afterOptimizeModules: "after module optimization",
optimizeChunksBasic: "basic chunk optimization",
optimizeChunks: "chunk optimization",
optimizeChunksAdvanced: "advanced chunk optimization",
afterOptimizeChunks: "after chunk optimization",
optimizeTree: "module and chunk tree optimization",
afterOptimizeTree: "after module and chunk tree optimization",
optimizeChunkModulesBasic: "basic chunk modules optimization",
optimizeChunkModules: "chunk modules optimization",
optimizeChunkModulesAdvanced: "advanced chunk modules optimization",
afterOptimizeChunkModules: "after chunk modules optimization",
reviveModules: "module reviving",
optimizeModuleOrder: "module order optimization",
advancedOptimizeModuleOrder: "advanced module order optimization",
beforeModuleIds: "before module ids",
moduleIds: "module ids",
optimizeModuleIds: "module id optimization",
afterOptimizeModuleIds: "module id optimization",
reviveChunks: "chunk reviving",
optimizeChunkOrder: "chunk order optimization",
beforeChunkIds: "before chunk ids",
optimizeChunkIds: "chunk id optimization",
afterOptimizeChunkIds: "after chunk id optimization",
recordModules: "record modules",
recordChunks: "record chunks",
beforeHash: "hashing",
contentHash: "content hashing",
afterHash: "after hashing",
recordHash: "record hash",
beforeModuleAssets: "module assets processing",
beforeChunkAssets: "chunk assets processing",
additionalChunkAssets: "additional chunk assets processing",
record: "recording",
additionalAssets: "additional asset processing",
optimizeChunkAssets: "chunk asset optimization",
afterOptimizeChunkAssets: "after chunk asset optimization",
optimizeAssets: "asset optimization",
afterOptimizeAssets: "after asset optimization",
afterSeal: "after seal"
};
Object.keys(syncHooks).forEach(name => {
let pass = 0;
const settings = syncHooks[name];
compilation.plugin(name, () => {
if(pass++ > 0)
handler(settings[0], settings[1], `pass ${pass}`);
else
handler(settings[0], settings[1]);
const numberOfHooks = Object.keys(hooks).length;
Object.keys(hooks).forEach((name, idx) => {
const title = hooks[name];
const percentage = (idx / numberOfHooks) * 0.25 + 0.7;
compilation.hooks[name].intercept({
name: "ProgressPlugin",
context: true,
call: () => {
handler(percentage, title);
},
tap: (context, tap) => {
if (context) {
// p is percentage from 0 to 1
// args is any number of messages in a hierarchical matter
context.reportProgress = (p, ...args) => {
handler(percentage, title, tap.name, ...args);
};
}
handler(percentage, title, tap.name);
}
});
});
compilation.plugin("optimize-tree", (chunks, modules, callback) => {
handler(0.79, "module and chunk tree optimization");
callback();
});
compilation.plugin("additional-assets", callback => {
handler(0.91, "additional asset processing");
callback();
});
compilation.plugin("optimize-chunk-assets", (chunks, callback) => {
handler(0.92, "chunk asset optimization");
callback();
});
compilation.plugin("optimize-assets", (assets, callback) => {
handler(0.94, "asset optimization");
callback();
});
});
compiler.plugin("emit", (compilation, callback) => {
handler(0.95, "emitting");
callback();
compiler.hooks.emit.intercept({
name: "ProgressPlugin",
context: true,
call: () => {
handler(0.95, "emitting");
},
tap: (context, tap) => {
if (context) {
context.reportProgress = (p, ...args) => {
handler(0.95, "emitting", tap.name, ...args);
};
}
handler(0.95, "emitting", tap.name);
}
});
compiler.plugin("done", () => {
compiler.hooks.afterEmit.intercept({
name: "ProgressPlugin",
context: true,
call: () => {
handler(0.98, "after emitting");
},
tap: (context, tap) => {
if (context) {
context.reportProgress = (p, ...args) => {
handler(0.98, "after emitting", tap.name, ...args);
};
}
handler(0.98, "after emitting", tap.name);
}
});
compiler.hooks.done.tap("ProgressPlugin", () => {
handler(1, "");
});
}
let lineCaretPosition = 0,
lastState, lastStateTime;
function defaultHandler(percentage, msg) {
let state = msg;
const details = Array.prototype.slice.call(arguments, 2);
if(percentage < 1) {
percentage = Math.floor(percentage * 100);
msg = `${percentage}% ${msg}`;
if(percentage < 100) {
msg = ` ${msg}`;
}
if(percentage < 10) {
msg = ` ${msg}`;
}
details.forEach(detail => {
if(!detail) return;
if(detail.length > 40) {
detail = `...${detail.substr(detail.length - 37)}`;
}
msg += ` ${detail}`;
});
}
if(profile) {
state = state.replace(/^\d+\/\d+\s+/, "");
if(percentage === 0) {
lastState = null;
lastStateTime = Date.now();
} else if(state !== lastState || percentage === 1) {
const now = Date.now();
if(lastState) {
const stateMsg = `${now - lastStateTime}ms ${lastState}`;
goToLineStart(stateMsg);
process.stderr.write(stateMsg + "\n");
lineCaretPosition = 0;
}
lastState = state;
lastStateTime = now;
}
}
goToLineStart(msg);
process.stderr.write(msg);
}
function goToLineStart(nextMessage) {
let str = "";
for(; lineCaretPosition > nextMessage.length; lineCaretPosition--) {
str += "\b \b";
}
for(var i = 0; i < lineCaretPosition; i++) {
str += "\b";
}
lineCaretPosition = nextMessage.length;
if(str) process.stderr.write(str);
}
}
}
ProgressPlugin.defaultOptions = {
profile: false,
modulesCount: 500,
modules: true,
activeModules: true,
// TODO webpack 5 default this to true
entries: false
};
module.exports = ProgressPlugin;

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