Skip to content

Commit

Permalink
fix memory leak with HMR
Browse files Browse the repository at this point in the history
fixes #6929
  • Loading branch information
sokra committed Jul 5, 2018
1 parent bdd4442 commit 6172d3c
Showing 1 changed file with 114 additions and 116 deletions.
230 changes: 114 additions & 116 deletions lib/HotModuleReplacementPlugin.js
Expand Up @@ -35,6 +35,118 @@ module.exports = class HotModuleReplacementPlugin {
return callback();
}
);

const addParserPlugins = (parser, parserOptions) => {
parser.hooks.expression
.for("__webpack_hash__")
.tap(
"HotModuleReplacementPlugin",
ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
"__webpack_require__.h()"
)
);
parser.hooks.evaluateTypeof
.for("__webpack_hash__")
.tap(
"HotModuleReplacementPlugin",
ParserHelpers.evaluateToString("string")
);
parser.hooks.evaluateIdentifier.for("module.hot").tap(
{
name: "HotModuleReplacementPlugin",
before: "NodeStuffPlugin"
},
expr => {
return ParserHelpers.evaluateToIdentifier(
"module.hot",
!!parser.state.compilation.hotUpdateChunkTemplate
)(expr);
}
);
// TODO webpack 5: refactor this, no custom hooks
if (!parser.hooks.hotAcceptCallback) {
parser.hooks.hotAcceptCallback = new SyncBailHook([
"expression",
"requests"
]);
}
if (!parser.hooks.hotAcceptWithoutCallback) {
parser.hooks.hotAcceptWithoutCallback = new SyncBailHook([
"expression",
"requests"
]);
}
parser.hooks.call
.for("module.hot.accept")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
if (expr.arguments.length >= 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
let params = [];
let requests = [];
if (arg.isString()) {
params = [arg];
} else if (arg.isArray()) {
params = arg.items.filter(param => param.isString());
}
if (params.length > 0) {
params.forEach((param, idx) => {
const request = param.string;
const dep = new ModuleHotAcceptDependency(request, param.range);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
parser.state.module.addDependency(dep);
requests.push(request);
});
if (expr.arguments.length > 1) {
parser.hooks.hotAcceptCallback.call(
expr.arguments[1],
requests
);
parser.walkExpression(expr.arguments[1]); // other args are ignored
return true;
} else {
parser.hooks.hotAcceptWithoutCallback.call(expr, requests);
return true;
}
}
}
});
parser.hooks.call
.for("module.hot.decline")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
if (expr.arguments.length === 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
let params = [];
if (arg.isString()) {
params = [arg];
} else if (arg.isArray()) {
params = arg.items.filter(param => param.isString());
}
params.forEach((param, idx) => {
const dep = new ModuleHotDeclineDependency(
param.string,
param.range
);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
parser.state.module.addDependency(dep);
});
}
});
parser.hooks.expression
.for("module.hot")
.tap("HotModuleReplacementPlugin", ParserHelpers.skipTraversal);
};

compiler.hooks.compilation.tap(
"HotModuleReplacementPlugin",
(compilation, { normalModuleFactory }) => {
Expand Down Expand Up @@ -275,127 +387,13 @@ module.exports = class HotModuleReplacementPlugin {
}
);

const handler = (parser, parserOptions) => {
parser.hooks.expression
.for("__webpack_hash__")
.tap(
"HotModuleReplacementPlugin",
ParserHelpers.toConstantDependencyWithWebpackRequire(
parser,
"__webpack_require__.h()"
)
);
parser.hooks.evaluateTypeof
.for("__webpack_hash__")
.tap(
"HotModuleReplacementPlugin",
ParserHelpers.evaluateToString("string")
);
parser.hooks.evaluateIdentifier.for("module.hot").tap(
{
name: "HotModuleReplacementPlugin",
before: "NodeStuffPlugin"
},
expr => {
return ParserHelpers.evaluateToIdentifier(
"module.hot",
!!parser.state.compilation.hotUpdateChunkTemplate
)(expr);
}
);
// TODO webpack 5: refactor this, no custom hooks
if (!parser.hooks.hotAcceptCallback) {
parser.hooks.hotAcceptCallback = new SyncBailHook([
"expression",
"requests"
]);
}
if (!parser.hooks.hotAcceptWithoutCallback) {
parser.hooks.hotAcceptWithoutCallback = new SyncBailHook([
"expression",
"requests"
]);
}
parser.hooks.call
.for("module.hot.accept")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
if (expr.arguments.length >= 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
let params = [];
let requests = [];
if (arg.isString()) {
params = [arg];
} else if (arg.isArray()) {
params = arg.items.filter(param => param.isString());
}
if (params.length > 0) {
params.forEach((param, idx) => {
const request = param.string;
const dep = new ModuleHotAcceptDependency(
request,
param.range
);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
parser.state.module.addDependency(dep);
requests.push(request);
});
if (expr.arguments.length > 1) {
parser.hooks.hotAcceptCallback.call(
expr.arguments[1],
requests
);
parser.walkExpression(expr.arguments[1]); // other args are ignored
return true;
} else {
parser.hooks.hotAcceptWithoutCallback.call(expr, requests);
return true;
}
}
}
});
parser.hooks.call
.for("module.hot.decline")
.tap("HotModuleReplacementPlugin", expr => {
if (!parser.state.compilation.hotUpdateChunkTemplate) {
return false;
}
if (expr.arguments.length === 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
let params = [];
if (arg.isString()) {
params = [arg];
} else if (arg.isArray()) {
params = arg.items.filter(param => param.isString());
}
params.forEach((param, idx) => {
const dep = new ModuleHotDeclineDependency(
param.string,
param.range
);
dep.optional = true;
dep.loc = Object.create(expr.loc);
dep.loc.index = idx;
parser.state.module.addDependency(dep);
});
}
});
parser.hooks.expression
.for("module.hot")
.tap("HotModuleReplacementPlugin", ParserHelpers.skipTraversal);
};

// TODO add HMR support for javascript/esm
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("HotModuleReplacementPlugin", handler);
.tap("HotModuleReplacementPlugin", addParserPlugins);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("HotModuleReplacementPlugin", handler);
.tap("HotModuleReplacementPlugin", addParserPlugins);

compilation.hooks.normalModuleLoader.tap(
"HotModuleReplacementPlugin",
Expand Down

0 comments on commit 6172d3c

Please sign in to comment.