UNPKG

10.3 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const Template = require("../Template");
8
9module.exports = class NodeMainTemplatePlugin {
10 constructor(asyncChunkLoading) {
11 this.asyncChunkLoading = asyncChunkLoading;
12 }
13
14 apply(mainTemplate) {
15 const needChunkOnDemandLoadingCode = chunk => {
16 for (const chunkGroup of chunk.groupsIterable) {
17 if (chunkGroup.getNumberOfChildren() > 0) return true;
18 }
19 return false;
20 };
21 const asyncChunkLoading = this.asyncChunkLoading;
22 mainTemplate.hooks.localVars.tap(
23 "NodeMainTemplatePlugin",
24 (source, chunk) => {
25 if (needChunkOnDemandLoadingCode(chunk)) {
26 return Template.asString([
27 source,
28 "",
29 "// object to store loaded chunks",
30 '// "0" means "already loaded"',
31 "var installedChunks = {",
32 Template.indent(
33 chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n")
34 ),
35 "};"
36 ]);
37 }
38 return source;
39 }
40 );
41 mainTemplate.hooks.requireExtensions.tap(
42 "NodeMainTemplatePlugin",
43 (source, chunk) => {
44 if (needChunkOnDemandLoadingCode(chunk)) {
45 return Template.asString([
46 source,
47 "",
48 "// uncaught error handler for webpack runtime",
49 `${mainTemplate.requireFn}.oe = function(err) {`,
50 Template.indent([
51 "process.nextTick(function() {",
52 Template.indent(
53 "throw err; // catch this error by using import().catch()"
54 ),
55 "});"
56 ]),
57 "};"
58 ]);
59 }
60 return source;
61 }
62 );
63 mainTemplate.hooks.requireEnsure.tap(
64 "NodeMainTemplatePlugin",
65 (source, chunk, hash) => {
66 const chunkFilename = mainTemplate.outputOptions.chunkFilename;
67 const chunkMaps = chunk.getChunkMaps();
68 const insertMoreModules = [
69 "var moreModules = chunk.modules, chunkIds = chunk.ids;",
70 "for(var moduleId in moreModules) {",
71 Template.indent(
72 mainTemplate.renderAddModule(
73 hash,
74 chunk,
75 "moduleId",
76 "moreModules[moduleId]"
77 )
78 ),
79 "}"
80 ];
81 if (asyncChunkLoading) {
82 return Template.asString([
83 source,
84 "",
85 "// ReadFile + VM.run chunk loading for javascript",
86 "",
87 "var installedChunkData = installedChunks[chunkId];",
88 'if(installedChunkData !== 0) { // 0 means "already installed".',
89 Template.indent([
90 '// array of [resolve, reject, promise] means "currently loading"',
91 "if(installedChunkData) {",
92 Template.indent(["promises.push(installedChunkData[2]);"]),
93 "} else {",
94 Template.indent([
95 "// load the chunk and return promise to it",
96 "var promise = new Promise(function(resolve, reject) {",
97 Template.indent([
98 "installedChunkData = installedChunks[chunkId] = [resolve, reject];",
99 "var filename = require('path').join(__dirname, " +
100 mainTemplate.getAssetPath(
101 JSON.stringify(`/${chunkFilename}`),
102 {
103 hash: `" + ${mainTemplate.renderCurrentHashCode(
104 hash
105 )} + "`,
106 hashWithLength: length =>
107 `" + ${mainTemplate.renderCurrentHashCode(
108 hash,
109 length
110 )} + "`,
111 chunk: {
112 id: '" + chunkId + "',
113 hash: `" + ${JSON.stringify(
114 chunkMaps.hash
115 )}[chunkId] + "`,
116 hashWithLength: length => {
117 const shortChunkHashMap = {};
118 for (const chunkId of Object.keys(chunkMaps.hash)) {
119 if (typeof chunkMaps.hash[chunkId] === "string") {
120 shortChunkHashMap[chunkId] = chunkMaps.hash[
121 chunkId
122 ].substr(0, length);
123 }
124 }
125 return `" + ${JSON.stringify(
126 shortChunkHashMap
127 )}[chunkId] + "`;
128 },
129 contentHash: {
130 javascript: `" + ${JSON.stringify(
131 chunkMaps.contentHash.javascript
132 )}[chunkId] + "`
133 },
134 contentHashWithLength: {
135 javascript: length => {
136 const shortContentHashMap = {};
137 const contentHash =
138 chunkMaps.contentHash.javascript;
139 for (const chunkId of Object.keys(contentHash)) {
140 if (typeof contentHash[chunkId] === "string") {
141 shortContentHashMap[chunkId] = contentHash[
142 chunkId
143 ].substr(0, length);
144 }
145 }
146 return `" + ${JSON.stringify(
147 shortContentHashMap
148 )}[chunkId] + "`;
149 }
150 },
151 name: `" + (${JSON.stringify(
152 chunkMaps.name
153 )}[chunkId]||chunkId) + "`
154 },
155 contentHashType: "javascript"
156 }
157 ) +
158 ");",
159 "require('fs').readFile(filename, 'utf-8', function(err, content) {",
160 Template.indent(
161 [
162 "if(err) return reject(err);",
163 "var chunk = {};",
164 "require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
165 "(chunk, require, require('path').dirname(filename), filename);"
166 ]
167 .concat(insertMoreModules)
168 .concat([
169 "var callbacks = [];",
170 "for(var i = 0; i < chunkIds.length; i++) {",
171 Template.indent([
172 "if(installedChunks[chunkIds[i]])",
173 Template.indent([
174 "callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);"
175 ]),
176 "installedChunks[chunkIds[i]] = 0;"
177 ]),
178 "}",
179 "for(i = 0; i < callbacks.length; i++)",
180 Template.indent("callbacks[i]();")
181 ])
182 ),
183 "});"
184 ]),
185 "});",
186 "promises.push(installedChunkData[2] = promise);"
187 ]),
188 "}"
189 ]),
190 "}"
191 ]);
192 } else {
193 const request = mainTemplate.getAssetPath(
194 JSON.stringify(`./${chunkFilename}`),
195 {
196 hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
197 hashWithLength: length =>
198 `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
199 chunk: {
200 id: '" + chunkId + "',
201 hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
202 hashWithLength: length => {
203 const shortChunkHashMap = {};
204 for (const chunkId of Object.keys(chunkMaps.hash)) {
205 if (typeof chunkMaps.hash[chunkId] === "string") {
206 shortChunkHashMap[chunkId] = chunkMaps.hash[
207 chunkId
208 ].substr(0, length);
209 }
210 }
211 return `" + ${JSON.stringify(
212 shortChunkHashMap
213 )}[chunkId] + "`;
214 },
215 contentHash: {
216 javascript: `" + ${JSON.stringify(
217 chunkMaps.contentHash.javascript
218 )}[chunkId] + "`
219 },
220 contentHashWithLength: {
221 javascript: length => {
222 const shortContentHashMap = {};
223 const contentHash = chunkMaps.contentHash.javascript;
224 for (const chunkId of Object.keys(contentHash)) {
225 if (typeof contentHash[chunkId] === "string") {
226 shortContentHashMap[chunkId] = contentHash[
227 chunkId
228 ].substr(0, length);
229 }
230 }
231 return `" + ${JSON.stringify(
232 shortContentHashMap
233 )}[chunkId] + "`;
234 }
235 },
236 name: `" + (${JSON.stringify(
237 chunkMaps.name
238 )}[chunkId]||chunkId) + "`
239 },
240 contentHashType: "javascript"
241 }
242 );
243 return Template.asString([
244 source,
245 "",
246 "// require() chunk loading for javascript",
247 "",
248 '// "0" is the signal for "already loaded"',
249 "if(installedChunks[chunkId] !== 0) {",
250 Template.indent(
251 [`var chunk = require(${request});`]
252 .concat(insertMoreModules)
253 .concat([
254 "for(var i = 0; i < chunkIds.length; i++)",
255 Template.indent("installedChunks[chunkIds[i]] = 0;")
256 ])
257 ),
258 "}"
259 ]);
260 }
261 }
262 );
263 mainTemplate.hooks.hotBootstrap.tap(
264 "NodeMainTemplatePlugin",
265 (source, chunk, hash) => {
266 const hotUpdateChunkFilename =
267 mainTemplate.outputOptions.hotUpdateChunkFilename;
268 const hotUpdateMainFilename =
269 mainTemplate.outputOptions.hotUpdateMainFilename;
270 const chunkMaps = chunk.getChunkMaps();
271 const currentHotUpdateChunkFilename = mainTemplate.getAssetPath(
272 JSON.stringify(hotUpdateChunkFilename),
273 {
274 hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
275 hashWithLength: length =>
276 `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
277 chunk: {
278 id: '" + chunkId + "',
279 hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
280 hashWithLength: length => {
281 const shortChunkHashMap = {};
282 for (const chunkId of Object.keys(chunkMaps.hash)) {
283 if (typeof chunkMaps.hash[chunkId] === "string") {
284 shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(
285 0,
286 length
287 );
288 }
289 }
290 return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
291 },
292 name: `" + (${JSON.stringify(
293 chunkMaps.name
294 )}[chunkId]||chunkId) + "`
295 }
296 }
297 );
298 const currentHotUpdateMainFilename = mainTemplate.getAssetPath(
299 JSON.stringify(hotUpdateMainFilename),
300 {
301 hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
302 hashWithLength: length =>
303 `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`
304 }
305 );
306 return Template.getFunctionContent(
307 asyncChunkLoading
308 ? require("./NodeMainTemplateAsync.runtime")
309 : require("./NodeMainTemplate.runtime")
310 )
311 .replace(/\$require\$/g, mainTemplate.requireFn)
312 .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename)
313 .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename);
314 }
315 );
316 mainTemplate.hooks.hash.tap("NodeMainTemplatePlugin", hash => {
317 hash.update("node");
318 hash.update("4");
319 });
320 }
321};