Refactor HMR for runtime modules

fixed a few HMR bugs
allow other module types for HMR
made HMR more flexible
This commit is contained in:
Tobias Koppers 2018-11-28 13:07:40 +01:00
parent fa0ec849bd
commit 9d91861592
59 changed files with 2932 additions and 2999 deletions

21
declarations.d.ts vendored
View File

@ -234,20 +234,19 @@ interface RuleSetConditionsAbsoluteRecursive
* @todo Once this issue is resolved, remove these globals and add JSDoc onsite instead
* https://github.com/Microsoft/TypeScript/issues/15626
*/
declare const $hash$;
declare const $requestTimeout$;
declare const $createFakeNamespaceObject$;
declare const $moduleCache$;
declare let $getFullHash$;
declare const $interceptModuleExecution$;
declare const $hmrDownloadManifest$;
declare let $hmrDownloadUpdateHandlers$;
declare let $hmrModuleData$;
declare const $options$;
declare const $updateModuleFactories$;
declare const $updateRuntimeModules$;
declare const $moduleFactories$;
declare const $onError$;
declare const $publicPath$;
declare const installedModules;
declare const __webpack_require__;
declare const hotDownloadManifest;
declare const hotDownloadUpdateChunk;
declare const hotDisposeChunk;
declare const modules;
declare const installedChunks;
declare const hotAddUpdateChunk;
declare const parentHotUpdateCallback;
declare const $hotChunkFilename$;
declare const $hotMainFilename$;
declare const WebAssembly;

View File

@ -1385,6 +1385,10 @@ export interface StatsOptions {
* add information about the reasons why modules are included
*/
reasons?: boolean;
/**
* add information about runtime modules
*/
runtime?: boolean;
/**
* add the source code of modules
*/

View File

@ -10,7 +10,10 @@ const SortableSet = require("./util/SortableSet");
const {
compareModulesById,
compareIterables,
compareModulesByIdentifier
compareModulesByIdentifier,
concatComparators,
compareSelect,
compareIds
} = require("./util/comparators");
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
@ -219,6 +222,18 @@ class ChunkGraph {
}
}
/**
* @param {Chunk} chunk the chunk
* @param {Iterable<RuntimeModule>} modules the runtime modules
* @returns {void}
*/
attachRuntimeModules(chunk, modules) {
const cgc = this._getChunkGraphChunk(chunk);
for (const module of modules) {
cgc.runtimeModules.add(module);
}
}
/**
* @param {Module} oldModule the replaced module
* @param {Module} newModule the replacing module
@ -760,6 +775,22 @@ class ChunkGraph {
return cgc.runtimeModules;
}
/**
* @param {Chunk} chunk the chunk
* @returns {RuntimeModule[]} array of modules in order of execution
*/
getChunkRuntimeModulesInOrder(chunk) {
const cgc = this._getChunkGraphChunk(chunk);
const array = Array.from(cgc.runtimeModules);
array.sort(
concatComparators(
compareSelect(r => r.stage, compareIds),
compareSelect(r => this.getModuleId(r), compareIds)
)
);
return array;
}
/** @typedef {[Module, ChunkGroup | undefined]} EntryModuleWithChunkGroup */
/**

View File

@ -28,7 +28,6 @@ const {
connectChunkGroupAndChunk,
connectChunkGroupParentAndChild
} = require("./GraphHelpers");
const HotUpdateChunkTemplate = require("./HotUpdateChunkTemplate");
const MainTemplate = require("./MainTemplate");
const ModuleDependencyError = require("./ModuleDependencyError");
const ModuleDependencyWarning = require("./ModuleDependencyWarning");
@ -435,9 +434,6 @@ class Compilation {
this.mainTemplate = new MainTemplate(this.outputOptions);
this.chunkTemplate = new ChunkTemplate(this.outputOptions);
this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
this.outputOptions
);
this.runtimeTemplate = new RuntimeTemplate(
this.outputOptions,
this.requestShortener
@ -1157,8 +1153,9 @@ class Compilation {
}
}
}
const entryChunkGroups = /** @type {Entrypoint[]} */ (this.chunkGroups.slice());
this.processDependenciesBlocksForChunkGroups(entryChunkGroups);
this.processDependenciesBlocksForChunkGroups(
/** @type {Entrypoint[]} */ (this.chunkGroups.slice())
);
this.hooks.afterChunks.call(this.chunks);
this.hooks.optimize.call();
@ -1211,7 +1208,7 @@ class Compilation {
this.hooks.afterModuleHash.call();
this.hooks.beforeRuntimeRequirements.call();
this.processRuntimeRequirements(entryChunkGroups);
this.processRuntimeRequirements(this.entrypoints.values());
this.hooks.afterRuntimeRequirements.call();
this.hooks.beforeHash.call();
@ -1308,7 +1305,7 @@ class Compilation {
}
/**
* @param {Entrypoint[]} entrypoints the entrypoints
* @param {Iterable<Entrypoint>} entrypoints the entrypoints
* @returns {void}
*/
processRuntimeRequirements(entrypoints) {
@ -2462,6 +2459,7 @@ class Compilation {
const usedIds = new Set();
for (const module of this.modules) {
if (module.type === "runtime") continue;
const moduleId = chunkGraph.getModuleId(module);
if (moduleId === null) continue;
if (usedIds.has(moduleId)) {

View File

@ -1,651 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/*global $hash$ $requestTimeout$ installedModules __webpack_require__ hotDownloadManifest hotDownloadUpdateChunk hotDisposeChunk modules $createFakeNamespaceObject$ */
module.exports = function() {
var hotApplyOnUpdate = true;
// eslint-disable-next-line no-unused-vars
var hotCurrentHash = $hash$;
var hotRequestTimeout = $requestTimeout$;
var hotCurrentModuleData = {};
var hotCurrentChildModule;
// eslint-disable-next-line no-unused-vars
var hotCurrentParents = [];
// eslint-disable-next-line no-unused-vars
var hotCurrentParentsTemp = [];
// eslint-disable-next-line no-unused-vars
function hotCreateRequire(moduleId) {
var me = installedModules[moduleId];
if (!me) return __webpack_require__;
var fn = function(request) {
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) === -1) {
me.children.push(request);
}
} else {
console.warn(
"[HMR] unexpected require(" +
request +
") from disposed module " +
moduleId
);
hotCurrentParents = [];
}
return __webpack_require__(request);
};
var ObjectFactory = function ObjectFactory(name) {
return {
configurable: true,
enumerable: true,
get: function() {
return __webpack_require__[name];
},
set: function(value) {
__webpack_require__[name] = value;
}
};
};
for (var name in __webpack_require__) {
if (
Object.prototype.hasOwnProperty.call(__webpack_require__, name) &&
name !== "e" &&
name !== "t"
) {
Object.defineProperty(fn, name, ObjectFactory(name));
}
}
fn.e = function(chunkId) {
if (hotStatus === "ready") hotSetStatus("prepare");
hotChunksLoading++;
return __webpack_require__
.e(chunkId)
.then(finishChunkLoading, function(err) {
finishChunkLoading();
throw err;
});
function finishChunkLoading() {
hotChunksLoading--;
if (hotStatus === "prepare") {
if (!hotWaitingFilesMap[chunkId]) {
hotEnsureUpdateChunk(chunkId);
}
if (hotChunksLoading === 0 && hotWaitingFiles === 0) {
hotUpdateDownloaded();
}
}
}
};
fn.t = function(value, mode) {
if (mode & 1) value = fn(value);
return $createFakeNamespaceObject$(value, mode & ~1);
};
return fn;
}
// eslint-disable-next-line no-unused-vars
function hotCreateModule(moduleId) {
var hot = {
// private stuff
_acceptedDependencies: {},
_declinedDependencies: {},
_selfAccepted: false,
_selfDeclined: false,
_disposeHandlers: [],
_main: hotCurrentChildModule !== moduleId,
// Module API
active: true,
accept: function(dep, callback) {
if (dep === undefined) hot._selfAccepted = true;
else if (typeof dep === "function") hot._selfAccepted = dep;
else if (typeof dep === "object" && dep !== null)
for (var i = 0; i < dep.length; i++)
hot._acceptedDependencies[dep[i]] = callback || function() {};
else hot._acceptedDependencies[dep] = callback || function() {};
},
decline: function(dep) {
if (dep === undefined) hot._selfDeclined = true;
else if (typeof dep === "object" && dep !== null)
for (var i = 0; i < dep.length; i++)
hot._declinedDependencies[dep[i]] = true;
else hot._declinedDependencies[dep] = true;
},
dispose: function(callback) {
hot._disposeHandlers.push(callback);
},
addDisposeHandler: function(callback) {
hot._disposeHandlers.push(callback);
},
removeDisposeHandler: function(callback) {
var idx = hot._disposeHandlers.indexOf(callback);
if (idx >= 0) hot._disposeHandlers.splice(idx, 1);
},
// Management API
check: hotCheck,
apply: hotApply,
status: function(l) {
if (!l) return hotStatus;
hotStatusHandlers.push(l);
},
addStatusHandler: function(l) {
hotStatusHandlers.push(l);
},
removeStatusHandler: function(l) {
var idx = hotStatusHandlers.indexOf(l);
if (idx >= 0) hotStatusHandlers.splice(idx, 1);
},
//inherit from previous dispose call
data: hotCurrentModuleData[moduleId]
};
hotCurrentChildModule = undefined;
return hot;
}
var hotStatusHandlers = [];
var hotStatus = "idle";
function hotSetStatus(newStatus) {
hotStatus = newStatus;
for (var i = 0; i < hotStatusHandlers.length; i++)
hotStatusHandlers[i].call(null, newStatus);
}
// while downloading
var hotWaitingFiles = 0;
var hotChunksLoading = 0;
var hotWaitingFilesMap = {};
var hotRequestedFilesMap = {};
var hotAvailableFilesMap = {};
var hotDeferred;
// The update info
var hotUpdate, hotUpdateNewHash;
function toModuleId(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");
}
hotApplyOnUpdate = apply;
hotSetStatus("check");
return hotDownloadManifest(hotRequestTimeout).then(function(update) {
if (!update) {
hotSetStatus("idle");
return null;
}
hotRequestedFilesMap = {};
hotWaitingFilesMap = {};
hotAvailableFilesMap = update.c;
hotUpdateNewHash = update.h;
hotSetStatus("prepare");
var promise = new Promise(function(resolve, reject) {
hotDeferred = {
resolve: resolve,
reject: reject
};
});
hotUpdate = {};
/*foreachInstalledChunks*/
// eslint-disable-next-line no-lone-blocks
{
/*globals chunkId */
hotEnsureUpdateChunk(chunkId);
}
if (
hotStatus === "prepare" &&
hotChunksLoading === 0 &&
hotWaitingFiles === 0
) {
hotUpdateDownloaded();
}
return promise;
});
}
// 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)) {
hotUpdate[moduleId] = moreModules[moduleId];
}
}
if (--hotWaitingFiles === 0 && hotChunksLoading === 0) {
hotUpdateDownloaded();
}
}
function hotEnsureUpdateChunk(chunkId) {
if (!hotAvailableFilesMap[chunkId]) {
hotWaitingFilesMap[chunkId] = true;
} else {
hotRequestedFilesMap[chunkId] = true;
hotWaitingFiles++;
hotDownloadUpdateChunk(chunkId);
}
}
function hotUpdateDownloaded() {
hotSetStatus("ready");
var deferred = hotDeferred;
hotDeferred = null;
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);
}
);
} else {
var outdatedModules = [];
for (var id in hotUpdate) {
if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
outdatedModules.push(toModuleId(id));
}
}
deferred.resolve(outdatedModules);
}
}
function hotApply(options) {
if (hotStatus !== "ready")
throw new Error("apply() is only allowed in ready status");
options = options || {};
var cb;
var i;
var j;
var module;
var moduleId;
function getAffectedStuff(updateModuleId) {
var outdatedModules = [updateModuleId];
var outdatedDependencies = {};
var queue = outdatedModules.slice().map(function(id) {
return {
chain: [id],
id: id
};
});
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) {
return {
type: "self-declined",
chain: chain,
moduleId: moduleId
};
}
if (module.hot._main) {
return {
type: "unaccepted",
chain: chain,
moduleId: moduleId
};
}
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]) {
return {
type: "declined",
chain: chain.concat([parentId]),
moduleId: moduleId,
parentId: parentId
};
}
if (outdatedModules.indexOf(parentId) !== -1) continue;
if (parent.hot._acceptedDependencies[moduleId]) {
if (!outdatedDependencies[parentId])
outdatedDependencies[parentId] = [];
addAllToSet(outdatedDependencies[parentId], [moduleId]);
continue;
}
delete outdatedDependencies[parentId];
outdatedModules.push(parentId);
queue.push({
chain: chain.concat([parentId]),
id: parentId
});
}
}
return {
type: "accepted",
moduleId: updateModuleId,
outdatedModules: outdatedModules,
outdatedDependencies: outdatedDependencies
};
}
function addAllToSet(a, b) {
for (var i = 0; i < b.length; i++) {
var item = b[i];
if (a.indexOf(item) === -1) a.push(item);
}
}
// at begin all updates modules are outdated
// the "outdated" status can propagate to parents if they don't accept the children
var outdatedDependencies = {};
var outdatedModules = [];
var appliedUpdate = {};
var warnUnexpectedRequire = function warnUnexpectedRequire() {
console.warn(
"[HMR] unexpected require(" + result.moduleId + ") to disposed module"
);
};
for (var id in hotUpdate) {
if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
moduleId = toModuleId(id);
/** @type {TODO} */
var result;
if (hotUpdate[id]) {
result = getAffectedStuff(moduleId);
} else {
result = {
type: "disposed",
moduleId: id
};
}
/** @type {Error|false} */
var abortError = false;
var doApply = false;
var doDispose = false;
var chainInfo = "";
if (result.chain) {
chainInfo = "\nUpdate propagation: " + result.chain.join(" -> ");
}
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
);
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
);
break;
case "unaccepted":
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);
doApply = true;
break;
case "disposed":
if (options.onDisposed) options.onDisposed(result);
doDispose = true;
break;
default:
throw new Error("Unexception type " + result.type);
}
if (abortError) {
hotSetStatus("abort");
return Promise.reject(abortError);
}
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])
outdatedDependencies[moduleId] = [];
addAllToSet(
outdatedDependencies[moduleId],
result.outdatedDependencies[moduleId]
);
}
}
}
if (doDispose) {
addAllToSet(outdatedModules, [result.moduleId]);
appliedUpdate[moduleId] = warnUnexpectedRequire;
}
}
}
// Store self accepted outdated modules to require them later by the module system
var outdatedSelfAcceptedModules = [];
for (i = 0; i < outdatedModules.length; i++) {
moduleId = outdatedModules[i];
if (
installedModules[moduleId] &&
installedModules[moduleId].hot._selfAccepted
)
outdatedSelfAcceptedModules.push({
module: moduleId,
errorHandler: installedModules[moduleId].hot._selfAccepted
});
}
// Now in "dispose" phase
hotSetStatus("dispose");
Object.keys(hotAvailableFilesMap).forEach(function(chunkId) {
if (hotAvailableFilesMap[chunkId] === false) {
hotDisposeChunk(chunkId);
}
});
var idx;
var queue = outdatedModules.slice();
while (queue.length > 0) {
moduleId = queue.pop();
module = installedModules[moduleId];
if (!module) continue;
var data = {};
// Call dispose handlers
var disposeHandlers = module.hot._disposeHandlers;
for (j = 0; j < disposeHandlers.length; j++) {
cb = disposeHandlers[j];
cb(data);
}
hotCurrentModuleData[moduleId] = data;
// disable module (this disables requires from this module)
module.hot.active = false;
// remove module from cache
delete installedModules[moduleId];
// when disposing there is no need to call dispose handler
delete outdatedDependencies[moduleId];
// remove "parents" references from all children
for (j = 0; j < module.children.length; j++) {
var child = installedModules[module.children[j]];
if (!child) continue;
idx = child.parents.indexOf(moduleId);
if (idx >= 0) {
child.parents.splice(idx, 1);
}
}
}
// remove outdated dependency from module children
var dependency;
var moduleOutdatedDependencies;
for (moduleId in outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
) {
module = installedModules[moduleId];
if (module) {
moduleOutdatedDependencies = outdatedDependencies[moduleId];
for (j = 0; j < moduleOutdatedDependencies.length; j++) {
dependency = moduleOutdatedDependencies[j];
idx = module.children.indexOf(dependency);
if (idx >= 0) module.children.splice(idx, 1);
}
}
}
}
// Not in "apply" phase
hotSetStatus("apply");
hotCurrentHash = hotUpdateNewHash;
// insert new code
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)
) {
module = installedModules[moduleId];
if (module) {
moduleOutdatedDependencies = outdatedDependencies[moduleId];
var callbacks = [];
for (i = 0; i < moduleOutdatedDependencies.length; i++) {
dependency = moduleOutdatedDependencies[i];
cb = module.hot._acceptedDependencies[dependency];
if (cb) {
if (callbacks.indexOf(cb) !== -1) continue;
callbacks.push(cb);
}
}
for (i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
try {
cb(moduleOutdatedDependencies);
} catch (err) {
if (options.onErrored) {
options.onErrored({
type: "accept-errored",
moduleId: moduleId,
dependencyId: moduleOutdatedDependencies[i],
error: err
});
}
if (!options.ignoreErrored) {
if (!error) error = err;
}
}
}
}
}
}
// Load self accepted modules
for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
var item = outdatedSelfAcceptedModules[i];
moduleId = item.module;
hotCurrentParents = [moduleId];
try {
__webpack_require__(moduleId);
} catch (err) {
if (typeof item.errorHandler === "function") {
try {
item.errorHandler(err);
} catch (err2) {
if (options.onErrored) {
options.onErrored({
type: "self-accept-error-handler-errored",
moduleId: moduleId,
error: err2,
originalError: err
});
}
if (!options.ignoreErrored) {
if (!error) error = err2;
}
if (!error) error = err;
}
} else {
if (options.onErrored) {
options.onErrored({
type: "self-accept-errored",
moduleId: moduleId,
error: err
});
}
if (!options.ignoreErrored) {
if (!error) error = err;
}
}
}
}
// handle errors in accept handlers and self accepted module load
if (error) {
hotSetStatus("fail");
return Promise.reject(error);
}
hotSetStatus("idle");
return new Promise(function(resolve) {
resolve(outdatedModules);
});
}
};

