Skip to content

Commit ff4bfbe

Browse files
fix: compatibility with webpack@5
1 parent ca8c327 commit ff4bfbe

File tree

4 files changed

+246
-117
lines changed

4 files changed

+246
-117
lines changed

src/index.js

+186-87
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable class-methods-use-this */
22

3-
import webpack from 'webpack';
3+
import webpack, { version as webpackVersion } from 'webpack';
4+
45
import validateOptions from 'schema-utils';
56

67
import CssDependency from './CssDependency';
@@ -16,6 +17,8 @@ const {
1617
util: { createHash },
1718
} = webpack;
1819

20+
const isWebpack4 = webpackVersion[0] === '4';
21+
1922
const MODULE_TYPE = 'css/mini-extract';
2023

2124
const pluginName = 'mini-css-extract-plugin';
@@ -26,6 +29,19 @@ const REGEXP_NAME = /\[name\]/i;
2629
const REGEXP_PLACEHOLDERS = /\[(name|id|chunkhash)\]/g;
2730
const DEFAULT_FILENAME = '[name].css';
2831

32+
const compareIds = (a, b) => {
33+
if (typeof a !== typeof b) {
34+
return typeof a < typeof b ? -1 : 1;
35+
}
36+
if (a < b) return -1;
37+
if (a > b) return 1;
38+
return 0;
39+
};
40+
41+
const compareModulesByIdentifier = (a, b) => {
42+
return compareIds(a.identifier(), b.identifier());
43+
};
44+
2945
class CssDependencyTemplate {
3046
apply() {}
3147
}
@@ -85,8 +101,8 @@ class CssModule extends webpack.Module {
85101
callback();
86102
}
87103

