|
2 | 2 | MIT License http://www.opensource.org/licenses/mit-license.php |
3 | 3 | Author Tobias Koppers @sokra |
4 | 4 | */ |
5 | | -var Template = require("./Template"); |
| 5 | +"use strict"; |
6 | 6 |
|
7 | | -function JsonpMainTemplatePlugin() {} |
8 | | -module.exports = JsonpMainTemplatePlugin; |
| 7 | +const Template = require("./Template"); |
| 8 | + |
| 9 | +class JsonpMainTemplatePlugin { |
9 | 10 |
|
10 | | -JsonpMainTemplatePlugin.prototype.constructor = JsonpMainTemplatePlugin; |
11 | | -JsonpMainTemplatePlugin.prototype.apply = function(mainTemplate) { |
12 | | - mainTemplate.plugin("local-vars", function(source, chunk) { |
13 | | - if(chunk.chunks.length > 0) { |
| 11 | + apply(mainTemplate) { |
| 12 | + mainTemplate.plugin("local-vars", function(source, chunk) { |
| 13 | + if(chunk.chunks.length > 0) { |
| 14 | + return this.asString([ |
| 15 | + source, |
| 16 | + "", |
| 17 | + "// objects to store loaded and loading chunks", |
| 18 | + "var installedChunks = {", |
| 19 | + this.indent( |
| 20 | + chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n") |
| 21 | + ), |
| 22 | + "};" |
| 23 | + ]); |
| 24 | + } |
| 25 | + return source; |
| 26 | + }); |
| 27 | + mainTemplate.plugin("jsonp-script", function(_, chunk, hash) { |
| 28 | + const chunkFilename = this.outputOptions.chunkFilename; |
| 29 | + const chunkMaps = chunk.getChunkMaps(); |
| 30 | + const crossOriginLoading = this.outputOptions.crossOriginLoading; |
| 31 | + const chunkLoadTimeout = this.outputOptions.chunkLoadTimeout || 120000; |
14 | 32 | return this.asString([ |
15 | | - source, |
16 | | - "", |
17 | | - "// objects to store loaded and loading chunks", |
18 | | - "var installedChunks = {", |
19 | | - this.indent( |
20 | | - chunk.ids.map(function(id) { |
21 | | - return JSON.stringify(id) + ": 0"; |
22 | | - }).join(",\n") |
23 | | - ), |
24 | | - "};" |
| 33 | + "var script = document.createElement('script');", |
| 34 | + "script.type = 'text/javascript';", |
| 35 | + "script.charset = 'utf-8';", |
| 36 | + "script.async = true;", |
| 37 | + `script.timeout = ${chunkLoadTimeout};`, |
| 38 | + crossOriginLoading ? `script.crossOrigin = '${crossOriginLoading}';` : "", |
| 39 | + `if (${this.requireFn}.nc) {`, |
| 40 | + this.indent(`script.setAttribute("nonce", ${this.requireFn}.nc);`), |
| 41 | + "}", |
| 42 | + `script.src = ${this.requireFn}.p + ${this.applyPluginsWaterfall("asset-path", JSON.stringify(chunkFilename), { |
| 43 | + hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", |
| 44 | + hashWithLength: length => "\" + " + this.renderCurrentHashCode(hash, length) + " + \"", |
| 45 | + chunk: { |
| 46 | + id: "\" + chunkId + \"", |
| 47 | + hash: "\" + " + JSON.stringify(chunkMaps.hash) + "[chunkId] + \"", |
| 48 | + hashWithLength(length) { |
| 49 | + const shortChunkHashMap = {}; |
| 50 | + Object.keys(chunkMaps.hash).forEach(chunkId => { |
| 51 | + if(typeof chunkMaps.hash[chunkId] === "string") |
| 52 | + shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length); |
| 53 | + }); |
| 54 | + return "\" + " + JSON.stringify(shortChunkHashMap) + "[chunkId] + \""; |
| 55 | + }, |
| 56 | + name: "\" + (" + JSON.stringify(chunkMaps.name) + "[chunkId]||chunkId) + \"" |
| 57 | + } |
| 58 | + })};`, |
| 59 | + "var timeout = setTimeout(onScriptComplete, " + chunkLoadTimeout + ");", |
| 60 | + "script.onerror = script.onload = onScriptComplete;", |
| 61 | + "function onScriptComplete() {", |
| 62 | + this.indent([ |
| 63 | + "// avoid mem leaks in IE.", |
| 64 | + "script.onerror = script.onload = null;", |
| 65 | + "clearTimeout(timeout);", |
| 66 | + "var chunk = installedChunks[chunkId];", |
| 67 | + "if(chunk !== 0) {", |
| 68 | + this.indent([ |
| 69 | + "if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));", |
| 70 | + "installedChunks[chunkId] = undefined;" |
| 71 | + ]), |
| 72 | + "}" |
| 73 | + ]), |
| 74 | + "};", |
25 | 75 | ]); |
26 | | - } |
27 | | - return source; |
28 | | - }); |
29 | | - mainTemplate.plugin("jsonp-script", function(_, chunk, hash) { |
30 | | - var chunkFilename = this.outputOptions.chunkFilename; |
31 | | - var chunkMaps = chunk.getChunkMaps(); |
32 | | - var crossOriginLoading = this.outputOptions.crossOriginLoading; |
33 | | - var chunkLoadTimeout = this.outputOptions.chunkLoadTimeout || 120000; |
34 | | - return this.asString([ |
35 | | - "var script = document.createElement('script');", |
36 | | - "script.type = 'text/javascript';", |
37 | | - "script.charset = 'utf-8';", |
38 | | - "script.async = true;", |
39 | | - "script.timeout = " + chunkLoadTimeout + ";", |
40 | | - crossOriginLoading ? "script.crossOrigin = '" + crossOriginLoading + "';" : "", |
41 | | - "if (" + this.requireFn + ".nc) {", |
42 | | - this.indent("script.setAttribute(\"nonce\", " + this.requireFn + ".nc);"), |
43 | | - "}", |
44 | | - "script.src = " + this.requireFn + ".p + " + |
45 | | - this.applyPluginsWaterfall("asset-path", JSON.stringify(chunkFilename), { |
46 | | - hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", |
47 | | - hashWithLength: function(length) { |
48 | | - return "\" + " + this.renderCurrentHashCode(hash, length) + " + \""; |
49 | | - }.bind(this), |
50 | | - chunk: { |
51 | | - id: "\" + chunkId + \"", |
52 | | - hash: "\" + " + JSON.stringify(chunkMaps.hash) + "[chunkId] + \"", |
53 | | - hashWithLength: function(length) { |
54 | | - var shortChunkHashMap = {}; |
55 | | - Object.keys(chunkMaps.hash).forEach(function(chunkId) { |
56 | | - if(typeof chunkMaps.hash[chunkId] === "string") |
57 | | - shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length); |
58 | | - }); |
59 | | - return "\" + " + JSON.stringify(shortChunkHashMap) + "[chunkId] + \""; |
60 | | - }, |
61 | | - name: "\" + (" + JSON.stringify(chunkMaps.name) + "[chunkId]||chunkId) + \"" |
62 | | - } |
63 | | - }) + ";", |
64 | | - "var timeout = setTimeout(onScriptComplete, " + chunkLoadTimeout + ");", |
65 | | - "script.onerror = script.onload = onScriptComplete;", |
66 | | - "function onScriptComplete() {", |
67 | | - this.indent([ |
68 | | - "// avoid mem leaks in IE.", |
69 | | - "script.onerror = script.onload = null;", |
70 | | - "clearTimeout(timeout);", |
71 | | - "var chunk = installedChunks[chunkId];", |
72 | | - "if(chunk !== 0) {", |
| 76 | + }); |
| 77 | + mainTemplate.plugin("require-ensure", function(_, chunk, hash) { |
| 78 | + return this.asString([ |
| 79 | + "if(installedChunks[chunkId] === 0)", |
| 80 | + this.indent([ |
| 81 | + "return Promise.resolve();" |
| 82 | + ]), |
| 83 | + "", |
| 84 | + "// a Promise means \"currently loading\".", |
| 85 | + "if(installedChunks[chunkId]) {", |
73 | 86 | this.indent([ |
74 | | - "if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));", |
75 | | - "installedChunks[chunkId] = undefined;" |
| 87 | + "return installedChunks[chunkId][2];" |
76 | 88 | ]), |
77 | | - "}" |
78 | | - ]), |
79 | | - "};", |
80 | | - ]); |
81 | | - }); |
82 | | - mainTemplate.plugin("require-ensure", function(_, chunk, hash) { |
83 | | - return this.asString([ |
84 | | - "if(installedChunks[chunkId] === 0)", |
85 | | - this.indent([ |
86 | | - "return Promise.resolve();" |
87 | | - ]), |
88 | | - "", |
89 | | - "// a Promise means \"currently loading\".", |
90 | | - "if(installedChunks[chunkId]) {", |
91 | | - this.indent([ |
92 | | - "return installedChunks[chunkId][2];" |
93 | | - ]), |
94 | | - "}", |
95 | | - "", |
96 | | - "// setup Promise in chunk cache", |
97 | | - "var promise = new Promise(function(resolve, reject) {", |
98 | | - this.indent([ |
99 | | - "installedChunks[chunkId] = [resolve, reject];" |
100 | | - ]), |
101 | | - "});", |
102 | | - "installedChunks[chunkId][2] = promise;", |
103 | | - "", |
104 | | - "// start chunk loading", |
105 | | - "var head = document.getElementsByTagName('head')[0];", |
106 | | - this.applyPluginsWaterfall("jsonp-script", "", chunk, hash), |
107 | | - "head.appendChild(script);", |
108 | | - "", |
109 | | - "return promise;" |
110 | | - ]); |
111 | | - }); |
112 | | - mainTemplate.plugin("require-extensions", function(source, chunk) { |
113 | | - if(chunk.chunks.length === 0) return source; |
114 | | - return this.asString([ |
115 | | - source, |
116 | | - "", |
117 | | - "// on error function for async loading", |
118 | | - this.requireFn + ".oe = function(err) { console.error(err); throw err; };" |
119 | | - ]); |
120 | | - }); |
121 | | - mainTemplate.plugin("bootstrap", function(source, chunk, hash) { |
122 | | - if(chunk.chunks.length > 0) { |
123 | | - var jsonpFunction = this.outputOptions.jsonpFunction; |
| 89 | + "}", |
| 90 | + "", |
| 91 | + "// setup Promise in chunk cache", |
| 92 | + "var promise = new Promise(function(resolve, reject) {", |
| 93 | + this.indent([ |
| 94 | + "installedChunks[chunkId] = [resolve, reject];" |
| 95 | + ]), |
| 96 | + "});", |
| 97 | + "installedChunks[chunkId][2] = promise;", |
| 98 | + "", |
| 99 | + "// start chunk loading", |
| 100 | + "var head = document.getElementsByTagName('head')[0];", |
| 101 | + this.applyPluginsWaterfall("jsonp-script", "", chunk, hash), |
| 102 | + "head.appendChild(script);", |
| 103 | + "", |
| 104 | + "return promise;" |
| 105 | + ]); |
| 106 | + }); |
| 107 | + mainTemplate.plugin("require-extensions", function(source, chunk) { |
| 108 | + if(chunk.chunks.length === 0) return source; |
| 109 | + |
124 | 110 | return this.asString([ |
125 | 111 | source, |
126 | 112 | "", |
127 | | - "// install a JSONP callback for chunk loading", |
128 | | - "var parentJsonpFunction = window[" + JSON.stringify(jsonpFunction) + "];", |
129 | | - "window[" + JSON.stringify(jsonpFunction) + "] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {", |
130 | | - this.indent([ |
131 | | - "// add \"moreModules\" to the modules object,", |
132 | | - "// then flag all \"chunkIds\" as loaded and fire callback", |
133 | | - "var moduleId, chunkId, i = 0, resolves = [], result;", |
134 | | - "for(;i < chunkIds.length; i++) {", |
135 | | - this.indent([ |
136 | | - "chunkId = chunkIds[i];", |
137 | | - "if(installedChunks[chunkId])", |
138 | | - this.indent("resolves.push(installedChunks[chunkId][0]);"), |
139 | | - "installedChunks[chunkId] = 0;" |
140 | | - ]), |
141 | | - "}", |
142 | | - "for(moduleId in moreModules) {", |
| 113 | + "// on error function for async loading", |
| 114 | + this.requireFn + ".oe = function(err) { console.error(err); throw err; };" |
| 115 | + ]); |
| 116 | + }); |
| 117 | + mainTemplate.plugin("bootstrap", function(source, chunk, hash) { |
| 118 | + if(chunk.chunks.length > 0) { |
| 119 | + var jsonpFunction = this.outputOptions.jsonpFunction; |
| 120 | + return this.asString([ |
| 121 | + source, |
| 122 | + "", |
| 123 | + "// install a JSONP callback for chunk loading", |
| 124 | + "var parentJsonpFunction = window[" + JSON.stringify(jsonpFunction) + "];", |
| 125 | + "window[" + JSON.stringify(jsonpFunction) + "] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {", |
143 | 126 | this.indent([ |
144 | | - "if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {", |
145 | | - this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), |
146 | | - "}" |
147 | | - ]), |
148 | | - "}", |
149 | | - "if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);", |
150 | | - "while(resolves.length)", |
151 | | - this.indent("resolves.shift()();"), |
152 | | - this.entryPointInChildren(chunk) ? [ |
153 | | - "if(executeModules) {", |
| 127 | + "// add \"moreModules\" to the modules object,", |
| 128 | + "// then flag all \"chunkIds\" as loaded and fire callback", |
| 129 | + "var moduleId, chunkId, i = 0, resolves = [], result;", |
| 130 | + "for(;i < chunkIds.length; i++) {", |
154 | 131 | this.indent([ |
155 | | - "for(i=0; i < executeModules.length; i++) {", |
156 | | - this.indent("result = " + this.requireFn + "(" + this.requireFn + ".s = executeModules[i]);"), |
| 132 | + "chunkId = chunkIds[i];", |
| 133 | + "if(installedChunks[chunkId])", |
| 134 | + this.indent("resolves.push(installedChunks[chunkId][0]);"), |
| 135 | + "installedChunks[chunkId] = 0;" |
| 136 | + ]), |
| 137 | + "}", |
| 138 | + "for(moduleId in moreModules) {", |
| 139 | + this.indent([ |
| 140 | + "if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {", |
| 141 | + this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), |
157 | 142 | "}" |
158 | 143 | ]), |
159 | 144 | "}", |
160 | | - "return result;", |
161 | | - ] : "" |
162 | | - ]), |
163 | | - "};" |
164 | | - ]); |
165 | | - } |
166 | | - return source; |
167 | | - }); |
168 | | - mainTemplate.plugin("hot-bootstrap", function(source, chunk, hash) { |
169 | | - var hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename; |
170 | | - var hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename; |
171 | | - var hotUpdateFunction = this.outputOptions.hotUpdateFunction; |
172 | | - var currentHotUpdateChunkFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateChunkFilename), { |
173 | | - hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", |
174 | | - hashWithLength: function(length) { |
175 | | - return "\" + " + this.renderCurrentHashCode(hash, length) + " + \""; |
176 | | - }.bind(this), |
177 | | - chunk: { |
178 | | - id: "\" + chunkId + \"" |
| 145 | + "if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);", |
| 146 | + "while(resolves.length)", |
| 147 | + this.indent("resolves.shift()();"), |
| 148 | + this.entryPointInChildren(chunk) ? [ |
| 149 | + "if(executeModules) {", |
| 150 | + this.indent([ |
| 151 | + "for(i=0; i < executeModules.length; i++) {", |
| 152 | + this.indent("result = " + this.requireFn + "(" + this.requireFn + ".s = executeModules[i]);"), |
| 153 | + "}" |
| 154 | + ]), |
| 155 | + "}", |
| 156 | + "return result;", |
| 157 | + ] : "" |
| 158 | + ]), |
| 159 | + "};" |
| 160 | + ]); |
179 | 161 | } |
| 162 | + return source; |
180 | 163 | }); |
181 | | - var currentHotUpdateMainFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateMainFilename), { |
182 | | - hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", |
183 | | - hashWithLength: function(length) { |
184 | | - return "\" + " + this.renderCurrentHashCode(hash, length) + " + \""; |
185 | | - }.bind(this) |
| 164 | + mainTemplate.plugin("hot-bootstrap", function(source, chunk, hash) { |
| 165 | + const hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename; |
| 166 | + const hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename; |
| 167 | + const hotUpdateFunction = this.outputOptions.hotUpdateFunction; |
| 168 | + const currentHotUpdateChunkFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateChunkFilename), { |
| 169 | + hash: `" + ${this.renderCurrentHashCode(hash)} + "`, |
| 170 | + hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`, |
| 171 | + chunk: { |
| 172 | + id: "\" + chunkId + \"" |
| 173 | + } |
| 174 | + }); |
| 175 | + const currentHotUpdateMainFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateMainFilename), { |
| 176 | + hash: `" + ${this.renderCurrentHashCode(hash)} + "`, |
| 177 | + hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "` |
| 178 | + }); |
| 179 | + const runtimeCode = Template.getFunctionContent(require("./JsonpMainTemplate.runtime.js")) |
| 180 | + .replace(/\/\/\$semicolon/g, ";") |
| 181 | + .replace(/\$require\$/g, this.requireFn) |
| 182 | + .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename) |
| 183 | + .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename) |
| 184 | + .replace(/\$hash\$/g, JSON.stringify(hash)); |
| 185 | + return `${source} |
| 186 | +function hotDisposeChunk(chunkId) { |
| 187 | + delete installedChunks[chunkId]; |
| 188 | +} |
| 189 | +var parentHotUpdateCallback = this[${JSON.stringify(hotUpdateFunction)}]; |
| 190 | +this[${JSON.stringify(hotUpdateFunction)}] = ${runtimeCode}`; |
186 | 191 | }); |
187 | | - |
188 | | - return source + "\n" + |
189 | | - "function hotDisposeChunk(chunkId) {\n" + |
190 | | - "\tdelete installedChunks[chunkId];\n" + |
191 | | - "}\n" + |
192 | | - "var parentHotUpdateCallback = this[" + JSON.stringify(hotUpdateFunction) + "];\n" + |
193 | | - "this[" + JSON.stringify(hotUpdateFunction) + "] = " + Template.getFunctionContent(require("./JsonpMainTemplate.runtime.js")) |
194 | | - .replace(/\/\/\$semicolon/g, ";") |
195 | | - .replace(/\$require\$/g, this.requireFn) |
196 | | - .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename) |
197 | | - .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename) |
198 | | - .replace(/\$hash\$/g, JSON.stringify(hash)); |
199 | | - }); |
200 | | - mainTemplate.plugin("hash", function(hash) { |
201 | | - hash.update("jsonp"); |
202 | | - hash.update("4"); |
203 | | - hash.update(this.outputOptions.filename + ""); |
204 | | - hash.update(this.outputOptions.chunkFilename + ""); |
205 | | - hash.update(this.outputOptions.jsonpFunction + ""); |
206 | | - hash.update(this.outputOptions.hotUpdateFunction + ""); |
207 | | - }); |
208 | | -}; |
| 192 | + mainTemplate.plugin("hash", function(hash) { |
| 193 | + hash.update("jsonp"); |
| 194 | + hash.update("4"); |
| 195 | + hash.update(`${this.outputOptions.filename}`); |
| 196 | + hash.update(`${this.outputOptions.chunkFilename}`); |
| 197 | + hash.update(`${this.outputOptions.jsonpFunction}`); |
| 198 | + hash.update(`${this.outputOptions.hotUpdateFunction}`); |
| 199 | + }); |
| 200 | + } |
| 201 | +} |
| 202 | +module.exports = JsonpMainTemplatePlugin; |
0 commit comments