View File

@ -18,10 +18,10 @@ const MainTemplate = require("./MainTemplate");
const NormalModule = require("./NormalModule");
const NullFactory = require("./NullFactory");
const RuntimeGlobals = require("./RuntimeGlobals");
const Template = require("./Template");
const ModuleHotAcceptDependency = require("./dependencies/ModuleHotAcceptDependency");
const ModuleHotDeclineDependency = require("./dependencies/ModuleHotDeclineDependency");
const ModuleHotDependency = require("./dependencies/ModuleHotDependency");
const HotModuleReplacementRuntimeModule = require("./hmr/HotModuleReplacementRuntimeModule");
const { find } = require("./util/SetHelpers");
const { compareModulesById } = require("./util/comparators");
@ -40,10 +40,6 @@ const { compareModulesById } = require("./util/comparators");
* @property {SyncBailHook<TODO, string[]>} hotAcceptWithoutCallback
*/
const hotInitCode = Template.getFunctionContent(
require("./HotModuleReplacement.runtime")
);
/** @type {WeakMap<MainTemplate, HMRMainTemplateHooks>} */
const mainTemplateHooksMap = new WeakMap();
@ -96,7 +92,6 @@ class HotModuleReplacementPlugin {
this.options = options || {};
this.multiStep = this.options.multiStep;
this.fullBuildTimeout = this.options.fullBuildTimeout || 200;
this.requestTimeout = this.options.requestTimeout || 10000;
}
/**
@ -106,9 +101,6 @@ class HotModuleReplacementPlugin {
apply(compiler) {
const multiStep = this.multiStep;
const fullBuildTimeout = this.fullBuildTimeout;
const requestTimeout = this.requestTimeout;
const hotUpdateChunkFilename =
compiler.options.output.hotUpdateChunkFilename;
const hotUpdateMainFilename = compiler.options.output.hotUpdateMainFilename;
compiler.hooks.additionalPass.tapAsync(
"HotModuleReplacementPlugin",
@ -141,18 +133,12 @@ class HotModuleReplacementPlugin {
before: "NodeStuffPlugin"
},
expr => {
return evaluateToIdentifier(
"module.hot",
!!parser.state.compilation.hotUpdateChunkTemplate
)(expr);
return evaluateToIdentifier("module.hot", true)(expr);
}
);
parser.hooks.call
.for("module.hot.accept")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
const dep = new ModuleHotDependency(expr.callee.range, "accept");
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
@ -190,9 +176,6 @@ class HotModuleReplacementPlugin {
parser.hooks.call
.for("module.hot.decline")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
const dep = new ModuleHotDependency(expr.callee.range, "decline");
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
@ -230,10 +213,6 @@ class HotModuleReplacementPlugin {
compiler.hooks.compilation.tap(
"HotModuleReplacementPlugin",
(compilation, { normalModuleFactory }) => {
const moduleGraph = compilation.moduleGraph;
const hotUpdateChunkTemplate = compilation.hotUpdateChunkTemplate;
if (!hotUpdateChunkTemplate) return;
compilation.dependencyFactories.set(
ModuleHotAcceptDependency,
normalModuleFactory
@ -326,6 +305,7 @@ class HotModuleReplacementPlugin {
"HotModuleReplacementPlugin",
() => {
const chunkGraph = compilation.chunkGraph;
const chunkTemplate = compilation.chunkTemplate;
const records = compilation.records;
if (records.hash === compilation.hash) return;
if (
@ -345,8 +325,11 @@ class HotModuleReplacementPlugin {
}
const hotUpdateMainContent = {
h: compilation.hash,
c: {}
c: [],
r: [],
m: undefined
};
const allRemovedModules = new Set();
for (const key of Object.keys(records.chunkHashs)) {
const chunkId = key;
const currentChunk = find(
@ -357,6 +340,9 @@ class HotModuleReplacementPlugin {
const newModules = chunkGraph
.getChunkModules(currentChunk)
.filter(module => updatedModules.has(module));
const newRuntimeModules = Array.from(
chunkGraph.getChunkRuntimeModulesIterable(currentChunk)
).filter(module => updatedModules.has(module));
/** @type {Set<number|string>} */
const allModules = new Set();
for (const module of chunkGraph.getChunkModulesIterable(
@ -371,32 +357,42 @@ class HotModuleReplacementPlugin {
const hotUpdateChunk = new HotUpdateChunk();
hotUpdateChunk.id = chunkId;
chunkGraph.attachModules(hotUpdateChunk, newModules);
hotUpdateChunk.removedModules = removedModules;
const source = hotUpdateChunkTemplate.render(
{
chunk: hotUpdateChunk,
dependencyTemplates: compilation.dependencyTemplates,
runtimeTemplate: compilation.runtimeTemplate,
moduleGraph,
chunkGraph: compilation.chunkGraph
},
compilation.moduleTemplates.javascript,
compilation.hash
chunkGraph.attachRuntimeModules(
hotUpdateChunk,
newRuntimeModules
);
const filename = compilation.getPath(hotUpdateChunkFilename, {
hotUpdateChunk.removedModules = removedModules;
const renderManifest = chunkTemplate.getRenderManifest({
chunk: hotUpdateChunk,
hash: records.hash,
chunk: currentChunk
fullHash: records.hash,
outputOptions: chunkTemplate.outputOptions,
moduleTemplates: compilation.moduleTemplates,
dependencyTemplates: compilation.dependencyTemplates,
runtimeTemplate: compilation.runtimeTemplate,
moduleGraph: compilation.moduleGraph,
chunkGraph
});
compilation.additionalChunkAssets.push(filename);
compilation.assets[filename] = source;
hotUpdateMainContent.c[chunkId] = true;
currentChunk.files.push(filename);
compilation.hooks.chunkAsset.call(currentChunk, filename);
for (const entry of renderManifest) {
const filename = compilation.getPath(
entry.filenameTemplate,
entry.pathOptions
);
const source = entry.render();
compilation.additionalChunkAssets.push(filename);
compilation.assets[filename] = source;
currentChunk.files.push(filename);
compilation.hooks.chunkAsset.call(currentChunk, filename);
}
hotUpdateMainContent.c.push(chunkId);
}
} else {
hotUpdateMainContent.c[chunkId] = false;
hotUpdateMainContent.r.push(chunkId);
for (const id of records.chunkModuleIds[chunkId])
allRemovedModules.add(id);
}
}
hotUpdateMainContent.m = Array.from(allRemovedModules);
const source = new RawSource(JSON.stringify(hotUpdateMainContent));
const filename = compilation.getPath(hotUpdateMainFilename, {
hash: records.hash
@ -405,78 +401,18 @@ class HotModuleReplacementPlugin {
}
);
const mainTemplate = compilation.mainTemplate;
const {
hotBootstrap
} = HotModuleReplacementPlugin.getMainTemplateHooks(mainTemplate);
mainTemplate.hooks.hash.tap("HotModuleReplacementPlugin", hash => {
hash.update("HotMainTemplateDecorator");
});
mainTemplate.hooks.moduleRequire.tap(
compilation.hooks.additionalTreeRuntimeRequirements.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(
`${
RuntimeGlobals.getFullHash
} = function() { return hotCurrentHash; };`
(chunk, runtimeRequirements) => {
runtimeRequirements.add(RuntimeGlobals.hmrDownloadManifest);
runtimeRequirements.add(RuntimeGlobals.hmrDownloadUpdateHandlers);
runtimeRequirements.add(RuntimeGlobals.getFullHash);
runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution);
runtimeRequirements.add(RuntimeGlobals.moduleCache);
compilation.addRuntimeModule(
chunk,
new HotModuleReplacementRuntimeModule()
);
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 }) => {
const hotSource = hotBootstrap.call(source, chunk, hash);
return Template.asString([
hotSource,
"",
hotInitCode
.replace(/\$hash\$/g, JSON.stringify(hash))
.replace(/\$requestTimeout\$/g, requestTimeout)
.replace(
/\$createFakeNamespaceObject\$/g,
RuntimeGlobals.createFakeNamespaceObject
)
.replace(
/\/\*foreachInstalledChunks\*\//g,
needChunkLoadingCode(chunk)
? "for(var chunkId in installedChunks)"
: `var chunkId = ${JSON.stringify(chunk.id)};`
)
]);
}
);
mainTemplate.hooks.moduleObj.tap(
"HotModuleReplacementPlugin",
(source, varModuleId) => {
return Template.asString([
`${source},`,
`hot: hotCreateModule(${varModuleId}),`,
"parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),",
"children: []"
]);
}
);

View File

@ -7,6 +7,9 @@
const Chunk = require("./Chunk");
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./util/createHash").Hash} Hash */
class HotUpdateChunk extends Chunk {
constructor() {
super();
@ -14,6 +17,11 @@ class HotUpdateChunk extends Chunk {
this.removedModules = undefined;
}
/**
* @param {Hash} hash hash (will be modified)
* @param {ChunkGraph} chunkGraph the chunk graph
* @returns {void}
*/
updateHash(hash, chunkGraph) {
super.updateHash(hash, chunkGraph);
hash.update(JSON.stringify(this.removedModules));

View File

@ -8,6 +8,7 @@
const { SyncBailHook } = require("tapable");
const { ConcatSource } = require("webpack-sources");
const Compilation = require("./Compilation");
const HotUpdateChunk = require("./HotUpdateChunk");
const JavascriptGenerator = require("./JavascriptGenerator");
const JavascriptParser = require("./JavascriptParser");
const Template = require("./Template");
@ -147,12 +148,16 @@ class JavascriptModulesPlugin {
"JavascriptModulesPlugin",
(result, options) => {
const chunk = options.chunk;
const hotUpdateChunk =
chunk instanceof HotUpdateChunk ? chunk : null;
const outputOptions = options.outputOptions;
const moduleTemplates = options.moduleTemplates;
const dependencyTemplates = options.dependencyTemplates;
let filenameTemplate;
if (chunk.filenameTemplate) {
if (hotUpdateChunk) {
filenameTemplate = outputOptions.hotUpdateChunkFilename;
} else if (chunk.filenameTemplate) {
filenameTemplate = chunk.filenameTemplate;
} else if (chunk.isOnlyInitial()) {
filenameTemplate = outputOptions.filename;
@ -176,6 +181,7 @@ class JavascriptModulesPlugin {
),
filenameTemplate,
pathOptions: {
hash: options.hash,
chunk,
contentHashType: "javascript"
},
@ -198,6 +204,7 @@ class JavascriptModulesPlugin {
hashFunction
}
} = compilation;
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
const hash = createHash(hashFunction);
if (hashSalt) hash.update(hashSalt);
const template = chunk.hasRuntime()
@ -218,6 +225,9 @@ class JavascriptModulesPlugin {
hash.update(chunkGraph.getModuleHash(m));
}
}
if (hotUpdateChunk) {
hash.update(JSON.stringify(hotUpdateChunk.removedModules));
}
chunk.contentHash.javascript = hash
.digest(hashDigest)
.substr(0, hashDigestLength);

View File

@ -72,12 +72,6 @@ module.exports = class MainTemplate {
"moduleTemplate",
"renderContext"
]),
/** @type {SyncWaterfallHook<string, string, RenderBootstrapContext>} */
moduleObj: new SyncWaterfallHook([
"source",
"moduleIdExpression",
"renderContext"
]),
/** @type {SyncWaterfallHook<string, RenderBootstrapContext>} */
bootstrap: new SyncWaterfallHook(["source", "renderContext"]),
/** @type {SyncWaterfallHook<string, Chunk, string>} */
@ -107,12 +101,6 @@ module.exports = class MainTemplate {
"hash",
"moduleIdExpression"
]),
/** @type {SyncWaterfallHook<string, {moduleId: string, module: string}, RenderBootstrapContext>} */
addModule: new SyncWaterfallHook([
"source",
"expressions",
"renderContext"
]),
/** @type {SyncWaterfallHook<string, object>} */
assetPath: new SyncWaterfallHook(["path", "options"]),
/** @type {SyncHook<Hash>} */
@ -123,11 +111,10 @@ module.exports = class MainTemplate {
this.hooks.startup.tap(
"MainTemplate",
(source, { chunk, hash, chunkGraph }) => {
if (
chunkGraph
.getTreeRuntimeRequirements(chunk)
.has(RuntimeGlobals.startup)
) {
const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(
chunk
);
if (runtimeRequirements.has(RuntimeGlobals.startup)) {
return Template.asString([
"// run modules when ready",
`return ${RuntimeGlobals.startup}();`
@ -152,13 +139,7 @@ module.exports = class MainTemplate {
RuntimeGlobals.entryModuleId
} = ${moduleIdExpr}`;
}
buf.push(
`${mayReturn}${this.renderRequireFunctionForModule(
hash,
chunk,
JSON.stringify(moduleId)
)}(${moduleIdExpr});`
);
buf.push(`${mayReturn}__webpack_require__(${moduleIdExpr});`);
}
return Template.asString(buf);
}
@ -195,7 +176,22 @@ module.exports = class MainTemplate {
]);
});
this.hooks.require.tap("MainTemplate", (source, renderContext) => {
const { chunk, hash } = renderContext;
const { chunk, chunkGraph } = renderContext;
const runtimeRequirements = chunkGraph.getTreeRuntimeRequirements(chunk);
const moduleExecution = runtimeRequirements.has(
RuntimeGlobals.interceptModuleExecution
)
? Template.asString([
"const execOptions = { id: moduleId, module: module, factory: modules[moduleId], require: __webpack_require__ };",
`${
RuntimeGlobals.interceptModuleExecution
}.forEach(function(handler) { handler(execOptions); });`,
"module = execOptions.module;",
"execOptions.factory.call(module.exports, module, module.exports, execOptions.require);"
])
: Template.asString([
"modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);"
]);
return Template.asString([
source,
"// Check if module is in cache",
@ -204,40 +200,23 @@ module.exports = class MainTemplate {
"}",
"// Create a new module (and put it into the cache)",
"var module = installedModules[moduleId] = {",
Template.indent(
this.hooks.moduleObj.call("", "moduleId", renderContext)
),
Template.indent(["i: moduleId,", "l: false,", "exports: {}"]),
"};",
"",
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"
)});`
]
),
outputOptions.strictModuleExceptionHandling
? Template.asString([
"// Execute the module function",
"var threw = true;",
"try {",
Template.indent([moduleExecution, "threw = false;"]),
"} finally {",
Template.indent(["if(threw) delete installedModules[moduleId];"]),
"}"
])
: Template.asString([
"// Execute the module function",
moduleExecution
]),
"",
"// Flag the module as loaded",
"module.l = true;",
@ -246,9 +225,6 @@ module.exports = class MainTemplate {
"return module.exports;"
]);
});
this.hooks.moduleObj.tap("MainTemplate", () => {
return Template.asString(["i: moduleId,", "l: false,", "exports: {}"]);
});
this.hooks.requireExtensions.tap(
"MainTemplate",
(source, renderContext) => {
@ -271,6 +247,12 @@ module.exports = class MainTemplate {
buf.push(`${RuntimeGlobals.moduleCache} = installedModules;`);
}
if (runtimeRequirements.has(RuntimeGlobals.interceptModuleExecution)) {
buf.push("");
buf.push("// expose the module execution interceptor");
buf.push(`${RuntimeGlobals.interceptModuleExecution} = [];`);
}
return Template.asString(buf);
}
);
@ -346,40 +328,6 @@ module.exports = class MainTemplate {
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.hooks.moduleRequire.call(
"__webpack_require__",
chunk,
hash,
varModuleId
);
}
/**
*
* @param {string} varModuleId module id
* @param {string} varModule Module instance
* @param {RenderBootstrapContext} renderContext the render context
* @returns {TODO} renderAddModule call
*/
renderAddModule(varModuleId, varModule, renderContext) {
return this.hooks.addModule.call(
`modules[${varModuleId}] = ${varModule};`,
{
moduleId: varModuleId,
module: varModule
},
renderContext
);
}
/**
*
* @param {object} options get public path options

View File

@ -100,7 +100,37 @@ exports.chunkName = "__webpack_require__.cn";
*/
exports.getChunkScriptFilename = "__webpack_require__.u";
/**
* the filename of the script part of the hot update chunk
*/
exports.getChunkUpdateScriptFilename = "__webpack_require__.hu";
/**
* startup signal from runtime
*/
exports.startup = "__webpack_require__.x";
/**
* startup signal from runtime
*/
exports.interceptModuleExecution = "__webpack_require__.i";
/**
* the filename of the HMR manifest
*/
exports.getUpdateManifestFilename = "__webpack_require__.hmrF";
/**
* function downloading the update manifest
*/
exports.hmrDownloadManifest = "__webpack_require__.hmrM";
/**
* array with handler functions to download chunk updates
*/
exports.hmrDownloadUpdateHandlers = "__webpack_require__.hmrC";
/**
* object with all hmr module data for all modules
*/
exports.hmrModuleData = "__webpack_require__.hmrD";

View File

@ -12,13 +12,9 @@ const CreateFakeNamespaceObjectRuntimeModule = require("./runtime/CreateFakeName
const DefinePropertyGetterRuntimeModule = require("./runtime/DefinePropertyGetterRuntimeModule");
const EnsureChunkRuntimeModule = require("./runtime/EnsureChunkRuntimeModule");
const GetChunkFilenameRuntimeModule = require("./runtime/GetChunkFilenameRuntimeModule");
const GetMainFilenameRuntimeModule = require("./runtime/GetMainFilenameRuntimeModule");
const MakeNamespaceObjectRuntimeModule = require("./runtime/MakeNamespaceObjectRuntimeModule");
const PublicPathRuntimeModule = require("./runtime/PublicPathRuntimeModule");
const {
concatComparators,
compareSelect,
compareIds
} = require("./util/comparators");
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Compiler")} Compiler */
@ -119,12 +115,56 @@ class RuntimePlugin {
compilation,
chunk,
"javascript",
"javascript",
RuntimeGlobals.getChunkScriptFilename,
compilation.outputOptions.chunkFilename
)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.getChunkUpdateScriptFilename)
.tap("RuntimePlugin", (chunk, set) => {
if (
/\[hash(:\d+)?\]/.test(
compilation.outputOptions.hotUpdateChunkFilename
)
)
set.add(RuntimeGlobals.getFullHash);
compilation.addRuntimeModule(
chunk,
new GetChunkFilenameRuntimeModule(
compilation,
chunk,
"javascript",
"javascript update",
RuntimeGlobals.getChunkUpdateScriptFilename,
compilation.outputOptions.hotUpdateChunkFilename
)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.getUpdateManifestFilename)
.tap("RuntimePlugin", (chunk, set) => {
if (
/\[hash(:\d+)?\]/.test(
compilation.outputOptions.hotUpdateMainFilename
)
)
set.add(RuntimeGlobals.getFullHash);
compilation.addRuntimeModule(
chunk,
new GetMainFilenameRuntimeModule(
compilation,
chunk,
"update manifest",
RuntimeGlobals.getUpdateManifestFilename,
compilation.outputOptions.hotUpdateMainFilename
)
);
return true;
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunk)
.tap("RuntimePlugin", (chunk, set) => {
@ -142,17 +182,11 @@ class RuntimePlugin {
"RuntimePlugin",
(source, { chunk, chunkGraph }) => {
const buf = [];
const runtimeModules = Array.from(
chunkGraph.getChunkRuntimeModulesIterable(chunk)
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(
chunk
);
if (runtimeModules.length > 0) {
buf.push("// Bootstrap all runtime modules");
runtimeModules.sort(
concatComparators(
compareSelect(r => r.stage, compareIds),
compareSelect(r => chunkGraph.getModuleId(r), compareIds)
)
);
for (const module of runtimeModules) {
buf.push(
`// ${module.name}\nmodules[${JSON.stringify(

View File

@ -396,7 +396,7 @@ class RuntimeTemplate {
} else {
getModuleFunction = `${
RuntimeGlobals.createFakeNamespaceObject
}.bind(null, ${comment}${idExpr}, 3)`;
}.bind(__webpack_require__, ${comment}${idExpr}, 3)`;
}
} else if (strict) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
@ -407,7 +407,7 @@ class RuntimeTemplate {
} else {
getModuleFunction = `${
RuntimeGlobals.createFakeNamespaceObject
}.bind(null, ${comment}${idExpr}, 1)`;
}.bind(__webpack_require__, ${comment}${idExpr}, 1)`;
}
} else {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
@ -418,7 +418,7 @@ class RuntimeTemplate {
} else {
getModuleFunction = `${
RuntimeGlobals.createFakeNamespaceObject
}.bind(null, ${comment}${idExpr}, 7)`;
}.bind(__webpack_require__, ${comment}${idExpr}, 7)`;
}
}

View File

@ -190,6 +190,10 @@ class Stats {
);
const showDepth = optionOrLocalFallback(options.depth, !forToString);
const showCachedModules = optionOrLocalFallback(options.cached, true);
const showRuntimeModules = optionOrLocalFallback(
options.runtime,
!forToString
);
const showCachedAssets = optionOrLocalFallback(options.cachedAssets, true);
const showReasons = optionOrLocalFallback(options.reasons, !forToString);
const showUsedExports = optionOrLocalFallback(
@ -250,6 +254,10 @@ class Stats {
);
}
if (!showRuntimeModules) {
excludeModules.push((ident, module) => module.type === "runtime");
}
const createModuleFilter = type => {
let i = 0;
return module => {

View File

@ -266,12 +266,7 @@ class ProfilingPlugin {
}
const interceptTemplateInstancesFrom = (compilation, tracer) => {
const {
mainTemplate,
chunkTemplate,
hotUpdateChunkTemplate,
moduleTemplates
} = compilation;
const { mainTemplate, chunkTemplate, moduleTemplates } = compilation;
const { javascript, webassembly } = moduleTemplates;
@ -284,10 +279,6 @@ const interceptTemplateInstancesFrom = (compilation, tracer) => {
instance: chunkTemplate,
name: "ChunkTemplate"
},
{
instance: hotUpdateChunkTemplate,
name: "HotUpdateChunkTemplate"
},
{
instance: javascript,
name: "JavaScriptModuleTemplate"

View File

@ -0,0 +1,314 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
// eslint-disable-next-line no-unused-vars
/*global $getFullHash$:true, $interceptModuleExecution$, $moduleCache$, $hmrModuleData$:true, $hmrDownloadManifest$, $hmrDownloadUpdateHandlers$:true, __webpack_require__ */
module.exports = function() {
var currentHash = $getFullHash$();
var currentModuleData = {};
var installedModules = $moduleCache$;
// module and require creation
var currentChildModule;
var currentParents = [];
// status
var registeredStatusHandlers = [];
var currentStatus = "idle";
// while downloading
var blockingPromises;
// The update info
var currentUpdateApplyHandlers;
var currentUpdateNewHash;
$hmrModuleData$ = currentModuleData;
$getFullHash$ = function() {
return currentHash;
};
$interceptModuleExecution$.push(function(options) {
var module = options.module;
var require = createRequire(options.require, options.id);
module.hot = createModuleHotObject(options.id, module);
module.parents = currentParents;
module.children = [];
currentParents = [];
options.require = require;
});
$hmrDownloadUpdateHandlers$ = {};
function createRequire(require, moduleId) {
var me = installedModules[moduleId];
if (!me) return require;
var fn = function(request) {
if (me.hot.active) {
if (installedModules[request]) {
var parents = installedModules[request].parents;
if (parents.indexOf(moduleId) === -1) {
parents.push(moduleId);
}
} else {
currentParents = [moduleId];
currentChildModule = request;
}
if (me.children.indexOf(request) === -1) {
me.children.push(request);
}
} else {
console.trace(
"[HMR] unexpected require(" +
request +
") from disposed module " +
moduleId
);
currentParents = [];
}
return require(request);
};
var createPropertyDescriptor = function(name) {
return {
configurable: true,
enumerable: true,
get: function() {
return require[name];
},
set: function(value) {
require[name] = value;
}
};
};
for (var name in require) {
if (Object.prototype.hasOwnProperty.call(require, name) && name !== "e") {
Object.defineProperty(fn, name, createPropertyDescriptor(name));
}
}
fn.e = function(chunkId) {
return trackBlockingPromise(require.e(chunkId));
};
return fn;
}
function createModuleHotObject(moduleId, me) {
var hot = {
// private stuff
_acceptedDependencies: {},
_declinedDependencies: {},
_selfAccepted: false,
_selfDeclined: false,
_disposeHandlers: [],
_main: currentChildModule !== moduleId,
_requireSelf: function() {
currentParents = me.parents.slice();
currentChildModule = moduleId;
__webpack_require__(moduleId);
},
// Module API
active: true,
accept: function(dep, callback) {
if (dep === undefined) hot._selfAccepted = true;
else if (typeof dep === "function") hot._selfAccepted = dep;
else if (typeof dep === "object" && dep !== null)
for (var i = 0; i < dep.length; i++)
hot._acceptedDependencies[dep[i]] = callback || function() {};
else hot._acceptedDependencies[dep] = callback || function() {};
},
decline: function(dep) {
if (dep === undefined) hot._selfDeclined = true;
else if (typeof dep === "object" && dep !== null)
for (var i = 0; i < dep.length; i++)
hot._declinedDependencies[dep[i]] = true;
else hot._declinedDependencies[dep] = true;
},
dispose: function(callback) {
hot._disposeHandlers.push(callback);
},
addDisposeHandler: function(callback) {
hot._disposeHandlers.push(callback);
},
removeDisposeHandler: function(callback) {
var idx = hot._disposeHandlers.indexOf(callback);
if (idx >= 0) hot._disposeHandlers.splice(idx, 1);
},
// Management API
check: hotCheck,
apply: hotApply,
status: function(l) {
if (!l) return currentStatus;
registeredStatusHandlers.push(l);
},
addStatusHandler: function(l) {
registeredStatusHandlers.push(l);
},
removeStatusHandler: function(l) {
var idx = registeredStatusHandlers.indexOf(l);
if (idx >= 0) registeredStatusHandlers.splice(idx, 1);
},
//inherit from previous dispose call
data: currentModuleData[moduleId]
};
currentChildModule = undefined;
return hot;
}
function setStatus(newStatus) {
currentStatus = newStatus;
for (var i = 0; i < registeredStatusHandlers.length; i++)
registeredStatusHandlers[i].call(null, newStatus);
}
function trackBlockingPromise(promise) {
switch (currentStatus) {
case "ready":
setStatus("prepare");
blockingPromises.push(promise);
waitForBlockingPromises(function() {
setStatus("ready");
});
return promise;
case "prepare":
blockingPromises.push(promise);
return promise;
default:
return promise;
}
}
function waitForBlockingPromises(fn) {
if (blockingPromises.length === 0) return fn();
var blocker = blockingPromises;
blockingPromises = [];
return Promise.all(blocker).then(function() {
return waitForBlockingPromises(fn);
});
}
function hotCheck(applyOnUpdate) {
if (currentStatus !== "idle") {
throw new Error("check() is only allowed in idle status");
}
setStatus("check");
return $hmrDownloadManifest$().then(function(update) {
if (!update) {
setStatus("idle");
return null;
}
setStatus("prepare");
currentUpdateNewHash = update.h;
var updatedModules = [];
blockingPromises = [];
currentUpdateApplyHandlers = [];
return Promise.all(
Object.keys($hmrDownloadUpdateHandlers$).reduce(function(
promises,
key
) {
$hmrDownloadUpdateHandlers$[key](
update.c,
update.r,
update.m,
promises,
currentUpdateApplyHandlers,
updatedModules
);
return promises;
},
[])
).then(function() {
return waitForBlockingPromises(function() {
if (applyOnUpdate) {
return internalApply(applyOnUpdate);
} else {
setStatus("ready");
return updatedModules;
}
});
});
});
}
function hotApply(options) {
if (currentStatus !== "ready") {
return Promise.resolve().then(function() {
throw new Error("apply() is only allowed in ready status");
});
}
return internalApply(options);
}
function internalApply(options) {
options = options || {};
var results = currentUpdateApplyHandlers.map(function(handler) {
return handler(options);
});
var errors = results
.map(function(r) {
return r.error;
})
.filter(Boolean);
if (errors.length > 0) {
setStatus("abort");
return Promise.resolve().then(function() {
throw errors[0];
});
}
// Now in "dispose" phase
setStatus("dispose");
results.forEach(function(result) {
if (result.dispose) result.dispose();
});
// Now in "apply" phase
setStatus("apply");
currentHash = currentUpdateNewHash;
var error;
var reportError = function(err) {
if (!error) error = err;
};
var outdatedModules = [];
results.forEach(function(result) {
if (result.apply) {
var modules = result.apply(reportError);
if (modules) {
for (var i = 0; i < modules.length; i++) {
outdatedModules.push(modules[i]);
}
}
}
});
// handle errors in accept handlers and self accepted module load
if (error) {
setStatus("fail");
return Promise.resolve().then(function() {
throw error;
});
}
setStatus("idle");
return Promise.resolve(outdatedModules);
}
};

View File

@ -0,0 +1,38 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
class HotModuleReplacementRuntimeModule extends RuntimeModule {
constructor() {
super("hot module replacement", 5);
}
/**
* @returns {string} runtime code
*/
generate() {
return Template.getFunctionContent(
require("./HotModuleReplacement.runtime.js")
)
.replace(/\$getFullHash\$/g, RuntimeGlobals.getFullHash)
.replace(
/\$interceptModuleExecution\$/g,
RuntimeGlobals.interceptModuleExecution
)
.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
.replace(/\$hmrDownloadManifest\$/g, RuntimeGlobals.hmrDownloadManifest)
.replace(
/\$hmrDownloadUpdateHandlers\$/g,
RuntimeGlobals.hmrDownloadUpdateHandlers
);
}
}
module.exports = HotModuleReplacementRuntimeModule;

View File

@ -0,0 +1,364 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/*global $options$ $updateModuleFactories$ $updateRuntimeModules$ $moduleCache$ $moduleFactories$ $hmrModuleData$ __webpack_require__ */
module.exports = function() {
function getAffectedModuleEffects(updateModuleId) {
var outdatedModules = [updateModuleId];
var outdatedDependencies = {};
var queue = outdatedModules.slice().map(function(id) {
return {
chain: [id],
id: id
};
});
while (queue.length > 0) {
var queueItem = queue.pop();
var moduleId = queueItem.id;
var chain = queueItem.chain;
var module = $moduleCache$[moduleId];
if (!module || module.hot._selfAccepted) continue;
if (module.hot._selfDeclined) {
return {
type: "self-declined",
chain: chain,
moduleId: moduleId
};
}
if (module.hot._main) {
return {
type: "unaccepted",
chain: chain,
moduleId: moduleId
};
}
for (var i = 0; i < module.parents.length; i++) {
var parentId = module.parents[i];
var parent = $moduleCache$[parentId];
if (!parent) continue;
if (parent.hot._declinedDependencies[moduleId]) {
return {
type: "declined",
chain: chain.concat([parentId]),
moduleId: moduleId,
parentId: parentId
};
}
if (outdatedModules.indexOf(parentId) !== -1) continue;
if (parent.hot._acceptedDependencies[moduleId]) {
if (!outdatedDependencies[parentId])
outdatedDependencies[parentId] = [];
addAllToSet(outdatedDependencies[parentId], [moduleId]);
continue;
}
delete outdatedDependencies[parentId];
outdatedModules.push(parentId);
queue.push({
chain: chain.concat([parentId]),
id: parentId
});
}
}
return {
type: "accepted",
moduleId: updateModuleId,
outdatedModules: outdatedModules,
outdatedDependencies: outdatedDependencies
};
}
function addAllToSet(a, b) {
for (var i = 0; i < b.length; i++) {
var item = b[i];
if (a.indexOf(item) === -1) a.push(item);
}
}
// at begin all updates modules are outdated
// the "outdated" status can propagate to parents if they don't accept the children
var outdatedDependencies = {};
var outdatedModules = [];
var appliedUpdate = {};
var warnUnexpectedRequire = function warnUnexpectedRequire() {
console.warn(
"[HMR] unexpected require(" + result.moduleId + ") to disposed module"
);
};
for (var moduleId in $updateModuleFactories$) {
if (
Object.prototype.hasOwnProperty.call($updateModuleFactories$, moduleId)
) {
var newModuleFactory = $updateModuleFactories$[moduleId];
/** @type {TODO} */
var result;
if (newModuleFactory) {
result = getAffectedModuleEffects(moduleId);
} else {
result = {
type: "disposed",
moduleId: moduleId
};
}
/** @type {Error|false} */
var abortError = false;
var doApply = false;
var doDispose = false;
var chainInfo = "";
if (result.chain) {
chainInfo = "\nUpdate propagation: " + result.chain.join(" -> ");
}
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
);
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
);
break;
case "unaccepted":
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);
doApply = true;
break;
case "disposed":
if ($options$.onDisposed) $options$.onDisposed(result);
doDispose = true;
break;
default:
throw new Error("Unexception type " + result.type);
}
if (abortError) {
return {
error: abortError
};
}
if (doApply) {
appliedUpdate[moduleId] = newModuleFactory;
addAllToSet(outdatedModules, result.outdatedModules);
for (moduleId in result.outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(
result.outdatedDependencies,
moduleId
)
) {
if (!outdatedDependencies[moduleId])
outdatedDependencies[moduleId] = [];
addAllToSet(
outdatedDependencies[moduleId],
result.outdatedDependencies[moduleId]
);
}
}
}
if (doDispose) {
addAllToSet(outdatedModules, [result.moduleId]);
appliedUpdate[moduleId] = warnUnexpectedRequire;
}
}
}
// Store self accepted outdated modules to require them later by the module system
var outdatedSelfAcceptedModules = [];
for (var j = 0; j < outdatedModules.length; j++) {
var outdatedModuleId = outdatedModules[j];
if (
$moduleCache$[outdatedModuleId] &&
$moduleCache$[outdatedModuleId].hot._selfAccepted
)
outdatedSelfAcceptedModules.push({
module: outdatedModuleId,
require: $moduleCache$[outdatedModuleId].hot._requireSelf,
errorHandler: $moduleCache$[outdatedModuleId].hot._selfAccepted
});
}
var moduleOutdatedDependencies;
return {
dispose: function() {
// $dispose$
var idx;
var queue = outdatedModules.slice();
while (queue.length > 0) {
var moduleId = queue.pop();
var module = $moduleCache$[moduleId];
if (!module) continue;
var data = {};
// Call dispose handlers
var disposeHandlers = module.hot._disposeHandlers;
for (j = 0; j < disposeHandlers.length; j++) {
disposeHandlers[j].call(null, data);
}
$hmrModuleData$[moduleId] = data;
// disable module (this disables requires from this module)
module.hot.active = false;
// remove module from cache
delete $moduleCache$[moduleId];
// when disposing there is no need to call dispose handler
delete outdatedDependencies[moduleId];
// remove "parents" references from all children
for (j = 0; j < module.children.length; j++) {
var child = $moduleCache$[module.children[j]];
if (!child) continue;
idx = child.parents.indexOf(moduleId);
if (idx >= 0) {
child.parents.splice(idx, 1);
}
}
}
// remove outdated dependency from module children
var dependency;
for (var outdatedModuleId in outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(
outdatedDependencies,
outdatedModuleId
)
) {
module = $moduleCache$[outdatedModuleId];
if (module) {
moduleOutdatedDependencies = outdatedDependencies[outdatedModuleId];
for (j = 0; j < moduleOutdatedDependencies.length; j++) {
dependency = moduleOutdatedDependencies[j];
idx = module.children.indexOf(dependency);
if (idx >= 0) module.children.splice(idx, 1);
}
}
}
}
},
apply: function(reportError) {
// insert new code
for (var updateModuleId in appliedUpdate) {
if (
Object.prototype.hasOwnProperty.call(appliedUpdate, updateModuleId)
) {
$moduleFactories$[updateModuleId] = appliedUpdate[updateModuleId];
}
}
// run new runtime modules
for (var i = 0; i < $updateRuntimeModules$.length; i++) {
appliedUpdate[$updateRuntimeModules$[i]](0, 0, __webpack_require__);
}
// call accept handlers
var error = null;
for (var outdatedModuleId in outdatedDependencies) {
if (
Object.prototype.hasOwnProperty.call(
outdatedDependencies,
outdatedModuleId
)
) {
var module = $moduleCache$[outdatedModuleId];
if (module) {
moduleOutdatedDependencies = outdatedDependencies[outdatedModuleId];
var callbacks = [];
for (var j = 0; j < moduleOutdatedDependencies.length; j++) {
var dependency = moduleOutdatedDependencies[j];
var acceptCallback = module.hot._acceptedDependencies[dependency];
if (acceptCallback) {
if (callbacks.indexOf(acceptCallback) !== -1) continue;
callbacks.push(acceptCallback);
}
}
for (var k = 0; k < callbacks.length; k++) {
try {
callbacks[k].call(null, moduleOutdatedDependencies);
} catch (err) {
if ($options$.onErrored) {
$options$.onErrored({
type: "accept-errored",
moduleId: outdatedModuleId,
dependencyId: moduleOutdatedDependencies[i],
error: err
});
}
if (!$options$.ignoreErrored) {
if (!error) error = err;
}
}
}
}
}
}
// Load self accepted modules
for (var o = 0; o < outdatedSelfAcceptedModules.length; o++) {
var item = outdatedSelfAcceptedModules[o];
var moduleId = item.module;
try {
item.require(moduleId);
} catch (err) {
if (typeof item.errorHandler === "function") {
try {
item.errorHandler(err);
} catch (err2) {
if ($options$.onErrored) {
$options$.onErrored({
type: "self-accept-error-handler-errored",
moduleId: moduleId,
error: err2,
originalError: err
});
}
if (!$options$.ignoreErrored) {
reportError(err2);
}
reportError(err);
}
} else {
if ($options$.onErrored) {
$options$.onErrored({
type: "self-accept-errored",
moduleId: moduleId,
error: err
});
}
if (!$options$.ignoreErrored) {
reportError(err);
}
}
}
}
return outdatedModules;
}
};
};

View File

@ -9,7 +9,16 @@ const { ConcatSource } = require("webpack-sources");
/** @typedef {import("../ChunkTemplate")} ChunkTemplate */
const getRuntimeModuleIds = (chunkGraph, chunk) => {
return chunkGraph
.getChunkRuntimeModulesInOrder(chunk)
.map(m => chunkGraph.getModuleId(m));
};
class NodeChunkTemplatePlugin {
constructor(compilation) {
this.compilation = compilation;
}
/**
* @param {ChunkTemplate} chunkTemplate the chunk template
* @returns {void}
@ -17,19 +26,30 @@ class NodeChunkTemplatePlugin {
apply(chunkTemplate) {
chunkTemplate.hooks.render.tap(
"NodeChunkTemplatePlugin",
(modules, moduleTemplate, { chunk }) => {
(modules, moduleTemplate, { chunk, chunkGraph }) => {
const source = new ConcatSource();
source.add(
`exports.ids = ${JSON.stringify(chunk.ids)};\nexports.modules = `
);
source.add(`exports.id = ${JSON.stringify(chunk.id)};\n`);
source.add(`exports.ids = ${JSON.stringify(chunk.ids)};\n`);
source.add(`exports.modules = `);
source.add(modules);
source.add(";");
source.add(";\n");
const runtimeModules = getRuntimeModuleIds(chunkGraph, chunk);
if (runtimeModules.length > 0) {
source.add(`exports.runtime = ${JSON.stringify(runtimeModules)};\n`);
}
return source;
}
);
chunkTemplate.hooks.hashForChunk.tap(
"NodeChunkTemplatePlugin",
(hash, chunk) => {
const chunkGraph = this.compilation.chunkGraph;
hash.update(JSON.stringify(getRuntimeModuleIds(chunkGraph, chunk)));
}
);
chunkTemplate.hooks.hash.tap("NodeChunkTemplatePlugin", hash => {
hash.update("node");
hash.update("3");
hash.update("5");
});
}
}

View File

@ -1,43 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
/** @typedef {import("../HotUpdateChunkTemplate")} HotUpdateChunkTemplate */
class NodeHotUpdateChunkTemplatePlugin {
/**
* @param {HotUpdateChunkTemplate} hotUpdateChunkTemplate the hot update chunk template
* @returns {void}
*/
apply(hotUpdateChunkTemplate) {
hotUpdateChunkTemplate.hooks.render.tap(
"NodeHotUpdateChunkTemplatePlugin",
(modulesSource, moduleTemplate, { chunk }) => {
const source = new ConcatSource();
source.add(
"exports.id = " + JSON.stringify(chunk.id) + ";\nexports.modules = "
);
source.add(modulesSource);
source.add(";");
return source;
}
);
hotUpdateChunkTemplate.hooks.hash.tap(
"NodeHotUpdateChunkTemplatePlugin",
hash => {
hash.update("NodeHotUpdateChunkTemplatePlugin");
hash.update("3");
hash.update(
hotUpdateChunkTemplate.outputOptions.hotUpdateFunction + ""
);
hash.update(hotUpdateChunkTemplate.outputOptions.library + "");
}
);
}
}
module.exports = NodeHotUpdateChunkTemplatePlugin;

View File

@ -1,30 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/*global installedChunks $hotChunkFilename$ hotAddUpdateChunk $hotMainFilename$ */
module.exports = function() {
// eslint-disable-next-line no-unused-vars
function hotDownloadUpdateChunk(chunkId) {
var chunk = require("./" + $hotChunkFilename$);
hotAddUpdateChunk(chunk.id, chunk.modules);
}
// eslint-disable-next-line no-unused-vars
function hotDownloadManifest() {
try {
var update = require("./" + $hotMainFilename$);
} catch (e) {
return Promise.resolve();
}
return Promise.resolve(update);
}
//eslint-disable-next-line no-unused-vars
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
};

View File

@ -1,48 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/*global installedChunks $hotChunkFilename$ hotAddUpdateChunk $hotMainFilename$ $onError$ */
module.exports = function() {
// eslint-disable-next-line no-unused-vars
function hotDownloadUpdateChunk(chunkId) {
var filename = require("path").join(__dirname, $hotChunkFilename$);
require("fs").readFile(filename, "utf-8", function(err, content) {
if (err) {
if ($onError$) return $onError$(err);
throw err;
}
var chunk = {};
require("vm").runInThisContext(
"(function(exports) {" + content + "\n})",
{ filename: filename }
)(chunk);
hotAddUpdateChunk(chunk.id, chunk.modules);
});
}
// eslint-disable-next-line no-unused-vars
function hotDownloadManifest() {
var filename = require("path").join(__dirname, $hotMainFilename$);
return new Promise(function(resolve, reject) {
require("fs").readFile(filename, "utf-8", function(err, content) {
if (err) return resolve();
try {
var update = JSON.parse(content);
} catch (e) {
return reject(e);
}
resolve(update);
});
});
}
// eslint-disable-next-line no-unused-vars
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
};

View File

@ -5,11 +5,8 @@
"use strict";
const HotModuleReplacementPlugin = require("../HotModuleReplacementPlugin");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const NodeChunkTemplatePlugin = require("./NodeChunkTemplatePlugin");
const NodeHotUpdateChunkTemplatePlugin = require("./NodeHotUpdateChunkTemplatePlugin");
const ReadFileChunkLoadingRuntimeModule = require("./ReadFileChunkLoadingRuntimeModule");
const RequireChunkLoadingRuntimeModule = require("./RequireChunkLoadingRuntimeModule");
@ -27,88 +24,57 @@ class NodeTemplatePlugin {
*/
apply(compiler) {
compiler.hooks.thisCompilation.tap("NodeTemplatePlugin", compilation => {
new NodeChunkTemplatePlugin().apply(compilation.chunkTemplate);
new NodeHotUpdateChunkTemplatePlugin().apply(
compilation.hotUpdateChunkTemplate
);
new NodeChunkTemplatePlugin(compilation).apply(compilation.chunkTemplate);
const onceForChunkSet = new WeakSet();
const handler = (chunk, set) => {
if (onceForChunkSet.has(chunk)) return;
onceForChunkSet.add(chunk);
set.add(RuntimeGlobals.moduleFactories);
if (this.asyncChunkLoading) {
compilation.addRuntimeModule(
chunk,
new ReadFileChunkLoadingRuntimeModule(chunk, set)
);
} else {
compilation.addRuntimeModule(
chunk,
new RequireChunkLoadingRuntimeModule(chunk, set)
);
}
};
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap("NodeTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.startup)
.tap("NodeTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap("NodeTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadManifest)
.tap("NodeTemplatePlugin", handler);
const mainTemplate = compilation.mainTemplate;
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap("NodeTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.moduleFactories);
set.add(RuntimeGlobals.getChunkScriptFilename);
if (this.asyncChunkLoading) {
compilation.addRuntimeModule(
chunk,
new ReadFileChunkLoadingRuntimeModule(chunk)
);
} else {
compilation.addRuntimeModule(
chunk,
new RequireChunkLoadingRuntimeModule(chunk)
);
}
});
const { hotBootstrap } = HotModuleReplacementPlugin.getMainTemplateHooks(
mainTemplate
);
hotBootstrap.tap("NodeTemplatePlugin", (source, chunk, hash) => {
const hotUpdateChunkFilename =
mainTemplate.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename =
mainTemplate.outputOptions.hotUpdateMainFilename;
const chunkMaps = chunk.getChunkMaps();
const currentHotUpdateChunkFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateChunkFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
chunk: {
id: '" + chunkId + "',
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
hashWithLength: length => {
const shortChunkHashMap = {};
for (const chunkId of Object.keys(chunkMaps.hash)) {
if (typeof chunkMaps.hash[chunkId] === "string") {
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(
0,
length
);
}
}
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
},
name: `" + (${JSON.stringify(
chunkMaps.name
)}[chunkId]||chunkId) + "`
}
}
);
const currentHotUpdateMainFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateMainFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`
}
);
return Template.getFunctionContent(
this.asyncChunkLoading
? require("./NodeMainTemplateAsync.runtime")
: require("./NodeMainTemplate.runtime")
)
.replace(/\$onError\$/g, RuntimeGlobals.uncaughtErrorHandler)
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename);
});
mainTemplate.hooks.hash.tap("NodeTemplatePlugin", hash => {
hash.update("node");
hash.update("4");
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap("NodeTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
set.add(RuntimeGlobals.moduleCache);
set.add(RuntimeGlobals.hmrModuleData);
set.add(RuntimeGlobals.moduleFactories);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadManifest)
.tap("NodeTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.getUpdateManifestFilename);
});
});
}
}

View File

@ -9,9 +9,10 @@ const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
constructor(chunk) {
constructor(chunk, runtimeRequirements) {
super("readFile chunk loading", 10);
this.chunk = chunk;
this.runtimeRequirements = runtimeRequirements;
}
/**
@ -20,6 +21,15 @@ class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
generate() {
const { chunk } = this;
const fn = RuntimeGlobals.ensureChunkHandlers;
const withLoading = this.runtimeRequirements.has(
RuntimeGlobals.ensureChunkHandlers
);
const withHmr = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadUpdateHandlers
);
const withHmrManifest = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadManifest
);
return Template.asString([
"// object to store loaded chunks",
'// "0" means "already loaded", Promise means loading',
@ -29,66 +39,189 @@ class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
),
"};",
"",
"// ReadFile + VM.run chunk loading for javascript",
`${fn}.push(function(chunkId, promises) {`,
Template.indent([
"",
"var installedChunkData = installedChunks[chunkId];",
'if(installedChunkData !== 0) { // 0 means "already installed".',
Template.indent([
'// array of [resolve, reject, promise] means "currently loading"',
"if(installedChunkData) {",
Template.indent(["promises.push(installedChunkData[2]);"]),
"} else {",
Template.indent([
"// load the chunk and return promise to it",
"var promise = new Promise(function(resolve, reject) {",
withLoading
? Template.asString([
"// ReadFile + VM.run chunk loading for javascript",
`${fn}.push(function(chunkId, promises) {`,
Template.indent([
"installedChunkData = installedChunks[chunkId] = [resolve, reject];",
`var filename = require('path').join(__dirname, ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId));`,
"require('fs').readFile(filename, 'utf-8', function(err, content) {",
"",
"var installedChunkData = installedChunks[chunkId];",
'if(installedChunkData !== 0) { // 0 means "already installed".',
Template.indent([
"if(err) return reject(err);",
"var chunk = {};",
"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
"(chunk, require, require('path').dirname(filename), filename);",
"var moreModules = chunk.modules, chunkIds = chunk.ids;",
"for(var moduleId in moreModules) {",
'// array of [resolve, reject, promise] means "currently loading"',
"if(installedChunkData) {",
Template.indent(["promises.push(installedChunkData[2]);"]),
"} else {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
"// load the chunk and return promise to it",
"var promise = new Promise(function(resolve, reject) {",
Template.indent([
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
"installedChunkData = installedChunks[chunkId] = [resolve, reject];",
`var filename = require('path').join(__dirname, ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId));`,
"require('fs').readFile(filename, 'utf-8', function(err, content) {",
Template.indent([
"if(err) return reject(err);",
"var chunk = {};",
"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
"(chunk, require, require('path').dirname(filename), filename);",
"var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;",
"for(var moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent([
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
]),
"}"
]),
"}",
`if(runtime) for(var i = 0; i < runtime.length; i++) moreModules[runtime[i]](0,0,__webpack_require__);`,
"var callbacks = [];",
"for(var i = 0; i < chunkIds.length; i++) {",
Template.indent([
"if(installedChunks[chunkIds[i]])",
Template.indent([
"callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);"
]),
"installedChunks[chunkIds[i]] = 0;"
]),
"}",
"for(i = 0; i < callbacks.length; i++)",
Template.indent("callbacks[i]();")
]),
"});"
]),
"}"
"});",
"promises.push(installedChunkData[2] = promise);",
"",
withHmr
? Template.asString([
"if(currentUpdateChunks && currentUpdateChunks[chunkId]) promises.push(loadUpdateChunk(chunkId));"
])
: "// no HMR"
]),
"}",
"var callbacks = [];",
"for(var i = 0; i < chunkIds.length; i++) {",
"}"
]),
"}"
]),
"})"
])
: "// no chunk loading",
"",
withHmr
? Template.asString([
"var currentUpdateChunks;",
"var currentUpdate;",
"var currentUpdateRuntime;",
"function loadUpdateChunk(chunkId, updatedModulesList) {",
Template.indent([
"return new Promise(function(resolve, reject) {",
Template.indent([
`var filename = require('path').join(__dirname, ${
RuntimeGlobals.getChunkUpdateScriptFilename
}(chunkId));`,
"require('fs').readFile(filename, 'utf-8', function(err, content) {",
Template.indent([
"if(installedChunks[chunkIds[i]])",
"if(err) return reject(err);",
"var update = {};",
"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
"(update, require, require('path').dirname(filename), filename);",
"var updatedModules = update.modules;",
"var runtime = update.runtime;",
"for(var moduleId in updatedModules) {",
Template.indent([
"callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);"
"if(Object.prototype.hasOwnProperty.call(updatedModules, moduleId)) {",
Template.indent([
`currentUpdate[moduleId] = updatedModules[moduleId];`,
"if(updatedModulesList) updatedModulesList.push(moduleId);"
]),
"}"
]),
"installedChunks[chunkIds[i]] = 0;"
"}",
"if(runtime) for(var i = 0; i < runtime.length; i++) currentUpdateRuntime.push(runtime[i]);",
"resolve();"
]),
"}",
"for(i = 0; i < callbacks.length; i++)",
Template.indent("callbacks[i]();")
"});"
]),
"});"
]),
"});",
"promises.push(installedChunkData[2] = promise);"
]),
"}"
]),
"}"
]),
"})"
"}",
"",
`${
RuntimeGlobals.hmrDownloadUpdateHandlers
}.readFileVm = function(chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList) {`,
Template.indent([
"applyHandlers.push(function(options) {",
Template.indent([
"currentUpdateChunks = undefined;",
Template.getFunctionContent(
require("../hmr/JavascriptHotModuleReplacement.runtime.js")
)
.replace(/\$options\$/g, "options")
.replace(/\$updateModuleFactories\$/g, "currentUpdate")
.replace(/\$updateRuntimeModules\$/g, "currentUpdateRuntime")
.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
.replace(
/\$moduleFactories\$/g,
RuntimeGlobals.moduleFactories
)
.replace(
/\/\/ \$dispose\$/g,
Template.asString([
"removedChunks.forEach(function(chunkId) { delete installedChunks[chunkId]; });"
])
)
]),
"});",
"currentUpdateChunks = {};",
"currentUpdate = removedModules.reduce(function(obj, key) { obj[key] = false; return obj; }, {});",
"currentUpdateRuntime = [];",
"chunkIds.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] !== undefined) {",
Template.indent([
"promises.push(loadUpdateChunk(chunkId, updatedModulesList));"
]),
"}",
"currentUpdateChunks[chunkId] = true;"
]),
"});"
]),
"};"
])
: "// no HMR",
"",
withHmrManifest
? Template.asString([
`${RuntimeGlobals.hmrDownloadManifest} = function() {`,
Template.indent([
"return new Promise(function(resolve, reject) {",
Template.indent([
`var filename = require('path').join(__dirname, ${
RuntimeGlobals.getUpdateManifestFilename
}());`,
"require('fs').readFile(filename, 'utf-8', function(err, content) {",
Template.indent([
"if(err) {",
Template.indent([
'if(err.code === "ENOENT") return resolve();',
"return reject(err);"
]),
"}",
"try { resolve(JSON.parse(content)); }",
"catch(e) { reject(e); }"
]),
"});"
]),
"});"
]),
"}"
])
: "// no HMR manifest"
]);
}
}

View File

@ -8,10 +8,11 @@ const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
constructor(chunk) {
class RequireChunkLoadingRuntimeModule extends RuntimeModule {
constructor(chunk, runtimeRequirements) {
super("require chunk loading", 10);
this.chunk = chunk;
this.runtimeRequirements = runtimeRequirements;
}
/**
@ -20,6 +21,15 @@ class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
generate() {
const { chunk } = this;
const fn = RuntimeGlobals.ensureChunkHandlers;
const withLoading = this.runtimeRequirements.has(
RuntimeGlobals.ensureChunkHandlers
);
const withHmr = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadUpdateHandlers
);
const withHmrManifest = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadManifest
);
return Template.asString([
"// object to store loaded chunks",
'// "1" means "loaded", otherwise not loaded yet',
@ -29,36 +39,134 @@ class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
),
"};",
"",
"// require() chunk loading for javascript",
`${fn}.push(function(chunkId, promises) {`,
Template.indent([
"",
'// "0" is the signal for "already loaded"',
"if(!installedChunks[chunkId]) {",
Template.indent([
`var chunk = require("./" + ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId));`,
"var moreModules = chunk.modules, chunkIds = chunk.ids;",
"for(var moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
withLoading
? Template.asString([
"// require() chunk loading for javascript",
`${fn}.push(function(chunkId, promises) {`,
Template.indent([
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
"",
'// "0" is the signal for "already loaded"',
"if(!installedChunks[chunkId]) {",
Template.indent([
`var chunk = require("./" + ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId));`,
"var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;",
"for(var moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent([
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
]),
"}"
]),
"}",
`if(runtime) for(var i = 0; i < runtime.length; i++) moreModules[runtime[i]](0,0,__webpack_require__);`,
"for(var i = 0; i < chunkIds.length; i++)",
Template.indent("installedChunks[chunkIds[i]] = 1;"),
"",
withHmr
? Template.asString([
"if(currentUpdateChunks && currentUpdateChunks[chunkId]) loadUpdateChunk(chunkId);"
])
: "// no HMR"
]),
"}"
]),
"});"
])
: "// no chunk loading",
"",
withHmr
? Template.asString([
"var currentUpdateChunks;",
"var currentUpdate;",
"var currentUpdateRuntime;",
"function loadUpdateChunk(chunkId, updatedModulesList) {",
Template.indent([
`var update = require("./" + ${
RuntimeGlobals.getChunkUpdateScriptFilename
}(chunkId));`,
"var updatedModules = update.modules;",
"var runtime = update.runtime;",
"for(var moduleId in updatedModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(updatedModules, moduleId)) {",
Template.indent([
`currentUpdate[moduleId] = updatedModules[moduleId];`,
"if(updatedModulesList) updatedModulesList.push(moduleId);"
]),
"}"
]),
"}",
"if(runtime) for(var i = 0; i < runtime.length; i++) currentUpdateRuntime.push(runtime[i]);"
]),
"}",
"",
`${
RuntimeGlobals.hmrDownloadUpdateHandlers
}.require = function(chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList) {`,
Template.indent([
"applyHandlers.push(function(options) {",
Template.indent([
"currentUpdateChunks = undefined;",
Template.getFunctionContent(
require("../hmr/JavascriptHotModuleReplacement.runtime.js")
)
.replace(/\$options\$/g, "options")
.replace(/\$updateModuleFactories\$/g, "currentUpdate")
.replace(/\$updateRuntimeModules\$/g, "currentUpdateRuntime")
.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
.replace(
/\$moduleFactories\$/g,
RuntimeGlobals.moduleFactories
)
.replace(
/\/\/ \$dispose\$/g,
Template.asString([
"removedChunks.forEach(function(chunkId) { delete installedChunks[chunkId]; });"
])
)
]),
"});",
"currentUpdateChunks = {};",
"currentUpdate = removedModules.reduce(function(obj, key) { obj[key] = false; return obj; }, {});",
"currentUpdateRuntime = [];",
"chunkIds.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] !== undefined) {",
Template.indent([
"loadUpdateChunk(chunkId, updatedModulesList);"
]),
"}",
"currentUpdateChunks[chunkId] = true;"
]),
"});"
]),
"};"
])
: "// no HMR",
"",
withHmrManifest
? Template.asString([
`${RuntimeGlobals.hmrDownloadManifest} = function() {`,
Template.indent([
"return Promise.resolve().then(function() {",
Template.indent([
`return require("./" + ${
RuntimeGlobals.getUpdateManifestFilename
}());`
]),
'}).catch(function(err) { if(err.code !== "MODULE_NOT_FOUND") throw err; });'
]),
"}"
]),
"}",
"for(var i = 0; i < chunkIds.length; i++)",
Template.indent("installedChunks[chunkIds[i]] = 1;")
]),
"}"
]),
"})"
])
: "// no HMR manifest"
]);
}
}
module.exports = ReadFileChunkLoadingRuntimeModule;
module.exports = RequireChunkLoadingRuntimeModule;

View File

@ -26,7 +26,7 @@ class CreateFakeNamespaceObjectRuntimeModule extends HelperRuntimeModule {
"// mode & 8|1: behave like require",
`${fn} = function(value, mode) {`,
Template.indent([
`if(mode & 1) value = __webpack_require__(value);`,
`if(mode & 1) value = this(value);`,
`if(mode & 8) return value;`,
"if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;",
"var ns = Object.create(null);",

View File

@ -9,8 +9,8 @@ const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
class GetChunkFilenameRuntimeModule extends RuntimeModule {
constructor(compilation, chunk, contentType, global, filename) {
super(`get chunk ${contentType} filename`);
constructor(compilation, chunk, contentType, name, global, filename) {
super(`get ${name} chunk filename`);
this.compilation = compilation;
this.chunk = chunk;
this.contentType = contentType;

View File

@ -0,0 +1,39 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
class GetMainFilenameRuntimeModule extends RuntimeModule {
constructor(compilation, chunk, name, global, filename) {
super(`get ${name} filename`);
this.compilation = compilation;
this.chunk = chunk;
this.global = global;
this.filename = filename;
}
/**
* @returns {string} runtime code
*/
generate() {
const { global, filename, compilation } = this;
const mainTemplate = compilation.mainTemplate;
const url = mainTemplate.getAssetPath(JSON.stringify(filename), {
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`
});
return Template.asString([
`${global} = function() {`,
Template.indent([`return ${url};`]),
"};"
]);
}
}
module.exports = GetMainFilenameRuntimeModule;

View File

@ -51,6 +51,12 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
RuntimeGlobals.ensureChunkHandlers
);
const withDefer = this.runtimeRequirements.has(RuntimeGlobals.startup);
const withHmr = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadUpdateHandlers
);
const withHmrManifest = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadManifest
);
const withPrefetch = needPrefetchingCode(chunk);
const preloadChunkMap = chunk.getChildIdsByOrdersMap(chunkGraph).preload;
const withPreload =
@ -111,8 +117,16 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
"promises.push(installedChunkData[2] = promise);",
"",
"// start chunk loading",
`var url = ${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId);`,
"var loadingEnded = function() { if(installedChunks[chunkId]) return installedChunks[chunkId][1]; if(installedChunks[chunkId] !== 0) installedChunks[chunkId] = undefined; };",
jsonpScript.call("", chunk),
"head.appendChild(script);"
"head.appendChild(script);",
"",
withHmr
? "if(currentUpdateChunks && currentUpdateChunks[chunkId]) promises.push(loadUpdateChunk(chunkId));"
: "// no HMR"
]),
"}"
]),
@ -164,58 +178,126 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
])
: "// no prefetching",
"",
"// install a JSONP callback for chunk loading",
"function webpackJsonpCallback(data) {",
Template.indent([
"var chunkIds = data[0];",
"var moreModules = data[1];",
withDefer ? "var executeModules = data[2];" : "",
withPrefetch ? "var prefetchChunks = data[3] || [];" : "",
'// add "moreModules" to the modules object,',
'// then flag all "chunkIds" as loaded and fire callback',
"var moduleId, chunkId, i = 0, resolves = [];",
"for(;i < chunkIds.length; i++) {",
Template.indent([
"chunkId = chunkIds[i];",
"if(installedChunks[chunkId]) {",
Template.indent("resolves.push(installedChunks[chunkId][0]);"),
"}",
"installedChunks[chunkId] = 0;"
]),
"}",
"for(moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent(
withHmr
? Template.asString([
"var currentUpdateChunks;",
"var currentUpdate;",
"var currentUpdateRuntime;",
"var currentUpdatedModulesList;",
"var waitingUpdateResolves = {};",
"var head = document.getElementsByTagName('head')[0];",
"function loadUpdateChunk(chunkId) {",
Template.indent([
"return new Promise(function(resolve, reject) {",
Template.indent([
"waitingUpdateResolves[chunkId] = resolve;",
"// start update chunk loading",
`var url = ${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getChunkUpdateScriptFilename
}(chunkId);`,
"var loadingEnded = function() {",
Template.indent([
"if(waitingUpdateResolves[chunkId]) {",
Template.indent([
"waitingUpdateResolves[chunkId] = undefined",
"return reject;"
]),
"}"
]),
"};",
jsonpScript.call("", chunk),
"head.appendChild(script);"
]),
"});"
]),
"}",
"",
`${outputOptions.globalObject}[${JSON.stringify(
outputOptions.hotUpdateFunction
)}] = function(chunkId, moreModules, runtime) {`,
Template.indent([
"for(moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent([
"currentUpdate[moduleId] = moreModules[moduleId];",
"if(currentUpdatedModulesList) currentUpdatedModulesList.push(moduleId);"
]),
"}"
]),
"}",
"if(runtime) for(var i = 0; i < runtime.length; i++) currentUpdateRuntime.push(runtime[i]);",
"if(waitingUpdateResolves[chunkId]) {",
Template.indent([
"waitingUpdateResolves[chunkId]();",
"waitingUpdateResolves[chunkId] = undefined;"
]),
"}"
]),
"};",
"",
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
),
"}"
]),
"}",
"if(parentJsonpFunction) parentJsonpFunction(data);",
withPrefetch
? Template.asString([
"// chunk prefetching for javascript",
"prefetchChunks.forEach(prefetchChunk);"
])
: "",
"while(resolves.length) {",
Template.indent("resolves.shift()();"),
"}",
withDefer
? Template.asString([
"",
"// add entry modules from loaded chunk to deferred list",
"deferredModules.push.apply(deferredModules, executeModules || []);",
"",
"// run deferred modules when all chunks ready",
"return checkDeferredModules();"
])
: ""
]),
"};",
RuntimeGlobals.hmrDownloadUpdateHandlers
}.jsonp = function(chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList) {`,
Template.indent([
"applyHandlers.push(function(options) {",
Template.indent([
"currentUpdateChunks = undefined;",
Template.getFunctionContent(
require("../hmr/JavascriptHotModuleReplacement.runtime.js")
)
.replace(/\$options\$/g, "options")
.replace(/\$updateModuleFactories\$/g, "currentUpdate")
.replace(/\$updateRuntimeModules\$/g, "currentUpdateRuntime")
.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
.replace(
/\$moduleFactories\$/g,
RuntimeGlobals.moduleFactories
)
.replace(
/\/\/ \$dispose\$/g,
Template.asString([
"removedChunks.forEach(function(chunkId) { delete installedChunks[chunkId]; });"
])
)
]),
"});",
"currentUpdateChunks = {};",
"currentUpdate = removedModules.reduce(function(obj, key) { obj[key] = false; return obj; }, {});",
"currentUpdateRuntime = [];",
"currentUpdatedModulesList = updatedModulesList;",
"chunkIds.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] !== undefined) {",
Template.indent(["promises.push(loadUpdateChunk(chunkId));"]),
"}",
"currentUpdateChunks[chunkId] = true;"
]),
"});"
]),
"}"
])
: "// no HMR",
"",
withHmrManifest
? Template.asString([
`${RuntimeGlobals.hmrDownloadManifest} = function() {`,
Template.indent([
'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");',
`return fetch(${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getUpdateManifestFilename
}()).then(function(response) {`,
Template.indent([
"if(response.status === 404) return; // no update available",
'if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText);',
"return response.json();"
]),
"});"
]),
"};"
])
: "// no HMR manifest",
"",
withDefer
? Template.asString([
@ -254,16 +336,75 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
])
: "// no deferred startup",
"",
`var jsonpArray = ${jsonpObject} = ${jsonpObject} || [];`,
"var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);",
"jsonpArray.push = webpackJsonpCallback;",
withDefer
withDefer || withLoading
? Template.asString([
"jsonpArray = jsonpArray.slice();",
"for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);"
"// install a JSONP callback for chunk loading",
"function webpackJsonpCallback(data) {",
Template.indent([
"var chunkIds = data[0];",
"var moreModules = data[1];",
withDefer ? "var executeModules = data[2];" : "",
"var runtime = data[3];",
withPrefetch ? "var prefetchChunks = data[4];" : "",
'// add "moreModules" to the modules object,',
'// then flag all "chunkIds" as loaded and fire callback',
"var moduleId, chunkId, i = 0, resolves = [];",
"for(;i < chunkIds.length; i++) {",
Template.indent([
"chunkId = chunkIds[i];",
"if(installedChunks[chunkId]) {",
Template.indent("resolves.push(installedChunks[chunkId][0]);"),
"}",
"installedChunks[chunkId] = 0;"
]),
"}",
"for(moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent(
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
),
"}"
]),
"}",
"if(runtime) for(var i = 0; i < runtime.length; i++) moreModules[runtime[i]](0,0,__webpack_require__);",
"if(parentJsonpFunction) parentJsonpFunction(data);",
withPrefetch
? Template.asString([
"// chunk prefetching for javascript",
"if(prefetchChunks) prefetchChunks.forEach(prefetchChunk);"
])
: "",
"while(resolves.length) {",
Template.indent("resolves.shift()();"),
"}",
withDefer
? Template.asString([
"",
"// add entry modules from loaded chunk to deferred list",
"if(executeModules) deferredModules.push.apply(deferredModules, executeModules);",
"",
"// run deferred modules when all chunks ready",
"return checkDeferredModules();"
])
: ""
]),
"};",
"",
`var jsonpArray = ${jsonpObject} = ${jsonpObject} || [];`,
"var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);",
"jsonpArray.push = webpackJsonpCallback;",
withDefer
? Template.asString([
"jsonpArray = jsonpArray.slice();",
"for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);"
])
: "",
"var parentJsonpFunction = oldJsonpFunction;"
])
: "",
"var parentJsonpFunction = oldJsonpFunction;"
: "// no jsonp function"
]);
}
}

View File

@ -30,31 +30,53 @@ class JsonpChunkTemplatePlugin {
"JsonpChunkTemplatePlugin",
(modules, moduleTemplate, { chunk, chunkGraph }) => {
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
const jsonpFunction = hotUpdateChunk
? chunkTemplate.outputOptions.hotUpdateFunction
: chunkTemplate.outputOptions.jsonpFunction;
const globalObject = chunkTemplate.outputOptions.globalObject;
const source = new ConcatSource();
const prefetchChunks = chunk.getChildIdsByOrders(chunkGraph).prefetch;
source.add(
`(${globalObject}[${JSON.stringify(
jsonpFunction
)}] = ${globalObject}[${JSON.stringify(
jsonpFunction
)}] || []).push([${JSON.stringify(chunk.ids)},`
);
source.add(modules);
const entries = getEntryInfo(chunkGraph, chunk);
if (entries.length > 0) {
source.add(`,${JSON.stringify(entries)}`);
} else if (prefetchChunks && prefetchChunks.length) {
source.add(`,0`);
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
const runtimePart =
runtimeModules.length > 0 &&
`,${JSON.stringify(
runtimeModules.map(m => chunkGraph.getModuleId(m))
)}`;
if (hotUpdateChunk) {
const jsonpFunction = chunkTemplate.outputOptions.hotUpdateFunction;
source.add(`${globalObject}[${JSON.stringify(jsonpFunction)}](`);
source.add(`${JSON.stringify(chunk.id)},`);
source.add(modules);
if (runtimePart) {
source.add(runtimePart);
}
source.add(")");
} else {
const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction;
source.add(
`(${globalObject}[${JSON.stringify(
jsonpFunction
)}] = ${globalObject}[${JSON.stringify(
jsonpFunction
)}] || []).push([`
);
source.add(`${JSON.stringify(chunk.ids)},`);
source.add(modules);
const entries = getEntryInfo(chunkGraph, chunk);
const prefetchChunks = chunk.getChildIdsByOrders(chunkGraph).prefetch;
const entriesPart =
entries.length > 0 && `,${JSON.stringify(entries)}`;
const prefetchPart =
prefetchChunks &&
prefetchChunks.length > 0 &&
`,${JSON.stringify(prefetchChunks)}`;
if (entriesPart || runtimePart || prefetchPart) {
source.add(entriesPart || ",0");
}
if (runtimePart || prefetchPart) {
source.add(runtimePart || ",0");
}
if (prefetchPart) {
source.add(prefetchPart);
}
source.add("])");
}
if (prefetchChunks && prefetchChunks.length) {
source.add(`,${JSON.stringify(prefetchChunks)}`);
}
source.add("])");
return source;
}
);
@ -73,6 +95,10 @@ class JsonpChunkTemplatePlugin {
hash.update(
JSON.stringify(chunk.getChildIdsByOrders(chunkGraph).prefetch) || ""
);
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
hash.update(
JSON.stringify(runtimeModules.map(m => chunkGraph.getModuleId(m)))
);
}
);
}

View File

@ -1,46 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
/** @typedef {import("../HotUpdateChunkTemplate")} HotUpdateChunkTemplate */
class JsonpHotUpdateChunkTemplatePlugin {
/**
* @param {HotUpdateChunkTemplate} hotUpdateChunkTemplate the hot update chunk template
* @returns {void}
*/
apply(hotUpdateChunkTemplate) {
hotUpdateChunkTemplate.hooks.render.tap(
"JsonpHotUpdateChunkTemplatePlugin",
(modulesSource, moduleTemplate, { chunk }) => {
const source = new ConcatSource();
source.add(
`${
hotUpdateChunkTemplate.outputOptions.hotUpdateFunction
}(${JSON.stringify(chunk.id)},`
);
source.add(modulesSource);
source.add(")");
return source;
}
);
hotUpdateChunkTemplate.hooks.hash.tap(
"JsonpHotUpdateChunkTemplatePlugin",
hash => {
hash.update("JsonpHotUpdateChunkTemplatePlugin");
hash.update("3");
hash.update(
`${hotUpdateChunkTemplate.outputOptions.hotUpdateFunction}`
);
hash.update(`${hotUpdateChunkTemplate.outputOptions.library}`);
}
);
}
}
module.exports = JsonpHotUpdateChunkTemplatePlugin;

View File

@ -1,69 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/*globals hotAddUpdateChunk parentHotUpdateCallback document XMLHttpRequest $publicPath$ $hotChunkFilename$ $hotMainFilename$ $crossOriginLoading$ */
module.exports = function() {
// eslint-disable-next-line no-unused-vars
function webpackHotUpdateCallback(chunkId, moreModules) {
hotAddUpdateChunk(chunkId, moreModules);
if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);
} //$semicolon
// eslint-disable-next-line no-unused-vars
function hotDownloadUpdateChunk(chunkId) {
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.charset = "utf-8";
script.src = $publicPath$ + $hotChunkFilename$;
if ($crossOriginLoading$) script.crossOrigin = $crossOriginLoading$;
head.appendChild(script);
}
// eslint-disable-next-line no-unused-vars
function hotDownloadManifest(requestTimeout) {
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 = $publicPath$ + $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

@ -7,12 +7,10 @@
const { SyncWaterfallHook } = require("tapable");
const Compilation = require("../Compilation");
const HotModuleReplacementPlugin = require("../HotModuleReplacementPlugin");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const JsonpChunkLoadingRuntimeModule = require("./JsonpChunkLoadingRuntimeModule");
const JsonpChunkTemplatePlugin = require("./JsonpChunkTemplatePlugin");
const JsonpHotUpdateChunkTemplatePlugin = require("./JsonpHotUpdateChunkTemplatePlugin");
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation")} Compilation */
@ -60,9 +58,6 @@ class JsonpTemplatePlugin {
new JsonpChunkTemplatePlugin(compilation).apply(
compilation.chunkTemplate
);
new JsonpHotUpdateChunkTemplatePlugin().apply(
compilation.hotUpdateChunkTemplate
);
const mainTemplate = compilation.mainTemplate;
const needEntryDeferringCode = chunk => {
for (const chunkGroup of chunk.groupsIterable) {
@ -77,10 +72,6 @@ class JsonpTemplatePlugin {
linkPrefetch
} = JsonpTemplatePlugin.getCompilationHooks(compilation);
const { hotBootstrap } = HotModuleReplacementPlugin.getMainTemplateHooks(
mainTemplate
);
jsonpScript.tap("JsonpTemplatePlugin", (_, chunk, hash) => {
const crossOriginLoading =
mainTemplate.outputOptions.crossOriginLoading;
@ -100,9 +91,7 @@ class JsonpTemplatePlugin {
`script.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`
),
"}",
`script.src = ${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getChunkScriptFilename
}(chunkId);`,
`script.src = url;`,
crossOriginLoading
? Template.asString([
"if (script.src.indexOf(window.location.origin + '/') !== 0) {",
@ -117,20 +106,15 @@ class JsonpTemplatePlugin {
"// avoid mem leaks in IE.",
"script.onerror = script.onload = null;",
"clearTimeout(timeout);",
"var chunk = installedChunks[chunkId];",
"if(chunk !== 0) {",
"var reportError = loadingEnded();",
"if(reportError) {",
Template.indent([
"if(chunk) {",
Template.indent([
"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
"var realSrc = event && event.target && event.target.src;",
"var error = new Error('Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')');",
"error.type = errorType;",
"error.request = realSrc;",
"chunk[1](error);"
]),
"}",
"installedChunks[chunkId] = undefined;"
"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
"var realSrc = event && event.target && event.target.src;",
"var error = new Error('Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')');",
"error.type = errorType;",
"error.request = realSrc;",
"reportError(error);"
]),
"}"
]),
@ -221,11 +205,33 @@ class JsonpTemplatePlugin {
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.startup)
.tap("JsonpTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap("JsonpTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadManifest)
.tap("JsonpTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap("JsonpTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.getChunkScriptFilename);
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkScriptFilename);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap("JsonpTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
set.add(RuntimeGlobals.moduleCache);
set.add(RuntimeGlobals.hmrModuleData);
set.add(RuntimeGlobals.moduleFactories);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadManifest)
.tap("JsonpTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getUpdateManifestFilename);
});
compilation.hooks.additionalTreeRuntimeRequirements.tap(
@ -236,60 +242,6 @@ class JsonpTemplatePlugin {
}
}
);
hotBootstrap.tap("JsonpTemplatePlugin", (source, chunk, hash) => {
const globalObject = mainTemplate.outputOptions.globalObject;
const hotUpdateChunkFilename =
mainTemplate.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename =
mainTemplate.outputOptions.hotUpdateMainFilename;
const crossOriginLoading =
mainTemplate.outputOptions.crossOriginLoading;
const hotUpdateFunction = mainTemplate.outputOptions.hotUpdateFunction;
const currentHotUpdateChunkFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateChunkFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
chunk: {
id: '" + chunkId + "'
}
}
);
const currentHotUpdateMainFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateMainFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`
}
);
const runtimeSource = Template.getFunctionContent(
require("./JsonpMainTemplate.runtime")
)
.replace(/\/\/\$semicolon/g, ";")
.replace(
/\$crossOriginLoading\$/g,
crossOriginLoading ? JSON.stringify(crossOriginLoading) : "null"
)
.replace(/\$$publicPath$\$/g, RuntimeGlobals.publicPath)
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename)
.replace(/\$hash\$/g, JSON.stringify(hash));
return `${source}
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
var parentHotUpdateCallback = ${globalObject}[${JSON.stringify(
hotUpdateFunction
)}];
${globalObject}[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`;
});
mainTemplate.hooks.hash.tap("JsonpTemplatePlugin", hash => {
hash.update("jsonp");
hash.update("6");
});
});
}
}

View File

@ -9,19 +9,29 @@ const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
constructor(chunk, outputOptions) {
constructor(chunk, outputOptions, runtimeRequirements) {
super("importScripts chunk loading", 10);
this.chunk = chunk;
this.outputOptions = outputOptions;
this.runtimeRequirements = runtimeRequirements;
}
/**
* @returns {string} runtime code
*/
generate() {
const { chunk } = this;
const { globalObject, chunkCallbackName } = this.outputOptions;
const { chunk, outputOptions } = this;
const { globalObject, chunkCallbackName } = outputOptions;
const fn = RuntimeGlobals.ensureChunkHandlers;
const withLoading = this.runtimeRequirements.has(
RuntimeGlobals.ensureChunkHandlers
);
const withHmr = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadUpdateHandlers
);
const withHmrManifest = this.runtimeRequirements.has(
RuntimeGlobals.hmrDownloadManifest
);
return Template.asString([
"// object to store loaded chunks",
'// "1" means "already loaded"',
@ -31,41 +41,145 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule {
),
"};",
"",
"// importScripts chunk loading",
`${fn}.push(function(chunkId, promises) {`,
Template.indent([
'// "1" is the signal for "already loaded"',
"if(!installedChunks[chunkId]) {",
Template.indent([
"promises.push(Promise.resolve().then(function() {",
Template.indent([
`importScripts(${RuntimeGlobals.getChunkScriptFilename}(chunkId));`
]),
"}));"
]),
"}"
]),
"});",
withLoading
? Template.asString([
"// importScripts chunk loading",
`${fn}.push(function(chunkId, promises) {`,
Template.indent([
'// "1" is the signal for "already loaded"',
"if(!installedChunks[chunkId]) {",
Template.indent([
`${globalObject}[${JSON.stringify(
chunkCallbackName
)}] = function webpackChunkCallback(chunkIds, moreModules, runtime) {`,
Template.indent([
"for(var moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent(
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
),
"}"
]),
"}",
"if(runtime) for(var i = 0; i < runtime.length; i++) moreModules[runtime[i]](0,0,__webpack_require__);",
"while(chunkIds.length)",
Template.indent("installedChunks[chunkIds.pop()] = 1;")
]),
"};",
`importScripts(${
RuntimeGlobals.getChunkScriptFilename
}(chunkId));`,
"",
withHmr
? "if(currentUpdateChunks && currentUpdateChunks[chunkId]) loadUpdateChunk(chunkId);"
: "// no HMR"
]),
"}"
]),
"});"
])
: "// no chunk loading",
"",
`${globalObject}[${JSON.stringify(
chunkCallbackName
)}] = function webpackChunkCallback(chunkIds, moreModules) {`,
Template.indent([
"for(var moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent(
withHmr
? Template.asString([
"var currentUpdateChunks;",
"var currentUpdate;",
"var currentUpdateRuntime;",
"function loadUpdateChunk(chunkId, updatedModulesList) {",
Template.indent([
"var success = false;",
`${outputOptions.globalObject}[${JSON.stringify(
outputOptions.hotUpdateFunction
)}] = function(moreModules, runtime) {`,
Template.indent([
"for(moduleId in moreModules) {",
Template.indent([
"if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {",
Template.indent([
"currentUpdate[moduleId] = moreModules[moduleId];",
"if(updatedModulesList) updatedModulesList.push(moduleId);"
]),
"}"
]),
"}",
"if(runtime) for(var i = 0; i < runtime.length; i++) currentUpdateRuntime.push(runtime[i]);",
"success = true;"
]),
"};",
"// start update chunk loading",
`importScripts(${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getChunkUpdateScriptFilename
}(chunkId));`,
'if(!success) throw new Error("Loading update chunk failed for unknown reason");'
]),
"}",
"",
`${
RuntimeGlobals.moduleFactories
}[moduleId] = moreModules[moduleId];`
),
"}"
]),
"}",
"while(chunkIds.length)",
Template.indent("installedChunks[chunkIds.pop()] = 1;")
]),
"};"
RuntimeGlobals.hmrDownloadUpdateHandlers
}.jsonp = function(chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList) {`,
Template.indent([
"applyHandlers.push(function(options) {",
Template.indent([
"currentUpdateChunks = undefined;",
Template.getFunctionContent(
require("../hmr/JavascriptHotModuleReplacement.runtime.js")
)
.replace(/\$options\$/g, "options")
.replace(/\$updateModuleFactories\$/g, "currentUpdate")
.replace(/\$updateRuntimeModules\$/g, "currentUpdateRuntime")
.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
.replace(
/\$moduleFactories\$/g,
RuntimeGlobals.moduleFactories
)
.replace(
/\/\/ \$dispose\$/g,
Template.asString([
"removedChunks.forEach(function(chunkId) { delete installedChunks[chunkId]; });"
])
)
]),
"});",
"currentUpdateChunks = {};",
"currentUpdate = removedModules.reduce(function(obj, key) { obj[key] = false; return obj; }, {});",
"currentUpdateRuntime = [];",
"chunkIds.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] !== undefined) {",
Template.indent([
"promises.push(loadUpdateChunk(chunkId, updatedModulesList));"
]),
"}",
"currentUpdateChunks[chunkId] = true;"
]),
"});"
]),
"}"
])
: "// no HMR",
"",
withHmrManifest
? Template.asString([
`${RuntimeGlobals.hmrDownloadManifest} = function() {`,
Template.indent([
'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");',
`return fetch(${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getUpdateManifestFilename
}()).then(function(response) {`,
Template.indent([
"if(response.status === 404) return; // no update available",
'if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText);',
"return response.json();"
]),
"});"
]),
"};"
])
: "// no HMR manifest"
]);
}
}

View File

@ -6,10 +6,19 @@
"use strict";
const { ConcatSource } = require("webpack-sources");
const HotUpdateChunk = require("../HotUpdateChunk");
/** @typedef {import("../ChunkTemplate")} ChunkTemplate */
/** @typedef {import("../Compilation")} Compilation */
class WebWorkerChunkTemplatePlugin {
/**
* @param {Compilation} compilation the compilation
*/
constructor(compilation) {
this.compilation = compilation;
}
/**
* @param {ChunkTemplate} chunkTemplate the chunk template
* @returns {void}
@ -17,27 +26,55 @@ class WebWorkerChunkTemplatePlugin {
apply(chunkTemplate) {
chunkTemplate.hooks.render.tap(
"WebWorkerChunkTemplatePlugin",
(modules, moduleTemplate, { chunk }) => {
const chunkCallbackName = chunkTemplate.outputOptions.chunkCallbackName;
(modules, moduleTemplate, { chunk, chunkGraph }) => {
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
const globalObject = chunkTemplate.outputOptions.globalObject;
const source = new ConcatSource();
source.add(
`${globalObject}[${JSON.stringify(
chunkCallbackName
)}](${JSON.stringify(chunk.ids)},`
);
source.add(modules);
source.add(")");
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
const runtimePart =
runtimeModules.length > 0 &&
`,${JSON.stringify(
runtimeModules.map(m => chunkGraph.getModuleId(m))
)}`;
if (hotUpdateChunk) {
const jsonpFunction = chunkTemplate.outputOptions.hotUpdateFunction;
source.add(`${globalObject}[${JSON.stringify(jsonpFunction)}](`);
source.add(modules);
if (runtimePart) {
source.add(runtimePart);
}
source.add(")");
} else {
const chunkCallbackName =
chunkTemplate.outputOptions.chunkCallbackName;
source.add(`${globalObject}[${JSON.stringify(chunkCallbackName)}](`);
source.add(`${JSON.stringify(chunk.ids)},`);
source.add(modules);
if (runtimePart) {
source.add(runtimePart);
}
source.add(")");
}
return source;
}
);
chunkTemplate.hooks.hash.tap("WebWorkerChunkTemplatePlugin", hash => {
hash.update("webworker");
hash.update("3");
hash.update("4");
hash.update(`${chunkTemplate.outputOptions.chunkCallbackName}`);
hash.update(`${chunkTemplate.outputOptions.hotUpdateFunction}`);
hash.update(`${chunkTemplate.outputOptions.globalObject}`);
});
chunkTemplate.hooks.hashForChunk.tap(
"WebWorkerChunkTemplatePlugin",
(hash, chunk) => {
const chunkGraph = this.compilation.chunkGraph;
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
hash.update(
JSON.stringify(runtimeModules.map(m => chunkGraph.getModuleId(m)))
);
}
);
}
}
module.exports = WebWorkerChunkTemplatePlugin;

View File

@ -1,48 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
/** @typedef {import("../HotUpdateChunkTemplate")} HotUpdateChunkTemplate */
class WebWorkerHotUpdateChunkTemplatePlugin {
/**
* @param {HotUpdateChunkTemplate} hotUpdateChunkTemplate the hot update chunk template
* @returns {void}
*/
apply(hotUpdateChunkTemplate) {
hotUpdateChunkTemplate.hooks.render.tap(
"WebWorkerHotUpdateChunkTemplatePlugin",
(modulesSource, moduleTemplate, { chunk }) => {
const hotUpdateFunction =
hotUpdateChunkTemplate.outputOptions.hotUpdateFunction;
const globalObject = hotUpdateChunkTemplate.outputOptions.globalObject;
const source = new ConcatSource();
source.add(
`${globalObject}[${JSON.stringify(
hotUpdateFunction
)}](${JSON.stringify(chunk.id)},`
);
source.add(modulesSource);
source.add(")");
return source;
}
);
hotUpdateChunkTemplate.hooks.hash.tap(
"WebWorkerHotUpdateChunkTemplatePlugin",
hash => {
hash.update("WebWorkerHotUpdateChunkTemplatePlugin");
hash.update("3");
hash.update(
hotUpdateChunkTemplate.outputOptions.hotUpdateFunction + ""
);
hash.update(hotUpdateChunkTemplate.outputOptions.globalObject + "");
}
);
}
}
module.exports = WebWorkerHotUpdateChunkTemplatePlugin;

View File

@ -1,69 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/*globals installedChunks hotAddUpdateChunk parentHotUpdateCallback importScripts XMLHttpRequest $publicPath$ $hotChunkFilename$ $hotMainFilename$ */
module.exports = function() {
// eslint-disable-next-line no-unused-vars
function webpackHotUpdateCallback(chunkId, moreModules) {
hotAddUpdateChunk(chunkId, moreModules);
if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);
} //$semicolon
// eslint-disable-next-line no-unused-vars
function hotDownloadUpdateChunk(chunkId) {
importScripts($publicPath$ + $hotChunkFilename$);
}
// eslint-disable-next-line no-unused-vars
function hotDownloadManifest(requestTimeout) {
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 = $publicPath$ + $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);
}
};
});
}
//eslint-disable-next-line no-unused-vars
function hotDisposeChunk(chunkId) {
delete installedChunks[chunkId];
}
};

View File

@ -5,11 +5,9 @@
"use strict";
const HotModuleReplacementPlugin = require("../HotModuleReplacementPlugin");
const RuntimeGlobals = require("../RuntimeGlobals");
const Template = require("../Template");
const ImportScriptsChunkLoadingRuntimeModule = require("./ImportScriptsChunkLoadingRuntimeModule");
const WebWorkerChunkTemplatePlugin = require("./WebWorkerChunkTemplatePlugin");
const WebWorkerHotUpdateChunkTemplatePlugin = require("./WebWorkerHotUpdateChunkTemplatePlugin");
/** @typedef {import("../Compiler")} Compiler */
@ -22,71 +20,58 @@ class WebWorkerTemplatePlugin {
compiler.hooks.thisCompilation.tap(
"WebWorkerTemplatePlugin",
compilation => {
new WebWorkerChunkTemplatePlugin().apply(compilation.chunkTemplate);
new WebWorkerHotUpdateChunkTemplatePlugin().apply(
compilation.hotUpdateChunkTemplate
new WebWorkerChunkTemplatePlugin(compilation).apply(
compilation.chunkTemplate
);
compilation.hooks.runtimeRequirementInTree.for(
RuntimeGlobals.ensureChunkHandlers
);
const onceForChunkSet = new WeakSet();
const handler = (chunk, set) => {
if (onceForChunkSet.has(chunk)) return;
onceForChunkSet.add(chunk);
set.add(RuntimeGlobals.moduleFactories);
compilation.addRuntimeModule(
chunk,
new ImportScriptsChunkLoadingRuntimeModule(
chunk,
compilation.outputOptions,
set
)
);
};
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap("JsonpTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.startup)
.tap("JsonpTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap("JsonpTemplatePlugin", handler);
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadManifest)
.tap("JsonpTemplatePlugin", handler);
const mainTemplate = compilation.mainTemplate;
const {
hotBootstrap
} = HotModuleReplacementPlugin.getMainTemplateHooks(mainTemplate);
hotBootstrap.tap(
"WebWorkerMainTemplatePlugin",
(source, chunk, hash) => {
const hotUpdateChunkFilename =
mainTemplate.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename =
mainTemplate.outputOptions.hotUpdateMainFilename;
const hotUpdateFunction =
mainTemplate.outputOptions.hotUpdateFunction;
const globalObject = mainTemplate.outputOptions.globalObject;
const currentHotUpdateChunkFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateChunkFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
chunk: {
id: '" + chunkId + "'
}
}
);
const currentHotUpdateMainFilename = mainTemplate.getAssetPath(
JSON.stringify(hotUpdateMainFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`
}
);
return (
source +
"\n" +
`var parentHotUpdateCallback = ${globalObject}[${JSON.stringify(
hotUpdateFunction
)}];\n` +
`${globalObject}[${JSON.stringify(hotUpdateFunction)}] = ` +
Template.getFunctionContent(
require("./WebWorkerMainTemplate.runtime")
)
.replace(/\/\/\$semicolon/g, ";")
.replace(/\$publicPath\$/g, RuntimeGlobals.publicPath)
.replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
.replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename)
.replace(/\$hash\$/g, JSON.stringify(hash))
);
}
);
mainTemplate.hooks.hash.tap("WebWorkerMainTemplatePlugin", hash => {
hash.update("webworker");
hash.update("4");
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap("JsonpTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkScriptFilename);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadUpdateHandlers)
.tap("JsonpTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getChunkUpdateScriptFilename);
set.add(RuntimeGlobals.moduleCache);
set.add(RuntimeGlobals.hmrModuleData);
set.add(RuntimeGlobals.moduleFactories);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.hmrDownloadManifest)
.tap("JsonpTemplatePlugin", (chunk, set) => {
set.add(RuntimeGlobals.publicPath);
set.add(RuntimeGlobals.getUpdateManifestFilename);
});
}
);
}

View File

@ -1868,6 +1868,10 @@
"description": "add information about the reasons why modules are included",
"type": "boolean"
},
"runtime": {
"description": "add information about runtime modules",
"type": "boolean"
},
"source": {
"description": "add the source code of modules",
"type": "boolean"

View File

@ -0,0 +1,248 @@
"use strict";
/* globals expect fit */
const path = require("path");
const fs = require("fs");
const vm = require("vm");
const checkArrayExpectation = require("./checkArrayExpectation");
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
const webpack = require("../lib/webpack");
const casesPath = path.join(__dirname, "hotCases");
let categories = fs
.readdirSync(casesPath)
.filter(dir => fs.statSync(path.join(casesPath, dir)).isDirectory());
categories = categories.map(cat => {
return {
name: cat,
tests: fs
.readdirSync(path.join(casesPath, cat))
.filter(folder => folder.indexOf("_") < 0)
};
});
const describeCases = config => {
describe(config.name, () => {
categories.forEach(category => {
describe(category.name, () => {
category.tests.forEach(testName => {
describe(testName, () => {
it(
testName + " should compile",
done => {
const testDirectory = path.join(
casesPath,
category.name,
testName
);
const outputDirectory = path.join(
__dirname,
"js",
`hot-cases-${config.name}`,
category.name,
testName
);
const recordsPath = path.join(outputDirectory, "records.json");
if (fs.existsSync(recordsPath)) fs.unlinkSync(recordsPath);
const fakeUpdateLoaderOptions = {
updateIndex: 0
};
const configPath = path.join(testDirectory, "webpack.config.js");
let options = {};
if (fs.existsSync(configPath)) options = require(configPath);
if (!options.mode) options.mode = "development";
if (!options.devtool) options.devtool = false;
if (!options.context) options.context = testDirectory;
if (!options.entry) options.entry = "./index.js";
if (!options.output) options.output = {};
if (!options.output.path) options.output.path = outputDirectory;
if (!options.output.filename)
options.output.filename = "bundle.js";
if (!options.output.chunkFilename)
options.output.chunkFilename = "[name].chunk.[hash].js";
if (options.output.pathinfo === undefined)
options.output.pathinfo = true;
if (!options.optimization) options.optimization = {};
if (!options.optimization.moduleIds)
options.optimization.moduleIds = "named";
if (!options.module) options.module = {};
if (!options.module.rules) options.module.rules = [];
options.module.rules.push({
loader: path.join(
__dirname,
"hotCases",
"fake-update-loader.js"
),
enforce: "pre"
});
if (!options.target) options.target = config.target;
if (!options.plugins) options.plugins = [];
options.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin(fakeUpdateLoaderOptions)
);
if (!options.recordsPath) options.recordsPath = recordsPath;
const compiler = webpack(options);
compiler.run((err, stats) => {
if (err) return done(err);
const jsonStats = stats.toJson({
errorDetails: true
});
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"error",
"Error",
done
)
) {
return;
}
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"warning",
"Warning",
done
)
) {
return;
}
const window = {
fetch: (url) => {
return Promise.resolve({
ok: true,
json() {
return Promise.resolve(require(path.resolve(outputDirectory, url)));
}
})
},
importScripts: url => {
_require("./" + url);
},
document: {
createElement(type) {
return {
_type: type,
_attrs: {},
setAttribute(name, value) {
this._attrs[name] = value;
}
};
},
head: {
appendChild(element) {
if(element._type === "script") {
// run it
Promise.resolve().then(() => {
_require("./" + element.src);
});
}
}
},
getElementsByTagName(name) {
if(name === "head") return [this.head];
throw new Error("Not supported");
}
}
};
function _next(callback) {
fakeUpdateLoaderOptions.updateIndex++;
compiler.run((err, stats) => {
if (err) return callback(err);
const jsonStats = stats.toJson({
errorDetails: true
});
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"error",
"errors" + fakeUpdateLoaderOptions.updateIndex,
"Error",
callback
)
) {
return;
}
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"warning",
"warnings" + fakeUpdateLoaderOptions.updateIndex,
"Warning",
callback
)
) {
return;
}
callback(null, jsonStats);
});
}
function _require(module) {
if (module.substr(0, 2) === "./") {
const p = path.join(outputDirectory, module);
if(module.endsWith(".json")) {
return JSON.parse(fs.readFileSync(p, "utf-8"));
} else {
const fn = vm.runInThisContext(
"(function(require, module, exports, __dirname, __filename, it, expect, self, window, fetch, document, importScripts, NEXT, STATS) {" +
"global.expect = expect;" +
'function nsObj(m) { Object.defineProperty(m, Symbol.toStringTag, { value: "Module" }); return m; }' +
fs.readFileSync(p, "utf-8") +
"\n})",
p
);
const m = {
exports: {}
};
fn.call(
m.exports,
_require,
m,
m.exports,
outputDirectory,
p,
_it,
expect,
window,
window,
window.fetch,
window.document,
window.importScripts,
_next,
jsonStats
);
return m.exports;
}
} else return require(module);
}
_require("./bundle.js");
if (getNumberOfTests() < 1)
return done(new Error("No tests exported by test case"));
done();
});
},
10000
);
const { it: _it, getNumberOfTests } = createLazyTestEnv(
jasmine.getEnv(),
10000
);
});
});
});
});
});
};
module.exports.describeCases = describeCases;

View File

@ -1,193 +0,0 @@
"use strict";
/* globals expect fit */
const path = require("path");
const fs = require("fs");
const vm = require("vm");
const checkArrayExpectation = require("./checkArrayExpectation");
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
const webpack = require("../lib/webpack");
describe("HotTestCases", () => {
const casesPath = path.join(__dirname, "hotCases");
let categories = fs
.readdirSync(casesPath)
.filter(dir => fs.statSync(path.join(casesPath, dir)).isDirectory());
categories = categories.map(cat => {
return {
name: cat,
tests: fs
.readdirSync(path.join(casesPath, cat))
.filter(folder => folder.indexOf("_") < 0)
};
});
categories.forEach(category => {
describe(category.name, () => {
category.tests.forEach(testName => {
describe(testName, () => {
it(
testName + " should compile",
done => {
const testDirectory = path.join(
casesPath,
category.name,
testName
);
const outputDirectory = path.join(
__dirname,
"js",
"hot-cases",
category.name,
testName
);
const recordsPath = path.join(outputDirectory, "records.json");
if (fs.existsSync(recordsPath)) fs.unlinkSync(recordsPath);
const fakeUpdateLoaderOptions = {
updateIndex: 0
};
const configPath = path.join(testDirectory, "webpack.config.js");
let options = {};
if (fs.existsSync(configPath)) options = require(configPath);
if (!options.mode) options.mode = "development";
if (!options.devtool) options.devtool = false;
if (!options.context) options.context = testDirectory;
if (!options.entry) options.entry = "./index.js";
if (!options.output) options.output = {};
if (!options.output.path) options.output.path = outputDirectory;
if (!options.output.filename)
options.output.filename = "bundle.js";
if (options.output.pathinfo === undefined)
options.output.pathinfo = true;
if (!options.optimization) options.optimization = {};
if (!options.optimization.moduleIds)
options.optimization.moduleIds = "named";
if (!options.module) options.module = {};
if (!options.module.rules) options.module.rules = [];
options.module.rules.push({
loader: path.join(
__dirname,
"hotCases",
"fake-update-loader.js"
),
enforce: "pre"
});
if (!options.target) options.target = "async-node";
if (!options.plugins) options.plugins = [];
options.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin(fakeUpdateLoaderOptions)
);
if (!options.recordsPath) options.recordsPath = recordsPath;
const compiler = webpack(options);
compiler.run((err, stats) => {
if (err) return done(err);
const jsonStats = stats.toJson({
errorDetails: true
});
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"error",
"Error",
done
)
) {
return;
}
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"warning",
"Warning",
done
)
) {
return;
}
function _next(callback) {
fakeUpdateLoaderOptions.updateIndex++;
compiler.run((err, stats) => {
if (err) return callback(err);
const jsonStats = stats.toJson({
errorDetails: true
});
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"error",
"errors" + fakeUpdateLoaderOptions.updateIndex,
"Error",
callback
)
) {
return;
}
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"warning",
"warnings" + fakeUpdateLoaderOptions.updateIndex,
"Warning",
callback
)
) {
return;
}
callback(null, jsonStats);
});
}
function _require(module) {
if (module.substr(0, 2) === "./") {
const p = path.join(outputDirectory, module);
const fn = vm.runInThisContext(
"(function(require, module, exports, __dirname, __filename, it, expect, NEXT, STATS) {" +
"global.expect = expect;" +
'function nsObj(m) { Object.defineProperty(m, Symbol.toStringTag, { value: "Module" }); return m; }' +
fs.readFileSync(p, "utf-8") +
"\n})",
p
);
const m = {
exports: {}
};
fn.call(
m.exports,
_require,
m,
m.exports,
outputDirectory,
p,
_it,
expect,
_next,
jsonStats
);
return m.exports;
} else return require(module);
}
_require("./bundle.js");
if (getNumberOfTests() < 1)
return done(new Error("No tests exported by test case"));
done();
});
},
10000
);
const { it: _it, getNumberOfTests } = createLazyTestEnv(
jasmine.getEnv(),
10000
);
});
});
});
});
});