88-
updateHash(hash) {
89-
super.updateHash(hash);
104+
updateHash(hash, context) {
105+
super.updateHash(hash, context);
90106

91107
hash.update(this.content);
92108
hash.update(this.media || '');
@@ -141,94 +157,151 @@ class MiniCssExtractPlugin {
141157
new CssDependencyTemplate()
142158
);
143159

144-
compilation.mainTemplate.hooks.renderManifest.tap(
145-
pluginName,
146-
(result, { chunk }) => {
147-
const renderedModules = Array.from(chunk.modulesIterable).filter(
148-
(module) => module.type === MODULE_TYPE
149-
);
150-
151-
if (renderedModules.length > 0) {
152-
result.push({
153-
render: () =>
154-
this.renderContentAsset(
155-
compilation,
160+
if (isWebpack4) {
161+
compilation.mainTemplate.hooks.renderManifest.tap(
162+
pluginName,
163+
(result, { chunk }) => {
164+
const { chunkGraph } = compilation;
165+
166+
const renderedModules = Array.from(
167+
this.getChunkModules(chunk, chunkGraph)
168+
).filter((module) => module.type === MODULE_TYPE);
169+
170+
const filenameTemplate =
171+
chunk.filenameTemplate ||
172+
(({ chunk: chunkData }) =>
173+
this.options.moduleFilename(chunkData));
174+
175+
if (renderedModules.length > 0) {
176+
result.push({
177+
render: () =>
178+
this.renderContentAsset(
179+
compilation,
180+
chunk,
181+
renderedModules,
182+
compilation.runtimeTemplate.requestShortener
183+
),
184+
filenameTemplate,
185+
pathOptions: {
156186
chunk,
157-
renderedModules,
158-
compilation.runtimeTemplate.requestShortener
159-
),
160-
filenameTemplate: ({ chunk: chunkData }) =>
161-
this.options.moduleFilename(chunkData),
162-
pathOptions: {
163-
chunk,
164-
contentHashType: MODULE_TYPE,
165-
},
166-
identifier: `${pluginName}.${chunk.id}`,
167-
hash: chunk.contentHash[MODULE_TYPE],
168-
});
187+
contentHashType: MODULE_TYPE,
188+
},
189+
identifier: `${pluginName}.${chunk.id}`,
190+
hash: chunk.contentHash[MODULE_TYPE],
191+
});
192+
}
169193
}
170-
}
171-
);
172-
173-
compilation.chunkTemplate.hooks.renderManifest.tap(
174-
pluginName,
175-
(result, { chunk }) => {
176-
const renderedModules = Array.from(chunk.modulesIterable).filter(
177-
(module) => module.type === MODULE_TYPE
178-
);
194+
);
179195

180-
if (renderedModules.length > 0) {
181-
result.push({
182-
render: () =>
183-
this.renderContentAsset(
184-
compilation,
196+
compilation.chunkTemplate.hooks.renderManifest.tap(
197+
pluginName,
198+
(result, { chunk }) => {
199+
const { chunkGraph } = compilation;
200+
201+
const renderedModules = Array.from(
202+
this.getChunkModules(chunk, chunkGraph)
203+
).filter((module) => module.type === MODULE_TYPE);
204+
205+
const filenameTemplate =
206+
chunk.filenameTemplate || this.options.chunkFilename;
207+
208+
if (renderedModules.length > 0) {
209+
result.push({
210+
render: () =>
211+
this.renderContentAsset(
212+
compilation,
213+
chunk,
214+
renderedModules,
215+
compilation.runtimeTemplate.requestShortener
216+
),
217+
filenameTemplate,
218+
pathOptions: {
185219
chunk,
186-
renderedModules,
187-
compilation.runtimeTemplate.requestShortener
188-
),
189-
filenameTemplate: this.options.chunkFilename,
190-
pathOptions: {
191-
chunk,
192-
contentHashType: MODULE_TYPE,
193-
},
194-
identifier: `${pluginName}.${chunk.id}`,
195-
hash: chunk.contentHash[MODULE_TYPE],
196-
});
220+
contentHashType: MODULE_TYPE,
221+
},
222+
identifier: `${pluginName}.${chunk.id}`,
223+
hash: chunk.contentHash[MODULE_TYPE],
224+
});
225+
}
197226
}
198-
}
199-
);
200-
201-
compilation.mainTemplate.hooks.hashForChunk.tap(
202-
pluginName,
203-
(hash, chunk) => {
204-
const { chunkFilename } = this.options;
205-
206-
if (REGEXP_CHUNKHASH.test(chunkFilename)) {
207-
hash.update(JSON.stringify(chunk.getChunkMaps(true).hash));
227+
);
228+
} else {
229+
compilation.hooks.renderManifest.tap(
230+
pluginName,
231+
(result, { chunk }) => {
232+
const { chunkGraph } = compilation;
233+
234+
const renderedModules = Array.from(
235+
this.getChunkModules(chunk, chunkGraph)
236+
).filter((module) => module.type === MODULE_TYPE);
237+
238+
const filenameTemplate =
239+
chunk.filenameTemplate ||
240+
chunk.hasRuntime() ||
241+
chunk.isOnlyInitial()
242+
? ({ chunk: chunkData }) =>
243+
this.options.moduleFilename(chunkData)
244+
: this.options.chunkFilename;
245+
246+
if (renderedModules.length > 0) {
247+
result.push({
248+
render: () =>
249+
this.renderContentAsset(
250+
compilation,
251+
chunk,
252+
renderedModules,
253+
compilation.runtimeTemplate.requestShortener
254+
),
255+
filenameTemplate,
256+
pathOptions: {
257+
chunk,
258+
contentHashType: MODULE_TYPE,
259+
},
260+
identifier: `${pluginName}.${chunk.id}`,
261+
hash: chunk.contentHash[MODULE_TYPE],
262+
});
263+
}
208264
}
265+
);
266+
}
209267

210-
if (REGEXP_CONTENTHASH.test(chunkFilename)) {
211-
hash.update(
212-
JSON.stringify(
213-
chunk.getChunkMaps(true).contentHash[MODULE_TYPE] || {}
214-
)
215-
);
216-
}
268+
/*
269+
* For webpack 5 this will be unneeded once the logic uses a RuntimeModule
270+
* as the content of runtime modules is hashed and added to the chunk hash automatically
271+
* */
272+
if (isWebpack4) {
273+
compilation.mainTemplate.hooks.hashForChunk.tap(
274+
pluginName,
275+
(hash, chunk) => {
276+
const { chunkFilename } = this.options;
277+
278+
if (REGEXP_CHUNKHASH.test(chunkFilename)) {
279+
hash.update(JSON.stringify(chunk.getChunkMaps(true).hash));
280+
}
281+
282+
if (REGEXP_CONTENTHASH.test(chunkFilename)) {
283+
hash.update(
284+
JSON.stringify(
285+
chunk.getChunkMaps(true).contentHash[MODULE_TYPE] || {}
286+
)
287+
);
288+
}
217289

218-
if (REGEXP_NAME.test(chunkFilename)) {
219-
hash.update(JSON.stringify(chunk.getChunkMaps(true).name));
290+
if (REGEXP_NAME.test(chunkFilename)) {
291+
hash.update(JSON.stringify(chunk.getChunkMaps(true).name));
292+
}
220293
}
221-
}
222-
);
294+
);
295+
}
223296

224297
compilation.hooks.contentHash.tap(pluginName, (chunk) => {
225-
const { outputOptions } = compilation;
298+
const { outputOptions, chunkGraph } = compilation;
226299
const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
227300
const hash = createHash(hashFunction);
228301

229-
for (const m of chunk.modulesIterable) {
302+
for (const m of this.getChunkModules(chunk, chunkGraph)) {
230303
if (m.type === MODULE_TYPE) {
231-
m.updateHash(hash);
304+
m.updateHash(hash, { chunkGraph });
232305
}
233306
}
234307

@@ -242,7 +315,7 @@ class MiniCssExtractPlugin {
242315
const { mainTemplate } = compilation;
243316

244317
mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {
245-
const chunkMap = this.getCssChunkObject(chunk);
318+
const chunkMap = this.getCssChunkObject(chunk, compilation);
246319

247320
if (Object.keys(chunkMap).length > 0) {
248321
return Template.asString([
@@ -263,17 +336,25 @@ class MiniCssExtractPlugin {
263336
mainTemplate.hooks.requireEnsure.tap(
264337
pluginName,
265338
(source, chunk, hash) => {
266-
const chunkMap = this.getCssChunkObject(chunk);
339+
const chunkMap = this.getCssChunkObject(chunk, compilation);
267340

268341
if (Object.keys(chunkMap).length > 0) {
342+
const maintemplateObject = isWebpack4 ? mainTemplate : compilation;
269343
const chunkMaps = chunk.getChunkMaps();
270-
const { crossOriginLoading } = mainTemplate.outputOptions;
271-
const linkHrefPath = mainTemplate.getAssetPath(
344+
const { crossOriginLoading } = maintemplateObject.outputOptions;
345+
const linkHrefPath = maintemplateObject.getAssetPath(
272346
JSON.stringify(this.options.chunkFilename),
273347
{
274-
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
348+
hash: isWebpack4
349+
? `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`
350+
: `" + ${webpack.RuntimeGlobals.getFullHash} + "`,
275351
hashWithLength: (length) =>
276-
`" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
352+
isWebpack4
353+
? `" + ${mainTemplate.renderCurrentHashCode(
354+
hash,
355+
length
356+
)} + "`
357+
: `" + ${webpack.RuntimeGlobals.getFullHash} + "`,
277358
chunk: {
278359
id: '" + chunkId + "',
279360
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
@@ -334,7 +415,11 @@ class MiniCssExtractPlugin {
334415
'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {',
335416
Template.indent([
336417
`var href = ${linkHrefPath};`,
337-
`var fullhref = ${mainTemplate.requireFn}.p + href;`,
418+
`var fullhref = ${
419+
isWebpack4
420+
? mainTemplate.requireFn
421+
: webpack.RuntimeGlobals.require
422+
}.p + href;`,
338423
'var existingLinkTags = document.getElementsByTagName("link");',
339424
'for(var i = 0; i < existingLinkTags.length; i++) {',
340425
Template.indent([
@@ -395,11 +480,21 @@ class MiniCssExtractPlugin {
395480
});
396481
}
397482

398-
getCssChunkObject(mainChunk) {
483+
getChunkModules(chunk, chunkGraph) {
484+
return typeof chunkGraph !== 'undefined'
485+
? chunkGraph.getOrderedChunkModulesIterable(
486+
chunk,
487+
compareModulesByIdentifier
488+
)
489+
: chunk.modulesIterable;
490+
}
491+
492+
getCssChunkObject(mainChunk, compilation) {
399493
const obj = {};
494+
const { chunkGraph } = compilation;
400495

401496
for (const chunk of mainChunk.getAllAsyncChunks()) {
402-
for (const module of chunk.modulesIterable) {
497+
for (const module of this.getChunkModules(chunk, chunkGraph)) {
403498
if (module.type === MODULE_TYPE) {
404499
obj[chunk.id] = 1;
405500
break;
@@ -414,8 +509,12 @@ class MiniCssExtractPlugin {
414509
let usedModules;
415510

416511
const [chunkGroup] = chunk.groupsIterable;
512+
const moduleIndexFunctionName =
513+
typeof compilation.chunkGraph !== 'undefined'
514+
? 'getModulePostOrderIndex'
515+
: 'getModuleIndex2';
417516

418-
if (typeof chunkGroup.getModuleIndex2 === 'function') {
517+
if (typeof chunkGroup[moduleIndexFunctionName] === 'function') {
419518
// Store dependencies for modules
420519
const moduleDependencies = new Map(modules.map((m) => [m, new Set()]));
421520
const moduleDependenciesReasons = new Map(
@@ -430,7 +529,7 @@ class MiniCssExtractPlugin {
430529
.map((m) => {
431530
return {
432531
module: m,
433-
index: cg.getModuleIndex2(m),
532+
index: cg[moduleIndexFunctionName](m),
434533
};
435534
})
436535
// eslint-disable-next-line no-undefined

0 commit comments

Comments
 (0)