1 |
|
2 |
|
3 |
|
4 |
|
5 | "use strict";
|
6 |
|
7 | const RuntimeGlobals = require("../RuntimeGlobals");
|
8 | const RuntimeModule = require("../RuntimeModule");
|
9 | const Template = require("../Template");
|
10 | const {
|
11 | chunkHasJs,
|
12 | getChunkFilenameTemplate
|
13 | } = require("../javascript/JavascriptModulesPlugin");
|
14 | const { getInitialChunkIds } = require("../javascript/StartupHelpers");
|
15 | const compileBooleanMatcher = require("../util/compileBooleanMatcher");
|
16 | const { getUndoPath } = require("../util/identifier");
|
17 |
|
18 |
|
19 |
|
20 | class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
|
21 | constructor(runtimeRequirements) {
|
22 | super("readFile chunk loading", RuntimeModule.STAGE_ATTACH);
|
23 | this.runtimeRequirements = runtimeRequirements;
|
24 | }
|
25 |
|
26 | |
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | _generateBaseUri(chunk, rootOutputDir) {
|
33 | const options = chunk.getEntryOptions();
|
34 | if (options && options.baseUri) {
|
35 | return `${RuntimeGlobals.baseURI} = ${JSON.stringify(options.baseUri)};`;
|
36 | }
|
37 |
|
38 | return `${RuntimeGlobals.baseURI} = require("url").pathToFileURL(${
|
39 | rootOutputDir
|
40 | ? `__dirname + ${JSON.stringify("/" + rootOutputDir)}`
|
41 | : "__filename"
|
42 | });`;
|
43 | }
|
44 |
|
45 | |
46 |
|
47 |
|
48 | generate() {
|
49 | const { chunkGraph, chunk } = this;
|
50 | const { runtimeTemplate } = this.compilation;
|
51 | const fn = RuntimeGlobals.ensureChunkHandlers;
|
52 | const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
|
53 | const withExternalInstallChunk = this.runtimeRequirements.has(
|
54 | RuntimeGlobals.externalInstallChunk
|
55 | );
|
56 | const withOnChunkLoad = this.runtimeRequirements.has(
|
57 | RuntimeGlobals.onChunksLoaded
|
58 | );
|
59 | const withLoading = this.runtimeRequirements.has(
|
60 | RuntimeGlobals.ensureChunkHandlers
|
61 | );
|
62 | const withHmr = this.runtimeRequirements.has(
|
63 | RuntimeGlobals.hmrDownloadUpdateHandlers
|
64 | );
|
65 | const withHmrManifest = this.runtimeRequirements.has(
|
66 | RuntimeGlobals.hmrDownloadManifest
|
67 | );
|
68 | const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
|
69 | const hasJsMatcher = compileBooleanMatcher(conditionMap);
|
70 | const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
|
71 |
|
72 | const outputName = this.compilation.getPath(
|
73 | getChunkFilenameTemplate(chunk, this.compilation.outputOptions),
|
74 | {
|
75 | chunk,
|
76 | contentHashType: "javascript"
|
77 | }
|
78 | );
|
79 | const rootOutputDir = getUndoPath(
|
80 | outputName,
|
81 | this.compilation.outputOptions.path,
|
82 | false
|
83 | );
|
84 |
|
85 | const stateExpression = withHmr
|
86 | ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_readFileVm`
|
87 | : undefined;
|
88 |
|
89 | return Template.asString([
|
90 | withBaseURI
|
91 | ? this._generateBaseUri(chunk, rootOutputDir)
|
92 | : "// no baseURI",
|
93 | "",
|
94 | "// object to store loaded chunks",
|
95 | '// "0" means "already loaded", Promise means loading',
|
96 | `var installedChunks = ${
|
97 | stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""
|
98 | }{`,
|
99 | Template.indent(
|
100 | Array.from(initialChunkIds, id => `${JSON.stringify(id)}: 0`).join(
|
101 | ",\n"
|
102 | )
|
103 | ),
|
104 | "};",
|
105 | "",
|
106 | withOnChunkLoad
|
107 | ? `${
|
108 | RuntimeGlobals.onChunksLoaded
|
109 | }.readFileVm = ${runtimeTemplate.returningFunction(
|
110 | "installedChunks[chunkId] === 0",
|
111 | "chunkId"
|
112 | )};`
|
113 | : "// no on chunks loaded",
|
114 | "",
|
115 | withLoading || withExternalInstallChunk
|
116 | ? `var installChunk = ${runtimeTemplate.basicFunction("chunk", [
|
117 | "var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;",
|
118 | "for(var moduleId in moreModules) {",
|
119 | Template.indent([
|
120 | `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
|
121 | Template.indent([
|
122 | `${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`
|
123 | ]),
|
124 | "}"
|
125 | ]),
|
126 | "}",
|
127 | `if(runtime) runtime(__webpack_require__);`,
|
128 | "for(var i = 0; i < chunkIds.length; i++) {",
|
129 | Template.indent([
|
130 | "if(installedChunks[chunkIds[i]]) {",
|
131 | Template.indent(["installedChunks[chunkIds[i]][0]();"]),
|
132 | "}",
|
133 | "installedChunks[chunkIds[i]] = 0;"
|
134 | ]),
|
135 | "}",
|
136 | withOnChunkLoad ? `${RuntimeGlobals.onChunksLoaded}();` : ""
|
137 | ])};`
|
138 | : "// no chunk install function needed",
|
139 | "",
|
140 | withLoading
|
141 | ? Template.asString([
|
142 | "// ReadFile + VM.run chunk loading for javascript",
|
143 | `${fn}.readFileVm = function(chunkId, promises) {`,
|
144 | hasJsMatcher !== false
|
145 | ? Template.indent([
|
146 | "",
|
147 | "var installedChunkData = installedChunks[chunkId];",
|
148 | 'if(installedChunkData !== 0) { // 0 means "already installed".',
|
149 | Template.indent([
|
150 | '// array of [resolve, reject, promise] means "currently loading"',
|
151 | "if(installedChunkData) {",
|
152 | Template.indent(["promises.push(installedChunkData[2]);"]),
|
153 | "} else {",
|
154 | Template.indent([
|
155 | hasJsMatcher === true
|
156 | ? "if(true) { // all chunks have JS"
|
157 | : `if(${hasJsMatcher("chunkId")}) {`,
|
158 | Template.indent([
|
159 | "// load the chunk and return promise to it",
|
160 | "var promise = new Promise(function(resolve, reject) {",
|
161 | Template.indent([
|
162 | "installedChunkData = installedChunks[chunkId] = [resolve, reject];",
|
163 | `var filename = require('path').join(__dirname, ${JSON.stringify(
|
164 | rootOutputDir
|
165 | )} + ${
|
166 | RuntimeGlobals.getChunkScriptFilename
|
167 | }(chunkId));`,
|
168 | "require('fs').readFile(filename, 'utf-8', function(err, content) {",
|
169 | Template.indent([
|
170 | "if(err) return reject(err);",
|
171 | "var chunk = {};",
|
172 | "require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
|
173 | "(chunk, require, require('path').dirname(filename), filename);",
|
174 | "installChunk(chunk);"
|
175 | ]),
|
176 | "});"
|
177 | ]),
|
178 | "});",
|
179 | "promises.push(installedChunkData[2] = promise);"
|
180 | ]),
|
181 | "} else installedChunks[chunkId] = 0;"
|
182 | ]),
|
183 | "}"
|
184 | ]),
|
185 | "}"
|
186 | ])
|
187 | : Template.indent(["installedChunks[chunkId] = 0;"]),
|
188 | "};"
|
189 | ])
|
190 | : "// no chunk loading",
|
191 | "",
|
192 | withExternalInstallChunk
|
193 | ? Template.asString([
|
194 | "module.exports = __webpack_require__;",
|
195 | `${RuntimeGlobals.externalInstallChunk} = installChunk;`
|
196 | ])
|
197 | : "// no external install chunk",
|
198 | "",
|
199 | withHmr
|
200 | ? Template.asString([
|
201 | "function loadUpdateChunk(chunkId, updatedModulesList) {",
|
202 | Template.indent([
|
203 | "return new Promise(function(resolve, reject) {",
|
204 | Template.indent([
|
205 | `var filename = require('path').join(__dirname, ${JSON.stringify(
|
206 | rootOutputDir
|
207 | )} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId));`,
|
208 | "require('fs').readFile(filename, 'utf-8', function(err, content) {",
|
209 | Template.indent([
|
210 | "if(err) return reject(err);",
|
211 | "var update = {};",
|
212 | "require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
|
213 | "(update, require, require('path').dirname(filename), filename);",
|
214 | "var updatedModules = update.modules;",
|
215 | "var runtime = update.runtime;",
|
216 | "for(var moduleId in updatedModules) {",
|
217 | Template.indent([
|
218 | `if(${RuntimeGlobals.hasOwnProperty}(updatedModules, moduleId)) {`,
|
219 | Template.indent([
|
220 | `currentUpdate[moduleId] = updatedModules[moduleId];`,
|
221 | "if(updatedModulesList) updatedModulesList.push(moduleId);"
|
222 | ]),
|
223 | "}"
|
224 | ]),
|
225 | "}",
|
226 | "if(runtime) currentUpdateRuntime.push(runtime);",
|
227 | "resolve();"
|
228 | ]),
|
229 | "});"
|
230 | ]),
|
231 | "});"
|
232 | ]),
|
233 | "}",
|
234 | "",
|
235 | Template.getFunctionContent(
|
236 | require("../hmr/JavascriptHotModuleReplacement.runtime.js")
|
237 | )
|
238 | .replace(/\$key\$/g, "readFileVm")
|
239 | .replace(/\$installedChunks\$/g, "installedChunks")
|
240 | .replace(/\$loadUpdateChunk\$/g, "loadUpdateChunk")
|
241 | .replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
|
242 | .replace(/\$moduleFactories\$/g, RuntimeGlobals.moduleFactories)
|
243 | .replace(
|
244 | /\$ensureChunkHandlers\$/g,
|
245 | RuntimeGlobals.ensureChunkHandlers
|
246 | )
|
247 | .replace(/\$hasOwnProperty\$/g, RuntimeGlobals.hasOwnProperty)
|
248 | .replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
|
249 | .replace(
|
250 | /\$hmrDownloadUpdateHandlers\$/g,
|
251 | RuntimeGlobals.hmrDownloadUpdateHandlers
|
252 | )
|
253 | .replace(
|
254 | /\$hmrInvalidateModuleHandlers\$/g,
|
255 | RuntimeGlobals.hmrInvalidateModuleHandlers
|
256 | )
|
257 | ])
|
258 | : "// no HMR",
|
259 | "",
|
260 | withHmrManifest
|
261 | ? Template.asString([
|
262 | `${RuntimeGlobals.hmrDownloadManifest} = function() {`,
|
263 | Template.indent([
|
264 | "return new Promise(function(resolve, reject) {",
|
265 | Template.indent([
|
266 | `var filename = require('path').join(__dirname, ${JSON.stringify(
|
267 | rootOutputDir
|
268 | )} + ${RuntimeGlobals.getUpdateManifestFilename}());`,
|
269 | "require('fs').readFile(filename, 'utf-8', function(err, content) {",
|
270 | Template.indent([
|
271 | "if(err) {",
|
272 | Template.indent([
|
273 | 'if(err.code === "ENOENT") return resolve();',
|
274 | "return reject(err);"
|
275 | ]),
|
276 | "}",
|
277 | "try { resolve(JSON.parse(content)); }",
|
278 | "catch(e) { reject(e); }"
|
279 | ]),
|
280 | "});"
|
281 | ]),
|
282 | "});"
|
283 | ]),
|
284 | "}"
|
285 | ])
|
286 | : "// no HMR manifest"
|
287 | ]);
|
288 | }
|
289 | }
|
290 |
|
291 | module.exports = ReadFileChunkLoadingRuntimeModule;
|