View File

@ -0,0 +1,8 @@
const { describeCases } = require("./HotTestCases.template");
describe("HotTestCases", () => {
describeCases({
name: "async-node",
target: "async-node"
});
});

View File

@ -0,0 +1,8 @@
const { describeCases } = require("./HotTestCases.template");
describe("HotTestCases", () => {
describeCases({
name: "node",
target: "node"
});
});

View File

@ -0,0 +1,8 @@
const { describeCases } = require("./HotTestCases.template");
describe("HotTestCases", () => {
describeCases({
name: "web",
target: "web"
});
});

View File

@ -0,0 +1,8 @@
const { describeCases } = require("./HotTestCases.template");
describe("HotTestCases", () => {
describeCases({
name: "webworker",
target: "webworker"
});
});

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
require("./common");
module.exports = "vendor";
it("should have the correct main flag for multi vendor module", function() {
expect(module.hot._main).toBe(true);
});

View File

@ -1,7 +1,6 @@
var HotModuleReplacementPlugin = require("../../../../lib/HotModuleReplacementPlugin");
module.exports = {
entry: {
vendor: ["./vendor"],
first: ["./shared", "./first"],
second: ["./shared", "./second"]
},
@ -11,8 +10,14 @@ module.exports = {
},
optimization: {
splitChunks: {
minSize: 1,
name: "vendor"
cacheGroups: {
vendor: {
chunks: "all",
name: "vendor",
minChunks: 2,
enforce: true
}
}
}
},
plugins: [new HotModuleReplacementPlugin()]

View File

@ -1,7 +1,6 @@
var HotModuleReplacementPlugin = require("../../../../lib/HotModuleReplacementPlugin");
module.exports = {
entry: {
vendor: ["./vendor"],
main: "./index"
},
target: "web",
@ -10,8 +9,14 @@ module.exports = {
},
optimization: {
splitChunks: {
minSize: 1,
name: "vendor"
cacheGroups: {
vendor: {
chunks: "all",
name: "vendor",
test: /vendor/,
enforce: true
}
}
}
},
plugins: [new HotModuleReplacementPlugin()]

View File

@ -17,6 +17,6 @@ it("should give modules the correct ids", function() {
"./index.js",
"dll-reference ../0-create-dll/dll.js",
"dll/index.js",
"runtime: make namespace object",
"webpack/runtime/make namespace object",
]);
});

