mirror of https://github.com/webpack/webpack.git
hmr support update
- import.meta.hot to import.meta.webpackHot - create separate tests for import.meta.webpackHot - remove HMRApiDependency in favor of ConstDependency
This commit is contained in:
parent
1beb4e5707
commit
4cd526f970
|
@ -12,7 +12,7 @@ const Compilation = require("./Compilation");
|
|||
const HotUpdateChunk = require("./HotUpdateChunk");
|
||||
const NormalModule = require("./NormalModule");
|
||||
const RuntimeGlobals = require("./RuntimeGlobals");
|
||||
const HMRApiDependency = require("./dependencies/HMRApiDependency");
|
||||
const ConstDependency = require("./dependencies/ConstDependency");
|
||||
const ImportMetaHotAcceptDependency = require("./dependencies/ImportMetaHotAcceptDependency");
|
||||
const ImportMetaHotDeclineDependency = require("./dependencies/ImportMetaHotDeclineDependency");
|
||||
const ModuleHotAcceptDependency = require("./dependencies/ModuleHotAcceptDependency");
|
||||
|
@ -83,17 +83,23 @@ class HotModuleReplacementPlugin {
|
|||
return callback();
|
||||
}
|
||||
);
|
||||
const runtimeRequirements = [RuntimeGlobals.module];
|
||||
|
||||
const createAcceptHandler = (parser, paramDependency) => {
|
||||
const createAcceptHandler = (parser, ParamDependency) => {
|
||||
const {
|
||||
hotAcceptCallback,
|
||||
hotAcceptWithoutCallback
|
||||
} = HotModuleReplacementPlugin.getParserHooks(parser);
|
||||
|
||||
return expr => {
|
||||
const dep = new HMRApiDependency(expr.callee.range, "accept");
|
||||
const module = parser.state.module;
|
||||
const dep = new ConstDependency(
|
||||
`${module.moduleArgument}.hot.accept`,
|
||||
expr.callee.range,
|
||||
runtimeRequirements
|
||||
);
|
||||
dep.loc = expr.loc;
|
||||
parser.state.module.addDependency(dep);
|
||||
module.addPresentationalDependency(dep);
|
||||
if (expr.arguments.length >= 1) {
|
||||
const arg = parser.evaluateExpression(expr.arguments[0]);
|
||||
let params = [];
|
||||
|
@ -106,11 +112,11 @@ class HotModuleReplacementPlugin {
|
|||
if (params.length > 0) {
|
||||
params.forEach((param, idx) => {
|
||||
const request = param.string;
|
||||
const dep = new paramDependency(request, param.range);
|
||||
const dep = new ParamDependency(request, param.range);
|
||||
dep.optional = true;
|
||||
dep.loc = Object.create(expr.loc);
|
||||
dep.loc.index = idx;
|
||||
parser.state.module.addDependency(dep);
|
||||
module.addDependency(dep);
|
||||
requests.push(request);
|
||||
});
|
||||
if (expr.arguments.length > 1) {
|
||||
|
@ -128,10 +134,15 @@ class HotModuleReplacementPlugin {
|
|||
};
|
||||
};
|
||||
|
||||
const createDeclineHandler = (parser, paramDependency) => expr => {
|
||||
const dep = new HMRApiDependency(expr.callee.range, "decline");
|
||||
const createDeclineHandler = (parser, ParamDependency) => expr => {
|
||||
const module = parser.state.module;
|
||||
const dep = new ConstDependency(
|
||||
`${module.moduleArgument}.hot.decline`,
|
||||
expr.callee.range,
|
||||
runtimeRequirements
|
||||
);
|
||||
dep.loc = expr.loc;
|
||||
parser.state.module.addDependency(dep);
|
||||
module.addPresentationalDependency(dep);
|
||||
if (expr.arguments.length === 1) {
|
||||
const arg = parser.evaluateExpression(expr.arguments[0]);
|
||||
let params = [];
|
||||
|
@ -141,20 +152,25 @@ class HotModuleReplacementPlugin {
|
|||
params = arg.items.filter(param => param.isString());
|
||||
}
|
||||
params.forEach((param, idx) => {
|
||||
const dep = new paramDependency(param.string, param.range);
|
||||
const dep = new ParamDependency(param.string, param.range);
|
||||
dep.optional = true;
|
||||
dep.loc = Object.create(expr.loc);
|
||||
dep.loc.index = idx;
|
||||
parser.state.module.addDependency(dep);
|
||||
module.addDependency(dep);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const createHMRExpressionHandler = parser => expr => {
|
||||
const dep = new HMRApiDependency(expr.range);
|
||||
const module = parser.state.module;
|
||||
const dep = new ConstDependency(
|
||||
`${module.moduleArgument}.hot`,
|
||||
expr.range,
|
||||
runtimeRequirements
|
||||
);
|
||||
dep.loc = expr.loc;
|
||||
parser.state.module.addDependency(dep);
|
||||
module.addPresentationalDependency(dep);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -192,29 +208,29 @@ class HotModuleReplacementPlugin {
|
|||
|
||||
const applyImportMetaHot = parser => {
|
||||
parser.hooks.evaluateIdentifier
|
||||
.for("import.meta.hot")
|
||||
.for("import.meta.webpackHot")
|
||||
.tap("HotModuleReplacementPlugin", expr => {
|
||||
return evaluateToIdentifier(
|
||||
"import.meta.hot",
|
||||
"import.meta.webpackHot",
|
||||
"import.meta",
|
||||
() => ["hot"],
|
||||
() => ["webpackHot"],
|
||||
true
|
||||
)(expr);
|
||||
});
|
||||
parser.hooks.call
|
||||
.for("import.meta.hot.accept")
|
||||
.for("import.meta.webpackHot.accept")
|
||||
.tap(
|
||||
"HotModuleReplacementPlugin",
|
||||
createAcceptHandler(parser, ImportMetaHotAcceptDependency)
|
||||
);
|
||||
parser.hooks.call
|
||||
.for("import.meta.hot.decline")
|
||||
.for("import.meta.webpackHot.decline")
|
||||
.tap(
|
||||
"HotModuleReplacementPlugin",
|
||||
createDeclineHandler(parser, ImportMetaHotDeclineDependency)
|
||||
);
|
||||
parser.hooks.expression
|
||||
.for("import.meta.hot")
|
||||
.for("import.meta.webpackHot")
|
||||
.tap("HotModuleReplacementPlugin", createHMRExpressionHandler(parser));
|
||||
};
|
||||
|
||||
|
@ -258,7 +274,7 @@ class HotModuleReplacementPlugin {
|
|||
);
|
||||
//#endregion
|
||||
|
||||
//#region import.meta.hot.* API
|
||||
//#region import.meta.webpackHot.* API
|
||||
compilation.dependencyFactories.set(
|
||||
ImportMetaHotAcceptDependency,
|
||||
normalModuleFactory
|
||||
|
@ -277,11 +293,6 @@ class HotModuleReplacementPlugin {
|
|||
);
|
||||
//#endregion
|
||||
|
||||
compilation.dependencyTemplates.set(
|
||||
HMRApiDependency,
|
||||
new HMRApiDependency.Template()
|
||||
);
|
||||
|
||||
compilation.hooks.record.tap(
|
||||
"HotModuleReplacementPlugin",
|
||||
(compilation, records) => {
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const makeSerializable = require("../util/makeSerializable");
|
||||
const NullDependency = require("./NullDependency");
|
||||
|
||||
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
||||
|
||||
class HMRApiDependency extends NullDependency {
|
||||
constructor(range, apiMethod) {
|
||||
super();
|
||||
|
||||
this.range = range;
|
||||
this.apiMethod = apiMethod;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return "module.hot and import.meta.hot";
|
||||
}
|
||||
|
||||
serialize(context) {
|
||||
const { write } = context;
|
||||
|
||||
write(this.range);
|
||||
write(this.apiMethod);
|
||||
|
||||
super.serialize(context);
|
||||
}
|
||||
|
||||
deserialize(context) {
|
||||
const { read } = context;
|
||||
|
||||
this.range = read();
|
||||
this.apiMethod = read();
|
||||
|
||||
super.deserialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(HMRApiDependency, "webpack/lib/dependencies/HMRApiDependency");
|
||||
|
||||
HMRApiDependency.Template = class HMRApiDependencyTemplate extends NullDependency.Template {
|
||||
/**
|
||||
* @param {Dependency} dependency the dependency for which the template should be applied
|
||||
* @param {ReplaceSource} source the current replace source which can be modified
|
||||
* @param {DependencyTemplateContext} templateContext the context object
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(dependency, source, { module, runtimeRequirements }) {
|
||||
const dep = /** @type {HMRApiDependency} */ (dependency);
|
||||
runtimeRequirements.add(RuntimeGlobals.module);
|
||||
source.replace(
|
||||
dep.range[0],
|
||||
dep.range[1] - 1,
|
||||
`${module.moduleArgument}.hot${dep.apiMethod ? `.${dep.apiMethod}` : ""}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = HMRApiDependency;
|
|
@ -17,7 +17,7 @@ class ImportMetaHotAcceptDependency extends ModuleDependency {
|
|||
}
|
||||
|
||||
get type() {
|
||||
return "import.meta.hot.accept";
|
||||
return "import.meta.webpackHot.accept";
|
||||
}
|
||||
|
||||
get category() {
|
||||
|
|
|
@ -18,7 +18,7 @@ class ImportMetaHotDeclineDependency extends ModuleDependency {
|
|||
}
|
||||
|
||||
get type() {
|
||||
return "import.meta.hot.decline";
|
||||
return "import.meta.webpackHot.decline";
|
||||
}
|
||||
|
||||
get category() {
|
||||
|
|
|
@ -113,8 +113,6 @@ module.exports = {
|
|||
require("../dependencies/ImportMetaHotAcceptDependency"),
|
||||
"dependencies/ImportMetaHotDeclineDependency": () =>
|
||||
require("../dependencies/ImportMetaHotDeclineDependency"),
|
||||
"dependencies/HMRApiDependency": () =>
|
||||
require("../dependencies/HMRApiDependency"),
|
||||
"dependencies/ProvidedDependency": () =>
|
||||
require("../dependencies/ProvidedDependency"),
|
||||
"dependencies/PureExpressionDependency": () =>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { value } from "./file";
|
|
@ -0,0 +1 @@
|
|||
export { value } from "./file";
|
|
@ -0,0 +1,18 @@
|
|||
it("should import a changed chunk", (done) => {
|
||||
import("./chunk").then((chunk) => {
|
||||
expect(chunk.value).toBe(1);
|
||||
import("./chunk2").then((chunk2) => {
|
||||
expect(chunk2.value).toBe(1);
|
||||
NEXT(require("../../update")(done));
|
||||
import.meta.webpackHot.accept(["./chunk", "./chunk2"], () => {
|
||||
import("./chunk").then((chunk) => {
|
||||
expect(chunk.value).toBe(2);
|
||||
import("./chunk2").then((chunk2) => {
|
||||
expect(chunk2.value).toBe(2);
|
||||
done();
|
||||
}).catch(done);
|
||||
}).catch(done);
|
||||
});
|
||||
}).catch(done);
|
||||
}).catch(done);
|
||||
});
|
|
@ -1,5 +1,3 @@
|
|||
export var value = 1;
|
||||
---
|
||||
export var value = 2;
|
||||
---
|
||||
export var value = 3;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
it("should import a changed chunk using module.hot.accept API", (done) => {
|
||||
it("should import a changed chunk", (done) => {
|
||||
import("./chunk").then((chunk) => {
|
||||
expect(chunk.value).toBe(1);
|
||||
import("./chunk2").then((chunk2) => {
|
||||
|
@ -16,22 +16,3 @@ it("should import a changed chunk using module.hot.accept API", (done) => {
|
|||
}).catch(done);
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it("should import a changed chunk using import.meta.hot.accept API", (done) => {
|
||||
import("./chunk").then((chunk) => {
|
||||
expect(chunk.value).toBe(2);
|
||||
import("./chunk2").then((chunk2) => {
|
||||
expect(chunk2.value).toBe(2);
|
||||
NEXT(require("../../update")(done));
|
||||
import.meta.hot.accept(["./chunk", "./chunk2"], () => {
|
||||
import("./chunk").then((chunk) => {
|
||||
expect(chunk.value).toBe(3);
|
||||
import("./chunk2").then((chunk2) => {
|
||||
expect(chunk2.value).toBe(3);
|
||||
done();
|
||||
}).catch(done);
|
||||
}).catch(done);
|
||||
});
|
||||
}).catch(done);
|
||||
}).catch(done);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import vendor from "vendor";
|
||||
import.meta.webpackHot.accept("vendor");
|
||||
it("should hot update a splitted initial chunk", function (done) {
|
||||
expect(vendor).toBe("1");
|
||||
NEXT(
|
||||
require("../../update")(done, true, () => {
|
||||
expect(vendor).toBe("2");
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
3
test/hotCases/chunks/split-chunks-webpackhot/node_modules/vendor.js
generated
vendored
Normal file
3
test/hotCases/chunks/split-chunks-webpackhot/node_modules/vendor.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = "1";
|
||||
---
|
||||
module.exports = "2";
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
output: {
|
||||
filename: "[name].js"
|
||||
},
|
||||
optimization: {
|
||||
chunkIds: "total-size",
|
||||
splitChunks: {
|
||||
chunks: "all",
|
||||
minSize: 0
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
import vendor from "vendor";
|
||||
import.meta.hot.accept("vendor");
|
||||
it("should hot update a splitted initial chunk using import.meta.hot.* API", function (done) {
|
||||
module.hot.accept("vendor");
|
||||
it("should hot update a splitted initial chunk", function (done) {
|
||||
expect(vendor).toBe("1");
|
||||
NEXT(
|
||||
require("../../update")(done, true, () => {
|
||||
|
@ -9,14 +9,3 @@ it("should hot update a splitted initial chunk using import.meta.hot.* API", fun
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should hot update a splitted initial chunk using module.hot.* API", function (done) {
|
||||
expect(vendor).toBe("2");
|
||||
module.hot.accept("vendor");
|
||||
NEXT(
|
||||
require("../../update")(done, true, () => {
|
||||
expect(vendor).toBe("3");
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
module.exports = "1";
|
||||
---
|
||||
module.exports = "2";
|
||||
---
|
||||
module.exports = "3";
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import x from "./module";
|
||||
|
||||
it("should have correct this context", (done) => {
|
||||
expect(x).toEqual("ok1");
|
||||
|
||||
(function() {
|
||||
import.meta.webpackHot.accept("./module", () => {
|
||||
expect(x).toEqual("ok2");
|
||||
expect(this).toEqual({ ok: true });
|
||||
done();
|
||||
});
|
||||
}).call({ ok: true });
|
||||
|
||||
NEXT(require("../../update")(done));
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
export default "ok1";
|
||||
---
|
||||
export default "ok2";
|
|
@ -1,6 +1,6 @@
|
|||
import x from "./module";
|
||||
|
||||
it("should have correct this context in module.hot.accept handler", (done) => {
|
||||
it("should have correct this context in accept handler", (done) => {
|
||||
expect(x).toEqual("ok1");
|
||||
|
||||
(function() {
|
||||
|
@ -13,17 +13,3 @@ it("should have correct this context in module.hot.accept handler", (done) => {
|
|||
|
||||
NEXT(require("../../update")(done));
|
||||
});
|
||||
|
||||
it("should have correct this context in import.meta.hot.accept handler", (done) => {
|
||||
expect(x).toEqual("ok2");
|
||||
|
||||
(function() {
|
||||
import.meta.hot.accept("./module", () => {
|
||||
expect(x).toEqual("ok3");
|
||||
expect(this).toEqual({ ok: true });
|
||||
done();
|
||||
});
|
||||
}).call({ ok: true });
|
||||
|
||||
NEXT(require("../../update")(done));
|
||||
});
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
export default "ok1";
|
||||
---
|
||||
export default "ok2";
|
||||
---
|
||||
export default "ok3";
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import b from "./b";
|
||||
|
||||
export default b;
|
||||
|
||||
if(import.meta.webpackHot) {
|
||||
import.meta.webpackHot.decline("./b");
|
||||
import.meta.webpackHot.accept();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./c"
|
|
@ -0,0 +1,3 @@
|
|||
export default 1;
|
||||
---
|
||||
export default 2;
|
|
@ -0,0 +1,14 @@
|
|||
import a from "./a";
|
||||
|
||||
it("should abort when module is declined by parent", (done) => {
|
||||
expect(a).toBe(1);
|
||||
NEXT(require("../../update")((err) => {
|
||||
try {
|
||||
expect(err.message).toMatch(/Aborted because of declined dependency: \.\/b\.js in \.\/a\.js/);
|
||||
expect(err.message).toMatch(/Update propagation: \.\/c\.js -> \.\/b\.js -> \.\/a\.js/);
|
||||
done();
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
}));
|
||||
});
|
|
@ -2,7 +2,7 @@ import b from "./b";
|
|||
|
||||
export default b;
|
||||
|
||||
if(import.meta.hot) {
|
||||
import.meta.hot.decline("./b");
|
||||
import.meta.hot.accept();
|
||||
if(module.hot) {
|
||||
module.hot.decline("./b");
|
||||
module.hot.accept();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import {val} from "./module";
|
||||
|
||||
it("should accept changes", (done) => {
|
||||
expect(val).toBe(1);
|
||||
NEXT(require("../../update")(done));
|
||||
done();
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import {value} from "dep1";
|
||||
|
||||
export const val = value;
|
||||
|
||||
import.meta.webpackHot.accept("dep1");
|
3
test/hotCases/esm-dependency-import/import-meta-webpack-hot/node_modules/dep1/file.js
generated
vendored
Normal file
3
test/hotCases/esm-dependency-import/import-meta-webpack-hot/node_modules/dep1/file.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
export var value = 1;
|
||||
---
|
||||
export var value = 2;
|
|
@ -1,6 +1,6 @@
|
|||
import {val} from "./module";
|
||||
|
||||
it("should fail import a changed chunk using module.hot.accept API", (done) => {
|
||||
it("should fail accept changes", (done) => {
|
||||
expect(val).toBe(1);
|
||||
NEXT(require("../../update")((err) => {
|
||||
try {
|
1
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/exports.js
generated
vendored
Normal file
1
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/exports.js
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export {value} from "./file";
|
3
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/file.js
generated
vendored
Normal file
3
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/file.js
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
export var value = 1;
|
||||
---
|
||||
export var value = 2;
|
5
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/main.js
generated
vendored
Normal file
5
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/main.js
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
(() => {
|
||||
throw new Error("should not resolve");
|
||||
})();
|
||||
|
||||
export default 1;
|
6
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/package.json
generated
vendored
Normal file
6
test/hotCases/esm-dependency-import/module-hot/node_modules/dep1/package.json
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"exports": {
|
||||
"import": "./exports.js",
|
||||
"default": "./main.js"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue