mirror of https://github.com/webpack/webpack.git
refactor: CSS HMR
This commit is contained in:
parent
f4092a6059
commit
193b712734
|
@ -65,6 +65,11 @@ class CssModule extends NormalModule {
|
|||
identifier += `|${inheritance.join("|")}`;
|
||||
}
|
||||
|
||||
// We generate extra code for HMR, so we need to invalidate the module
|
||||
if (this.hot) {
|
||||
identifier += `|${this.hot}`;
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,11 +40,11 @@
|
|||
|
||||
/**
|
||||
* @typedef {object} CssDependencyTemplateContextExtras
|
||||
* @property {CssExportsData} cssExportsData the css exports data
|
||||
* @property {CssData} cssData the css exports data
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CssExportsData
|
||||
* @typedef {object} CssData
|
||||
* @property {boolean} esModule whether export __esModule
|
||||
* @property {Map<string, string>} exports the css exports
|
||||
*/
|
||||
|
|
|
@ -856,6 +856,10 @@ To fix this, make sure to include [runtime] in the output.hotUpdateMainFilename
|
|||
.tap(PLUGIN_NAME, parser => {
|
||||
applyImportMetaHot(parser);
|
||||
});
|
||||
normalModuleFactory.hooks.module.tap(PLUGIN_NAME, module => {
|
||||
module.hot = true;
|
||||
return module;
|
||||
});
|
||||
|
||||
NormalModule.getCompilationHooks(compilation).loader.tap(
|
||||
PLUGIN_NAME,
|
||||
|
|
|
@ -200,6 +200,9 @@ class Module extends DependenciesBlock {
|
|||
/** @type {boolean} */
|
||||
this.useSimpleSourceMap = false;
|
||||
|
||||
// Is hot context
|
||||
/** @type {boolean} */
|
||||
this.hot = false;
|
||||
// Info from Build
|
||||
/** @type {WebpackError[] | undefined} */
|
||||
this._warnings = undefined;
|
||||
|
@ -1075,6 +1078,7 @@ class Module extends DependenciesBlock {
|
|||
write(this.factoryMeta);
|
||||
write(this.useSourceMap);
|
||||
write(this.useSimpleSourceMap);
|
||||
write(this.hot);
|
||||
write(
|
||||
this._warnings !== undefined && this._warnings.length === 0
|
||||
? undefined
|
||||
|
@ -1104,6 +1108,7 @@ class Module extends DependenciesBlock {
|
|||
this.factoryMeta = read();
|
||||
this.useSourceMap = read();
|
||||
this.useSimpleSourceMap = read();
|
||||
this.hot = read();
|
||||
this._warnings = read();
|
||||
this._errors = read();
|
||||
this.buildMeta = read();
|
||||
|
|
|
@ -22,8 +22,8 @@ const Template = require("../Template");
|
|||
/** @typedef {import("../../declarations/WebpackOptions").CssModuleGeneratorOptions} CssModuleGeneratorOptions */
|
||||
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
|
||||
/** @typedef {import("../Dependency")} Dependency */
|
||||
/** @typedef {import("../DependencyTemplate").CssData} CssData */
|
||||
/** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
|
||||
/** @typedef {import("../DependencyTemplate").CssExportsData} CssExportsData */
|
||||
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
|
||||
/** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
|
||||
/** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
|
||||
|
@ -69,8 +69,8 @@ class CssGenerator extends Generator {
|
|||
|
||||
/** @type {InitFragment<GenerateContext>[]} */
|
||||
const initFragments = [];
|
||||
/** @type {CssExportsData} */
|
||||
const cssExportsData = {
|
||||
/** @type {CssData} */
|
||||
const cssData = {
|
||||
esModule: this.esModule,
|
||||
exports: new Map()
|
||||
};
|
||||
|
@ -91,7 +91,7 @@ class CssGenerator extends Generator {
|
|||
/** @type {CodeGenerationResults} */
|
||||
(generateContext.codeGenerationResults),
|
||||
initFragments,
|
||||
cssExportsData,
|
||||
cssData,
|
||||
get chunkInitFragments() {
|
||||
if (!chunkInitFragments) {
|
||||
const data =
|
||||
|
@ -131,12 +131,14 @@ class CssGenerator extends Generator {
|
|||
|
||||
switch (generateContext.type) {
|
||||
case "javascript": {
|
||||
module.buildInfo.cssData = cssData;
|
||||
|
||||
generateContext.runtimeRequirements.add(RuntimeGlobals.module);
|
||||
|
||||
if (generateContext.concatenationScope) {
|
||||
const source = new ConcatSource();
|
||||
const usedIdentifiers = new Set();
|
||||
for (const [name, v] of cssExportsData.exports) {
|
||||
for (const [name, v] of cssData.exports) {
|
||||
const usedName = generateContext.moduleGraph
|
||||
.getExportInfo(module, name)
|
||||
.getUsedName(name, generateContext.runtime);
|
||||
|
@ -180,7 +182,7 @@ class CssGenerator extends Generator {
|
|||
|
||||
const exports = [];
|
||||
|
||||
for (const [name, v] of cssExportsData.exports) {
|
||||
for (const [name, v] of cssData.exports) {
|
||||
exports.push(`\t${JSON.stringify(name)}: ${JSON.stringify(v)}`);
|
||||
}
|
||||
|
||||
|
@ -199,11 +201,6 @@ class CssGenerator extends Generator {
|
|||
|
||||
generateContext.runtimeRequirements.add(RuntimeGlobals.hasCssModules);
|
||||
|
||||
const data =
|
||||
/** @type {NonNullable<GenerateContext["getData"]>} */
|
||||
(generateContext.getData)();
|
||||
data.set("css-exports", cssExportsData);
|
||||
|
||||
return InitFragment.addToSource(source, initFragments, generateContext);
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +223,16 @@ class CssGenerator extends Generator {
|
|||
getSize(module, type) {
|
||||
switch (type) {
|
||||
case "javascript": {
|
||||
return 42;
|
||||
/** @type {undefined} */
|
||||
const exports = module.buildInfo.cssData.exports;
|
||||
const stringifiedExports = JSON.stringify(
|
||||
Array.from(exports).reduce((obj, [key, value]) => {
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
}, {})
|
||||
);
|
||||
|
||||
return stringifiedExports.length + 42;
|
||||
}
|
||||
case "css": {
|
||||
const originalSource = module.originalSource();
|
||||
|
|
|
@ -190,21 +190,13 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
runtimeTemplate.outputOptions.uniqueName
|
||||
)};`
|
||||
: "// data-webpack is not used as build has no uniqueName",
|
||||
`var loadCssChunkData = ${runtimeTemplate.basicFunction(
|
||||
"target, chunkId",
|
||||
[
|
||||
`${withHmr ? "var moduleIds = [];" : ""}`,
|
||||
`${
|
||||
withHmr ? `if(target == ${RuntimeGlobals.moduleFactories}) ` : ""
|
||||
}installedChunks[chunkId] = 0;`,
|
||||
withHmr ? "return moduleIds;" : ""
|
||||
]
|
||||
)}`,
|
||||
withLoading || withHmr
|
||||
? Template.asString([
|
||||
'var loadingAttribute = "data-webpack-loading";',
|
||||
`var loadStylesheet = ${runtimeTemplate.basicFunction(
|
||||
`chunkId, url, done${withHmr ? ", hmr" : ""}${
|
||||
`chunkId, url, done${
|
||||
withFetchPriority ? ", fetchPriority" : ""
|
||||
}`,
|
||||
}${withHmr ? ", hmr" : ""}`,
|
||||
[
|
||||
'var link, needAttach, key = "chunk-" + chunkId;',
|
||||
withHmr ? "if(!hmr) {" : "",
|
||||
|
@ -249,12 +241,16 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
"link.onload = onLinkComplete.bind(null, link.onload);"
|
||||
]),
|
||||
"} else onLinkComplete(undefined, { type: 'load', target: link });", // We assume any existing stylesheet is render blocking
|
||||
withFetchPriority
|
||||
? 'if (hmr && hmr.getAttribute("fetchpriority")) link.setAttribute("fetchpriority", hmr.getAttribute("fetchpriority"));'
|
||||
: "",
|
||||
withHmr ? "hmr ? document.head.insertBefore(link, hmr) :" : "",
|
||||
"needAttach && document.head.appendChild(link);",
|
||||
"return link;"
|
||||
]
|
||||
)};`,
|
||||
"",
|
||||
)};`
|
||||
])
|
||||
: "",
|
||||
withLoading
|
||||
? Template.asString([
|
||||
`${fn}.css = ${runtimeTemplate.basicFunction(
|
||||
|
@ -306,7 +302,7 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
]),
|
||||
"} else {",
|
||||
Template.indent([
|
||||
`loadCssChunkData(${RuntimeGlobals.moduleFactories}, chunkId);`,
|
||||
"installedChunks[chunkId] = 0;",
|
||||
"installedChunkData[0]();"
|
||||
]),
|
||||
"}"
|
||||
|
@ -429,32 +425,20 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
"var oldTags = [];",
|
||||
"var newTags = [];",
|
||||
`var applyHandler = ${runtimeTemplate.basicFunction("options", [
|
||||
`return { dispose: ${runtimeTemplate.basicFunction(
|
||||
"",
|
||||
[]
|
||||
)}, apply: ${runtimeTemplate.basicFunction("", [
|
||||
"var moduleIds = [];",
|
||||
`newTags.forEach(${runtimeTemplate.expressionFunction(
|
||||
"info[1].sheet.disabled = false",
|
||||
"info"
|
||||
)});`,
|
||||
`return { dispose: ${runtimeTemplate.basicFunction("", [
|
||||
"while(oldTags.length) {",
|
||||
Template.indent([
|
||||
"var oldTag = oldTags.pop();",
|
||||
"if(oldTag.parentNode) oldTag.parentNode.removeChild(oldTag);"
|
||||
]),
|
||||
"}",
|
||||
"}"
|
||||
])}, apply: ${runtimeTemplate.basicFunction("", [
|
||||
"while(newTags.length) {",
|
||||
Template.indent([
|
||||
"var info = newTags.pop();",
|
||||
`var chunkModuleIds = loadCssChunkData(${RuntimeGlobals.moduleFactories}, info[0]);`,
|
||||
`chunkModuleIds.forEach(${runtimeTemplate.expressionFunction(
|
||||
"moduleIds.push(id)",
|
||||
"id"
|
||||
)});`
|
||||
"var newTag = newTags.pop();",
|
||||
"newTag.sheet.disabled = false"
|
||||
]),
|
||||
"}",
|
||||
"return moduleIds;"
|
||||
"}"
|
||||
])} };`
|
||||
])}`,
|
||||
`var cssTextKey = ${runtimeTemplate.returningFunction(
|
||||
|
@ -494,20 +478,14 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|||
"} else {",
|
||||
Template.indent([
|
||||
"try { if(cssTextKey(oldTag) == cssTextKey(link)) { if(link.parentNode) link.parentNode.removeChild(link); return resolve(); } } catch(e) {}",
|
||||
"var factories = {};",
|
||||
"loadCssChunkData(factories, chunkId);",
|
||||
`Object.keys(factories).forEach(${runtimeTemplate.expressionFunction(
|
||||
"updatedModulesList.push(id)",
|
||||
"id"
|
||||
)})`,
|
||||
"link.sheet.disabled = true;",
|
||||
"oldTags.push(oldTag);",
|
||||
"newTags.push([chunkId, link]);",
|
||||
"newTags.push(link);",
|
||||
"resolve();"
|
||||
]),
|
||||
"}"
|
||||
]
|
||||
)}, oldTag);`
|
||||
)}, ${withFetchPriority ? "undefined," : ""} oldTag);`
|
||||
]
|
||||
)}));`
|
||||
])});`
|
||||
|
|
|
@ -10,7 +10,8 @@ const {
|
|||
ConcatSource,
|
||||
PrefixSource,
|
||||
ReplaceSource,
|
||||
CachedSource
|
||||
CachedSource,
|
||||
RawSource
|
||||
} = require("webpack-sources");
|
||||
const Compilation = require("../Compilation");
|
||||
const CssModule = require("../CssModule");
|
||||
|
@ -24,6 +25,7 @@ const {
|
|||
} = require("../ModuleTypeConstants");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const SelfModuleFactory = require("../SelfModuleFactory");
|
||||
const Template = require("../Template");
|
||||
const WebpackError = require("../WebpackError");
|
||||
const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency");
|
||||
const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency");
|
||||
|
@ -33,6 +35,7 @@ const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifier
|
|||
const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
|
||||
const CssUrlDependency = require("../dependencies/CssUrlDependency");
|
||||
const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
||||
const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
|
||||
const { compareModulesByIdentifier } = require("../util/comparators");
|
||||
const createSchemaValidation = require("../util/create-schema-validation");
|
||||
const createHash = require("../util/createHash");
|
||||
|
@ -358,8 +361,36 @@ class CssModulesPlugin {
|
|||
return new CssModule(createData);
|
||||
});
|
||||
}
|
||||
|
||||
JavascriptModulesPlugin.getCompilationHooks(
|
||||
compilation
|
||||
).renderModuleContent.tap(PLUGIN_NAME, (source, module) => {
|
||||
if (module instanceof CssModule && module.hot) {
|
||||
const exports = module.buildInfo.cssData.exports;
|
||||
const stringifiedExports = JSON.stringify(
|
||||
Array.from(exports).reduce((obj, [key, value]) => {
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
}, {})
|
||||
);
|
||||
|
||||
const hmrCode = Template.asString([
|
||||
"",
|
||||
`var exports = ${stringifiedExports};`,
|
||||
"// only invalidate when locals change",
|
||||
"if (module.hot.data && module.hot.data.exports && module.hot.data.exports != exports) {",
|
||||
Template.indent("module.hot.invalidate();"),
|
||||
"} else {",
|
||||
Template.indent("module.hot.accept();"),
|
||||
"}",
|
||||
"module.hot.dispose(function(data) { data.exports = exports; });"
|
||||
]);
|
||||
|
||||
return new ConcatSource(source, "\n", new RawSource(hmrCode));
|
||||
}
|
||||
});
|
||||
const orderedCssModulesPerChunk = new WeakMap();
|
||||
compilation.hooks.afterCodeGeneration.tap("CssModulesPlugin", () => {
|
||||
compilation.hooks.afterCodeGeneration.tap(PLUGIN_NAME, () => {
|
||||
const { chunkGraph } = compilation;
|
||||
for (const chunk of compilation.chunks) {
|
||||
if (CssModulesPlugin.chunkHasCss(chunk, chunkGraph)) {
|
||||
|
@ -370,13 +401,10 @@ class CssModulesPlugin {
|
|||
}
|
||||
}
|
||||
});
|
||||
compilation.hooks.chunkHash.tap(
|
||||
"CssModulesPlugin",
|
||||
(chunk, hash, context) => {
|
||||
compilation.hooks.chunkHash.tap(PLUGIN_NAME, (chunk, hash, context) => {
|
||||
hooks.chunkHash.call(chunk, hash, context);
|
||||
}
|
||||
);
|
||||
compilation.hooks.contentHash.tap("CssModulesPlugin", chunk => {
|
||||
});
|
||||
compilation.hooks.contentHash.tap(PLUGIN_NAME, chunk => {
|
||||
const {
|
||||
chunkGraph,
|
||||
codeGenerationResults,
|
||||
|
@ -483,7 +511,6 @@ class CssModulesPlugin {
|
|||
onceForChunkSet.add(chunk);
|
||||
if (!isEnabledForChunk(chunk)) return;
|
||||
|
||||
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
|
||||
set.add(RuntimeGlobals.makeNamespaceObject);
|
||||
|
||||
const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
|
||||
|
@ -531,7 +558,6 @@ class CssModulesPlugin {
|
|||
}
|
||||
set.add(RuntimeGlobals.publicPath);
|
||||
set.add(RuntimeGlobals.getChunkCssFilename);
|
||||
set.add(RuntimeGlobals.moduleFactoriesAddOnly);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -801,7 +827,6 @@ class CssModulesPlugin {
|
|||
*/
|
||||
renderChunk(
|
||||
{
|
||||
uniqueName,
|
||||
undoPath,
|
||||
chunk,
|
||||
chunkGraph,
|
||||
|
|
|
@ -125,11 +125,7 @@ CssIcssExportDependency.Template = class CssIcssExportDependencyTemplate extends
|
|||
* @param {DependencyTemplateContext} templateContext the context object
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(
|
||||
dependency,
|
||||
source,
|
||||
{ cssExportsData, module: m, runtime, moduleGraph }
|
||||
) {
|
||||
apply(dependency, source, { cssData, module: m, runtime, moduleGraph }) {
|
||||
const dep = /** @type {CssIcssExportDependency} */ (dependency);
|
||||
const module = /** @type {CssModule} */ (m);
|
||||
const convention =
|
||||
|
@ -147,7 +143,7 @@ CssIcssExportDependency.Template = class CssIcssExportDependencyTemplate extends
|
|||
);
|
||||
|
||||
for (const used of usedNames.concat(names)) {
|
||||
cssExportsData.exports.set(used, dep.value);
|
||||
cssData.exports.set(used, dep.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -115,12 +115,12 @@ CssIcssSymbolDependency.Template = class CssValueAtRuleDependencyTemplate extend
|
|||
* @param {DependencyTemplateContext} templateContext the context object
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(dependency, source, { cssExportsData }) {
|
||||
apply(dependency, source, { cssData }) {
|
||||
const dep = /** @type {CssIcssSymbolDependency} */ (dependency);
|
||||
|
||||
source.replace(dep.range[0], dep.range[1] - 1, dep.value);
|
||||
|
||||
cssExportsData.exports.set(dep.name, dep.value);
|
||||
cssData.exports.set(dep.name, dep.value);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ CssLocalIdentifierDependency.Template = class CssLocalIdentifierDependencyTempla
|
|||
* @returns {void}
|
||||
*/
|
||||
apply(dependency, source, templateContext) {
|
||||
const { module: m, moduleGraph, runtime, cssExportsData } = templateContext;
|
||||
const { module: m, moduleGraph, runtime, cssData } = templateContext;
|
||||
const dep = /** @type {CssLocalIdentifierDependency} */ (dependency);
|
||||
const module = /** @type {CssModule} */ (m);
|
||||
const convention =
|
||||
|
@ -364,7 +364,7 @@ CssLocalIdentifierDependency.Template = class CssLocalIdentifierDependencyTempla
|
|||
source.replace(dep.range[0], dep.range[1] - 1, identifier);
|
||||
|
||||
for (const used of usedNames.concat(names)) {
|
||||
cssExportsData.exports.set(used, identifier);
|
||||
cssData.exports.set(used, identifier);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8730,6 +8730,7 @@ declare class Module extends DependenciesBlock {
|
|||
factoryMeta?: FactoryMeta;
|
||||
useSourceMap: boolean;
|
||||
useSimpleSourceMap: boolean;
|
||||
hot: boolean;
|
||||
buildMeta?: BuildMeta;
|
||||
buildInfo?: BuildInfo;
|
||||
presentationalDependencies?: Dependency[];
|
||||
|
|
Loading…
Reference in New Issue