View File

@ -44,8 +44,8 @@ it("should give modules the correct ids", function() {
"dll/e2.js",
"dll/f.jsx",
"dll/g.abc.js",
"runtime: compat get default export",
"runtime: define property getter",
"runtime: make namespace object",
"webpack/runtime/compat get default export",
"webpack/runtime/define property getter",
"webpack/runtime/make namespace object",
]);
});

View File

@ -44,8 +44,8 @@ it("should give modules the correct ids", function() {
"../0-create-dll/g.abc.js",
"./index.js",
"dll-reference ../0-create-dll/dll.js",
"runtime: compat get default export",
"runtime: define property getter",
"runtime: make namespace object",
"webpack/runtime/compat get default export",
"webpack/runtime/define property getter",
"webpack/runtime/make namespace object",
]);
});

View File

@ -1 +1,8 @@
export default Date.now();
export default "version a1";
---
export default "version a1";
---
export default "version a2";
---
export default "version a2";
---

View File

@ -0,0 +1,7 @@
export default "version b1";
---
export default "version b1";
---
export default "version b2";
---
export default "version b2";

View File

@ -1,14 +1,23 @@
it("should dispose a chunk which is removed from bundle", (done) => {
var m1 = require("./module");
m1.default.then((x1) => {
expect(x1.default).toEqual("version a1");
NEXT(require("../../update")(done, true, () => {
var m2 = require("./module");
m2.default.then((x2) => {
expect(x2.default).toEqual("version b1");
NEXT(require("../../update")(done, true, () => {
var m3 = require("./module");
m3.default.then((x3) => {
expect(x1).not.toEqual(x2);
done();
expect(x3.default).toEqual("version b2");
NEXT(require("../../update")(done, true, () => {
var m4 = require("./module");
m4.default.then((x4) => {
expect(x4.default).toEqual("version a2");
expect(x4).not.toEqual(x1);
done();
}).catch(done);
}));
}).catch(done);
}));
}).catch(done);

View File

@ -2,4 +2,6 @@ export default import("./a");
---
export default import("./b");
---
export default import("./b");
---
export default import("./a");

View File

@ -0,0 +1,5 @@
import value from "./inner";
module.hot.accept("./inner");
export { value as default };

View File

@ -0,0 +1,3 @@
export default 1;
---
export default 2;

View File

@ -0,0 +1,28 @@
import value from "./file";
module.hot.accept("./file");
const asyncNext = () => {
return new Promise((resolve, reject) => {
NEXT((err, stats) => {
if(err) return reject(err);
resolve(stats);
});
});
}
it("should download the missing update chunk on import", () => {
expect(value).toBe(1);
return asyncNext().then(() => {
return module.hot.check().then(() => {
return import("./chunk").then(chunk => {
expect(value).toBe(1);
expect(chunk.default).toBe(10);
return module.hot.apply().then(() => {
expect(value).toBe(2);
expect(chunk.default).toBe(20);
});
});
});
});
});

View File

@ -0,0 +1,3 @@
export default 10;
---
export default 20;