Skip to content

Commit c2ad59d

Browse files
refactor: add runtimeRequirementInTree hook for webpack 5
1 parent ffded7a commit c2ad59d

File tree

2 files changed

+322
-178
lines changed

2 files changed

+322
-178
lines changed

src/CssChunkLoadingRuntimeModule.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import webpack from 'webpack';
2+
3+
const { RuntimeGlobals, RuntimeModule, Template } = webpack;
4+
5+
class CssChunkLoadingRuntimeModule extends RuntimeModule {
6+
constructor(runtimeRequirements, chunkMap, MODULE_TYPE, pluginName, options) {
7+
super('css chunk loading', 10);
8+
this.runtimeRequirements = runtimeRequirements;
9+
this.chunkMap = chunkMap;
10+
this.MODULE_TYPE = MODULE_TYPE;
11+
this.pluginName = pluginName;
12+
this.options = options;
13+
}
14+
15+
generate() {
16+
const fn = RuntimeGlobals.ensureChunkHandlers;
17+
const { compilation, chunk } = this;
18+
const { runtimeTemplate } = compilation;
19+
const withCompat = this.runtimeRequirements.has(
20+
RuntimeGlobals.compatGetDefaultExport
21+
);
22+
23+
const chunkMaps = chunk.getChunkMaps();
24+
const { crossOriginLoading } = compilation.outputOptions;
25+
const linkHrefPath = compilation.getAssetPath(
26+
JSON.stringify(this.options.chunkFilename),
27+
{
28+
hash: `" + ${RuntimeGlobals.getFullHash} + "`,
29+
hashWithLength: () => `" + ${RuntimeGlobals.getFullHash} + "`,
30+
chunk: {
31+
id: '" + chunkId + "',
32+
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
33+
hashWithLength(length) {
34+
const shortChunkHashMap = Object.create(null);
35+
36+
for (const chunkId of Object.keys(chunkMaps.hash)) {
37+
if (typeof chunkMaps.hash[chunkId] === 'string') {
38+
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substring(
39+
0,
40+
length
41+
);
42+
}
43+
}
44+
45+
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
46+
},
47+
contentHash: {
48+
[this.MODULE_TYPE]: `" + ${JSON.stringify(
49+
chunkMaps.contentHash[this.MODULE_TYPE]
50+
)}[chunkId] + "`,
51+
},
52+
contentHashWithLength: {
53+
[this.MODULE_TYPE]: (length) => {
54+
const shortContentHashMap = {};
55+
const contentHash = chunkMaps.contentHash[this.MODULE_TYPE];
56+
57+
for (const chunkId of Object.keys(contentHash)) {
58+
if (typeof contentHash[chunkId] === 'string') {
59+
shortContentHashMap[chunkId] = contentHash[chunkId].substring(
60+
0,
61+
length
62+
);
63+
}
64+
}
65+
66+
return `" + ${JSON.stringify(shortContentHashMap)}[chunkId] + "`;
67+
},
68+
},
69+
name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`,
70+
},
71+
contentHashType: this.MODULE_TYPE,
72+
}
73+
);
74+
75+
return Template.asString([
76+
'',
77+
'// object to store loaded CSS chunks',
78+
'var installedCssChunks = {',
79+
Template.indent(
80+
chunk.ids.map((id) => `${JSON.stringify(id)}: 0`).join(',\n')
81+
),
82+
'}',
83+
'',
84+
withCompat
85+
? Template.asString([
86+
`${fn}.n = ${runtimeTemplate.basicFunction(
87+
'chunkId, promises',
88+
Template.indent([
89+
// source,
90+
`// ${this.pluginName} CSS loading`,
91+
`var cssChunks = ${JSON.stringify(this.chunkMap)};`,
92+
'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);',
93+
'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {',
94+
Template.indent([
95+
'promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {',
96+
Template.indent([
97+
`var href = ${linkHrefPath};`,
98+
`var fullhref = __webpack_require__.p + href;`,
99+
'var existingLinkTags = document.getElementsByTagName("link");',
100+
'for(var i = 0; i < existingLinkTags.length; i++) {',
101+
Template.indent([
102+
'var tag = existingLinkTags[i];',
103+
'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");',
104+
'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();',
105+
]),
106+
'}',
107+
'var existingStyleTags = document.getElementsByTagName("style");',
108+
'for(var i = 0; i < existingStyleTags.length; i++) {',
109+
Template.indent([
110+
'var tag = existingStyleTags[i];',
111+
'var dataHref = tag.getAttribute("data-href");',
112+
'if(dataHref === href || dataHref === fullhref) return resolve();',
113+
]),
114+
'}',
115+
'var linkTag = document.createElement("link");',
116+
'linkTag.rel = "stylesheet";',
117+
'linkTag.type = "text/css";',
118+
'linkTag.onload = resolve;',
119+
'linkTag.onerror = function(event) {',
120+
Template.indent([
121+
'var request = event && event.target && event.target.src || fullhref;',
122+
'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");',
123+
'err.code = "CSS_CHUNK_LOAD_FAILED";',
124+
'err.request = request;',
125+
'delete installedCssChunks[chunkId]',
126+
'linkTag.parentNode.removeChild(linkTag)',
127+
'reject(err);',
128+
]),
129+
'};',
130+
'linkTag.href = fullhref;',
131+
crossOriginLoading
132+
? Template.asString([
133+
`if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {`,
134+
Template.indent(
135+
`linkTag.crossOrigin = ${JSON.stringify(
136+
crossOriginLoading
137+
)};`
138+
),
139+
'}',
140+
])
141+
: '',
142+
'var head = document.getElementsByTagName("head")[0];',
143+
'head.appendChild(linkTag);',
144+
]),
145+
'}).then(function() {',
146+
Template.indent(['installedCssChunks[chunkId] = 0;']),
147+
'}));',
148+
]),
149+
'}',
150+
])
151+
)};`,
152+
])
153+
: '',
154+
]);
155+
}
156+
}
157+
158+
module.exports = CssChunkLoadingRuntimeModule;

0 commit comments

Comments
 (0)