new Worker() support

add support for async anonymous entrypoints from inside the codebase
add worker example
move entry options into Entrypoint and receive them from there
This commit is contained in:
Tobias Koppers 2020-09-07 18:02:14 +02:00
parent 3f12b0fa27
commit ed06a7f83a
64 changed files with 2124 additions and 166 deletions

View File

@ -179,6 +179,7 @@
"analysing",
"etags",
"destructure",
"onconnect",
"webassemblyjs",
"fsevents",

View File

@ -1759,6 +1759,10 @@ export interface Output {
* The filename of WebAssembly modules as relative path inside the `output.path` directory.
*/
webassemblyModuleFilename?: WebassemblyModuleFilename;
/**
* The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).
*/
workerChunkLoading?: ChunkLoading;
}
/**
* Configuration object for web performance recommendations.
@ -2361,6 +2365,10 @@ export interface OutputNormalized {
* The filename of WebAssembly modules as relative path inside the `output.path` directory.
*/
webassemblyModuleFilename?: WebassemblyModuleFilename;
/**
* The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).
*/
workerChunkLoading?: ChunkLoading;
}
/**
* Normalized webpack options object.

View File

@ -14,9 +14,7 @@ function lessStrict(regExpStr) {
}
const runtimeModulesRegexp = /(\/\*{72}\/\n(?:\/(?:\*{6}|\*{72})\/.*\n)*\/\*{72}\/\n)/g;
const timeRegexp = /\s*Time: \d+ ms/g;
const buildAtRegexp = /\s*Built at: .+/mg;
const hashRegexp = /Hash: [a-f0-9]+/g;
const timeRegexp = / in \d+ ms/g;
const dataUrlRegexp = /("data:[^"]+")/g;
exports.replaceBase = (template) => {
@ -40,8 +38,6 @@ exports.replaceBase = (template) => {
.replace(webpack, "(webpack)")
.replace(webpackParent, "(webpack)/~")
.replace(timeRegexp, "")
.replace(buildAtRegexp, "")
.replace(hashRegexp, "Hash: 0a1b2c3d4e5f6a7b8c9d")
.replace(dataUrlRegexp, function(match) {
if(match.length < 100) return match;
return match.slice(0, 50) + "..." + match.slice(-10);

809
examples/worker/README.md Normal file
View File

@ -0,0 +1,809 @@
# example.js
```javascript
document.body.innerHTML = `
<pre id="history"></pre>
<form>
<input id="message" type="text">
<button id="send">Send Message</button>
</form>
<p>Computing fibonacci without worker:</p>
<input id="fib1" type="number">
<pre id="output1"></pre>
<p>Computing fibonacci with worker:</p>
<input id="fib2" type="number">
<pre id="output2"></pre>
`;
const history = document.getElementById("history");
const message = document.getElementById("message");
const send = document.getElementById("send");
const fib1 = document.getElementById("fib1");
const output1 = document.getElementById("output1");
const fib2 = document.getElementById("fib2");
const output2 = document.getElementById("output2");
/// CHAT with shared worker ///
const chatWorker = new SharedWorker(
new URL("./chat-worker.js", import.meta.url),
{
name: "chat"
}
);
let historyTimeout;
const scheduleUpdateHistory = () => {
clearTimeout(historyTimeout);
historyTimeout = setTimeout(() => {
chatWorker.port.postMessage({ type: "history" });
}, 1000);
};
scheduleUpdateHistory();
const from = `User ${Math.floor(Math.random() * 10000)}`;
send.addEventListener("click", e => {
chatWorker.port.postMessage({
type: "message",
content: message.value,
from
});
message.value = "";
message.focus();
e.preventDefault();
});
chatWorker.port.onmessage = event => {
const msg = event.data;
switch (msg.type) {
case "history":
history.innerText = msg.history.join("\n");
scheduleUpdateHistory();
break;
}
};
/// FIBONACCI without worker ///
fib1.addEventListener("change", async () => {
try {
const value = parseInt(fib1.value, 10);
const { fibonacci } = await import("./fibonacci");
const result = fibonacci(value);
output1.innerText = `fib(${value}) = ${result}`;
} catch (e) {
output1.innerText = e.message;
}
});
/// FIBONACCI with worker ///
const fibWorker = new Worker(new URL("./fib-worker.js", import.meta.url), {
name: "fibonacci"
/* webpackEntryOptions: { filename: "workers/[name].js" } */
});
fib2.addEventListener("change", () => {
try {
const value = parseInt(fib2.value, 10);
fibWorker.postMessage(`${value}`);
} catch (e) {
output2.innerText = e.message;
}
});
fibWorker.onmessage = event => {
output2.innerText = event.data;
};
```
# fib-worker.js
```javascript
onmessage = async event => {
const { fibonacci } = await import("./fibonacci");
const value = JSON.parse(event.data);
postMessage(`fib(${value}) = ${fibonacci(value)}`);
};
```
# fibonacci.js
```javascript
export function fibonacci(n) {
return n < 1 ? 0 : n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2);
}
```
# chat-worker.js
```javascript
import { history, add } from "./chat-module";
onconnect = function (e) {
for (const port of e.ports) {
port.onmessage = event => {
const msg = event.data;
switch (msg.type) {
case "message":
add(msg.content, msg.from);
// fallthrough
case "history":
port.postMessage({
type: "history",
history
});
break;
}
};
}
};
```
# chat-module.js
```javascript
export const history = [];
export const add = (content, from) => {
if (history.length > 10) history.shift();
history.push(`${from}: ${content}`);
};
```
# dist/main.js
```javascript
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({});
```
<details><summary><code>/* webpack runtime code */</code></summary>
``` js
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(__webpack_module_cache__[moduleId]) {
/******/ return __webpack_module_cache__[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames not based on template
/******/ if (chunkId === 631) return "workers/fibonacci.js";
/******/ // return url for filenames based on template
/******/ return "" + (chunkId === 348 ? "chat" : chunkId) + ".js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
/******/ })();
/******/
/******/ /* webpack/runtime/load script */
/******/ (() => {
/******/ var inProgress = {};
/******/ // data-webpack is not used as build has no uniqueName
/******/ // loadScript function to load a script via script tag
/******/ __webpack_require__.l = (url, done, key) => {
/******/ if(inProgress[url]) { inProgress[url].push(done); return; }
/******/ var script, needAttach;
/******/ if(key !== undefined) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ for(var i = 0; i < scripts.length; i++) {
/******/ var s = scripts[i];
/******/ if(s.getAttribute("src") == url) { script = s; break; }
/******/ }
/******/ }
/******/ if(!script) {
/******/ needAttach = true;
/******/ script = document.createElement('script');
/******/
/******/ script.charset = 'utf-8';
/******/ script.timeout = 120;
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/
/******/ script.src = url;
/******/ }
/******/ inProgress[url] = [done];
/******/ var onScriptComplete = (prev, event) => {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var doneFns = inProgress[url];
/******/ delete inProgress[url];
/******/ script.parentNode && script.parentNode.removeChild(script);
/******/ doneFns && doneFns.forEach((fn) => fn(event));
/******/ if(prev) return prev(event);
/******/ }
/******/ ;
/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
/******/ script.onerror = onScriptComplete.bind(null, script.onerror);
/******/ script.onload = onScriptComplete.bind(null, script.onload);
/******/ needAttach && document.head.appendChild(script);
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
/******/ __webpack_require__.p = "dist/";
/******/ })();
/******/
/******/ /* webpack/runtime/jsonp chunk loading */
/******/ (() => {
/******/ __webpack_require__.b = document.baseURI || self.location.href;
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ 179: 0
/******/ };
/******/
/******/
/******/ __webpack_require__.f.j = (chunkId, promises) => {
/******/ // JSONP chunk loading for javascript
/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ if(true) { // all chunks have JS
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise((resolve, reject) => {
/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/ });
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId);
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ var loadingEnded = (event) => {
/******/ if(__webpack_require__.o(installedChunks, chunkId)) {
/******/ installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/ if(installedChunkData) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ installedChunkData[1](error);
/******/ }
/******/ }
/******/ };
/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId);
/******/ } else installedChunks[chunkId] = 0;
/******/ }
/******/ }
/******/ };
/******/
/******/ // no prefetching
/******/
/******/ // no preloaded
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/
/******/ // no deferred startup
/******/
/******/ // install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) runtime(__webpack_require__);
/******/ parentChunkLoadingFunction(data);
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ }
/******/
/******/ var chunkLoadingGlobal = self["webpackChunk"] = self["webpackChunk"] || [];
/******/ var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);
/******/ chunkLoadingGlobal.push = webpackJsonpCallback;
/******/ })();
/******/
/************************************************************************/
```
</details>
``` js
/*!********************!*\
!*** ./example.js ***!
\********************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: __webpack_require__.p, __webpack_require__.b, __webpack_require__.u, __webpack_require__.e, __webpack_require__, __webpack_require__.* */
/*! ModuleConcatenation bailout: Module is not an ECMAScript module */
document.body.innerHTML = `
<pre id="history"></pre>
<form>
<input id="message" type="text">
<button id="send">Send Message</button>
</form>
<p>Computing fibonacci without worker:</p>
<input id="fib1" type="number">
<pre id="output1"></pre>
<p>Computing fibonacci with worker:</p>
<input id="fib2" type="number">
<pre id="output2"></pre>
`;
const history = document.getElementById("history");
const message = document.getElementById("message");
const send = document.getElementById("send");
const fib1 = document.getElementById("fib1");
const output1 = document.getElementById("output1");
const fib2 = document.getElementById("fib2");
const output2 = document.getElementById("output2");
/// CHAT with shared worker ///
const chatWorker = new SharedWorker(
new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u(348), __webpack_require__.b),
{
name: "chat"
}
);
let historyTimeout;
const scheduleUpdateHistory = () => {
clearTimeout(historyTimeout);
historyTimeout = setTimeout(() => {
chatWorker.port.postMessage({ type: "history" });
}, 1000);
};
scheduleUpdateHistory();
const from = `User ${Math.floor(Math.random() * 10000)}`;
send.addEventListener("click", e => {
chatWorker.port.postMessage({
type: "message",
content: message.value,
from
});
message.value = "";
message.focus();
e.preventDefault();
});
chatWorker.port.onmessage = event => {
const msg = event.data;
switch (msg.type) {
case "history":
history.innerText = msg.history.join("\n");
scheduleUpdateHistory();
break;
}
};
/// FIBONACCI without worker ///
fib1.addEventListener("change", async () => {
try {
const value = parseInt(fib1.value, 10);
const { fibonacci } = await __webpack_require__.e(/*! import() */ 129).then(__webpack_require__.bind(__webpack_require__, /*! ./fibonacci */ 2));
const result = fibonacci(value);
output1.innerText = `fib(${value}) = ${result}`;
} catch (e) {
output1.innerText = e.message;
}
});
/// FIBONACCI with worker ///
const fibWorker = new Worker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u(631), __webpack_require__.b), {
name: "fibonacci"
/* webpackEntryOptions: { filename: "workers/[name].js" } */
});
fib2.addEventListener("change", () => {
try {
const value = parseInt(fib2.value, 10);
fibWorker.postMessage(`${value}`);
} catch (e) {
output2.innerText = e.message;
}
});
fibWorker.onmessage = event => {
output2.innerText = event.data;
};
/******/ })()
;
```
# dist/chat.js
```javascript
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/*!************************************!*\
!*** ./chat-worker.js + 1 modules ***!
\************************************/
/*! namespace exports */
/*! runtime requirements: */
// CONCATENATED MODULE: ./chat-module.js
const chat_module_history = [];
const add = (content, from) => {
if (chat_module_history.length > 10) chat_module_history.shift();
chat_module_history.push(`${from}: ${content}`);
};
// CONCATENATED MODULE: ./chat-worker.js
onconnect = function (e) {
for (const port of e.ports) {
port.onmessage = event => {
const msg = event.data;
switch (msg.type) {
case "message":
add(msg.content, msg.from);
// fallthrough
case "history":
port.postMessage({
type: "history",
history: chat_module_history
});
break;
}
};
}
};
/******/ })()
;
```
```javascript
(()=>{"use strict";const s=[];onconnect=function(t){for(const o of t.ports)o.onmessage=t=>{const e=t.data;switch(e.type){case"message":n=e.content,c=e.from,s.length>10&&s.shift(),s.push(`${c}: ${n}`);case"history":o.postMessage({type:"history",history:s})}var n,c}}})();
```
# dist/workers/fibonacci.js
```javascript
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({});
```
<details><summary><code>/* webpack runtime code */</code></summary>
``` js
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(__webpack_module_cache__[moduleId]) {
/******/ return __webpack_module_cache__[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
/******/ __webpack_require__.p = "dist/";
/******/ })();
/******/
/******/ /* webpack/runtime/importScripts chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded chunks
/******/ // "1" means "already loaded"
/******/ var installedChunks = {
/******/ 631: 1
/******/ };
/******/
/******/ // importScripts chunk loading
/******/ var chunkLoadingCallback = (data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ for(var moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) runtime(__webpack_require__);
/******/ while(chunkIds.length)
/******/ installedChunks[chunkIds.pop()] = 1;
/******/ parentChunkLoadingFunction(data);
/******/ };
/******/ __webpack_require__.f.i = (chunkId, promises) => {
/******/ // "1" is the signal for "already loaded"
/******/ if(!installedChunks[chunkId]) {
/******/ importScripts("../" + __webpack_require__.u(chunkId));
/******/ }
/******/ };
/******/
/******/ var chunkLoadingGlobal = self["webpackChunk"] = self["webpackChunk"] || [];
/******/ var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);
/******/ chunkLoadingGlobal.push = chunkLoadingCallback;
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/ })();
/******/
/************************************************************************/
```
</details>
``` js
/*!***********************!*\
!*** ./fib-worker.js ***!
\***********************/
/*! unknown exports (runtime-defined) */
/*! runtime requirements: __webpack_require__.e, __webpack_require__, __webpack_require__.* */
/*! ModuleConcatenation bailout: Module is not an ECMAScript module */
onmessage = async event => {
const { fibonacci } = await __webpack_require__.e(/*! import() */ 129).then(__webpack_require__.bind(__webpack_require__, /*! ./fibonacci */ 2));
const value = JSON.parse(event.data);
postMessage(`fib(${value}) = ${fibonacci(value)}`);
};
/******/ })()
;
```
```javascript
(()=>{var e={},r={};function o(t){if(r[t])return r[t].exports;var a=r[t]={exports:{}};return e[t](a,a.exports,o),a.exports}o.m=e,o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce((r,t)=>(o.f[t](e,r),r),[])),o.u=e=>e+".js",o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.p="dist/",(()=>{var e={631:1};o.f.i=(r,t)=>{e[r]||importScripts("../"+o.u(r))};var r=self.webpackChunk=self.webpackChunk||[],t=r.push.bind(r);r.push=r=>{var[a,n,s]=r;for(var p in n)o.o(n,p)&&(o.m[p]=n[p]);for(s&&s(o);a.length;)e[a.pop()]=1;t(r)}})(),onmessage=async e=>{const{fibonacci:r}=await o.e(129).then(o.bind(o,129)),t=JSON.parse(e.data);postMessage(`fib(${t}) = ${r(t)}`)}})();
```
# dist/129.js
```javascript
(self["webpackChunk"] = self["webpackChunk"] || []).push([[129],{
/***/ 2:
/*!**********************!*\
!*** ./fibonacci.js ***!
\**********************/
/*! namespace exports */
/*! export fibonacci [provided] [maybe used in main, fibonacci (runtime-defined)] [usage prevents renaming] */
/*! other exports [not provided] [maybe used in main, fibonacci (runtime-defined)] */
/*! runtime requirements: __webpack_require__.r, __webpack_exports__, __webpack_require__.d, __webpack_require__.* */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "fibonacci": () => /* binding */ fibonacci
/* harmony export */ });
function fibonacci(n) {
return n < 1 ? 0 : n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2);
}
/***/ })
}]);
```
# Info
## Unoptimized
```
asset 129.js 907 bytes [emitted]
asset chat.js 879 bytes [emitted] (name: chat)
asset main.js 12.2 KiB [emitted] (name: main)
asset workers/fibonacci.js 5.3 KiB [emitted] (name: fibonacci)
chunk (runtime: fibonacci, main) 129.js 103 bytes [rendered]
> ./fibonacci ./example.js 69:30-51
> ./fibonacci ./fib-worker.js 2:29-50
./fibonacci.js 103 bytes [built] [code generated]
[exports: fibonacci]
import() ./fibonacci ./example.js 69:30-51
import() ./fibonacci ./fib-worker.js 2:29-50
chunk (runtime: main) main.js (main) 2.22 KiB (javascript) 5.59 KiB (runtime) [entry] [rendered]
> ./example.js main
runtime modules 5.59 KiB 8 modules
./example.js 2.22 KiB [built] [code generated]
[no exports used]
entry ./example.js main
chunk (runtime: chat) chat.js (chat) 527 bytes [entry] [rendered]
> ./example.js 25:19-30:1
./chat-worker.js + 1 modules 527 bytes [built] [code generated]
[no exports]
[no exports used]
new Worker() ./chat-worker.js ./example.js 25:19-30:1
chunk (runtime: fibonacci) workers/fibonacci.js (fibonacci) 176 bytes (javascript) 2.1 KiB (runtime) [entry] [rendered]
> ./example.js 79:18-82:2
runtime modules 2.1 KiB 7 modules
./fib-worker.js 176 bytes [built] [code generated]
[no exports used]
new Worker() ./fib-worker.js ./example.js 79:18-82:2
webpack 5.0.0-beta.29 compiled successfully
```
## Production mode
```
asset 129.js 166 bytes [emitted] [minimized]
asset chat.js 270 bytes [emitted] [minimized] (name: chat)
asset main.js 3.37 KiB [emitted] [minimized] (name: main)
asset workers/fibonacci.js 930 bytes [emitted] [minimized] (name: fibonacci)
chunk (runtime: fibonacci, main) 129.js 103 bytes [rendered]
> ./fibonacci ./example.js 69:30-51
> ./fibonacci ./fib-worker.js 2:29-50
./fibonacci.js 103 bytes [built] [code generated]
[exports: fibonacci]
import() ./fibonacci ./example.js 69:30-51
import() ./fibonacci ./fib-worker.js 2:29-50
chunk (runtime: main) main.js (main) 2.22 KiB (javascript) 5.59 KiB (runtime) [entry] [rendered]
> ./example.js main
runtime modules 5.59 KiB 8 modules
./example.js 2.22 KiB [built] [code generated]
[no exports used]
entry ./example.js main
chunk (runtime: chat) chat.js (chat) 527 bytes [entry] [rendered]
> ./example.js 25:19-30:1
./chat-worker.js + 1 modules 527 bytes [built] [code generated]
[no exports]
[no exports used]
new Worker() ./chat-worker.js ./example.js 25:19-30:1
chunk (runtime: fibonacci) workers/fibonacci.js (fibonacci) 176 bytes (javascript) 2.1 KiB (runtime) [entry] [rendered]
> ./example.js 79:18-82:2
runtime modules 2.1 KiB 7 modules
./fib-worker.js 176 bytes [built] [code generated]
[no exports used]
new Worker() ./fib-worker.js ./example.js 79:18-82:2
webpack 5.0.0-beta.29 compiled successfully
```

2
examples/worker/build.js Normal file
View File

@ -0,0 +1,2 @@
global.NO_TARGET_ARGS = true;
require("../build-common");

View File

@ -0,0 +1,6 @@
export const history = [];
export const add = (content, from) => {
if (history.length > 10) history.shift();
history.push(`${from}: ${content}`);
};

View File

@ -0,0 +1,20 @@
import { history, add } from "./chat-module";
onconnect = function (e) {
for (const port of e.ports) {
port.onmessage = event => {
const msg = event.data;
switch (msg.type) {
case "message":
add(msg.content, msg.from);
// fallthrough
case "history":
port.postMessage({
type: "history",
history
});
break;
}
};
}
};

View File

@ -0,0 +1,95 @@
document.body.innerHTML = `
<pre id="history"></pre>
<form>
<input id="message" type="text">
<button id="send">Send Message</button>
</form>
<p>Computing fibonacci without worker:</p>
<input id="fib1" type="number">
<pre id="output1"></pre>
<p>Computing fibonacci with worker:</p>
<input id="fib2" type="number">
<pre id="output2"></pre>
`;
const history = document.getElementById("history");
const message = document.getElementById("message");
const send = document.getElementById("send");
const fib1 = document.getElementById("fib1");
const output1 = document.getElementById("output1");
const fib2 = document.getElementById("fib2");
const output2 = document.getElementById("output2");
/// CHAT with shared worker ///
const chatWorker = new SharedWorker(
new URL("./chat-worker.js", import.meta.url),
{
name: "chat"
}
);
let historyTimeout;
const scheduleUpdateHistory = () => {
clearTimeout(historyTimeout);
historyTimeout = setTimeout(() => {
chatWorker.port.postMessage({ type: "history" });
}, 1000);
};
scheduleUpdateHistory();
const from = `User ${Math.floor(Math.random() * 10000)}`;
send.addEventListener("click", e => {
chatWorker.port.postMessage({
type: "message",
content: message.value,
from
});
message.value = "";
message.focus();
e.preventDefault();
});
chatWorker.port.onmessage = event => {
const msg = event.data;
switch (msg.type) {
case "history":
history.innerText = msg.history.join("\n");
scheduleUpdateHistory();
break;
}
};
/// FIBONACCI without worker ///
fib1.addEventListener("change", async () => {
try {
const value = parseInt(fib1.value, 10);
const { fibonacci } = await import("./fibonacci");
const result = fibonacci(value);
output1.innerText = `fib(${value}) = ${result}`;
} catch (e) {
output1.innerText = e.message;
}
});
/// FIBONACCI with worker ///
const fibWorker = new Worker(new URL("./fib-worker.js", import.meta.url), {
name: "fibonacci"
/* webpackEntryOptions: { filename: "workers/[name].js" } */
});
fib2.addEventListener("change", () => {
try {
const value = parseInt(fib2.value, 10);
fibWorker.postMessage(`${value}`);
} catch (e) {
output2.innerText = e.message;
}
});
fibWorker.onmessage = event => {
output2.innerText = event.data;
};

View File

@ -0,0 +1,5 @@
onmessage = async event => {
const { fibonacci } = await import("./fibonacci");
const value = JSON.parse(event.data);
postMessage(`fib(${value}) = ${fibonacci(value)}`);
};

View File

@ -0,0 +1,3 @@
export function fibonacci(n) {
return n < 1 ? 0 : n <= 2 ? 1 : fibonacci(n - 1) + fibonacci(n - 2);
}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Worker example</title>
</head>
<body>
<script src="./dist/main.js" async></script>
</body>
</html>

View File

@ -0,0 +1,75 @@
# example.js
```javascript
_{{example.js}}_
```
# fib-worker.js
```javascript
_{{fib-worker.js}}_
```
# fibonacci.js
```javascript
_{{fibonacci.js}}_
```
# chat-worker.js
```javascript
_{{chat-worker.js}}_
```
# chat-module.js
```javascript
_{{chat-module.js}}_
```
# dist/main.js
```javascript
_{{dist/main.js}}_
```
# dist/chat.js
```javascript
_{{dist/chat.js}}_
```
```javascript
_{{production:dist/chat.js}}_
```
# dist/workers/fibonacci.js
```javascript
_{{dist/workers/fibonacci.js}}_
```
```javascript
_{{production:dist/workers/fibonacci.js}}_
```
# dist/129.js
```javascript
_{{dist/129.js}}_
```
# Info
## Unoptimized
```
_{{stdout}}_
```
## Production mode
```
_{{production:stdout}}_
```

View File

@ -0,0 +1,17 @@
var path = require("path");
module.exports = {
entry: "./example.js",
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js",
chunkFilename: "[name].js",
publicPath: "/dist/"
},
optimization: {
concatenateModules: true,
usedExports: true,
providedExports: true,
chunkIds: "deterministic" // To keep filename consistent between different modes (for example building only)
}
};

View File

@ -13,12 +13,13 @@ const makeSerializable = require("./util/makeSerializable");
/** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("./Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./util/Hash")} Hash */
class AsyncDependenciesBlock extends DependenciesBlock {
/**
* @param {ChunkGroupOptions} groupOptions options for the group
* @param {ChunkGroupOptions & { entryOptions?: EntryOptions }} groupOptions options for the group
* @param {DependencyLocation=} loc the line of code
* @param {string=} request the request
*/
@ -64,14 +65,6 @@ class AsyncDependenciesBlock extends DependenciesBlock {
super.updateHash(hash, context);
}
/**
* @param {ChunkGroup} parentChunkGroup the parent chunk group
* @returns {boolean} true when this dependencies block should be loaded async
*/
isAsync(parentChunkGroup) {
return true;
}
serialize(context) {
const { write } = context;
write(this.groupOptions);

View File

@ -26,6 +26,7 @@ const { mergeRuntime } = require("./util/runtime");
/** @typedef {import("./Compilation")} Compilation */
/** @typedef {import("./Compilation").AssetInfo} AssetInfo */
/** @typedef {import("./Compilation").PathData} PathData */
/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
/** @typedef {import("./util/Hash")} Hash */
@ -427,7 +428,6 @@ class Chunk {
hasRuntime() {
for (const chunkGroup of this._groups) {
if (
chunkGroup.isInitial() &&
chunkGroup instanceof Entrypoint &&
chunkGroup.getRuntimeChunk() === this
) {
@ -458,6 +458,18 @@ class Chunk {
return true;
}
/**
* @returns {EntryOptions | undefined} the entry options for this chunk
*/
getEntryOptions() {
for (const chunkGroup of this._groups) {
if (chunkGroup instanceof Entrypoint) {
return chunkGroup.options;
}
}
return undefined;
}
/**
* @param {ChunkGroup} chunkGroup the chunkGroup the chunk is being added
* @returns {void}
@ -602,6 +614,25 @@ class Chunk {
return chunks;
}
/**
* @returns {Set<Entrypoint>} a set of all the referenced entrypoints
*/
getAllReferencedAsyncEntrypoints() {
const queue = new Set(this.groupsIterable);
const entrypoints = new Set();
for (const chunkGroup of queue) {
for (const entrypoint of chunkGroup.asyncEntrypointsIterable) {
entrypoints.add(entrypoint);
}
for (const child of chunkGroup.childrenIterable) {
queue.add(child);
}
}
return entrypoints;
}
/**
* @returns {boolean} true, if the chunk references async chunks
*/

View File

@ -17,6 +17,7 @@ const {
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./ChunkGraph")} ChunkGraph */
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("./Entrypoint")} Entrypoint */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./ModuleGraph")} ModuleGraph */
@ -81,7 +82,10 @@ class ChunkGroup {
this.options = options;
/** @type {SortableSet<ChunkGroup>} */
this._children = new SortableSet(undefined, sortById);
/** @type {SortableSet<ChunkGroup>} */
this._parents = new SortableSet(undefined, sortById);
/** @type {SortableSet<ChunkGroup>} */
this._asyncEntrypoints = new SortableSet(undefined, sortById);
this._blocks = new SortableSet();
/** @type {Chunk[]} */
this.chunks = [];
@ -241,20 +245,21 @@ class ChunkGroup {
return false;
}
/**
* @returns {boolean} true, when this chunk group will be loaded on initial page load
*/
isInitial() {
return false;
}
/**
* @param {ChunkGroup} group chunk ground to add
* @returns {boolean} returns true if chunk ground was added
* @param {ChunkGroup} group chunk group to add
* @returns {boolean} returns true if chunk group was added
*/
addChild(group) {
if (this._children.has(group)) {
return false;
}
const size = this._children.size;
this._children.add(group);
return true;
return size !== this._children.size;
}
/**
@ -333,6 +338,20 @@ class ChunkGroup {
return false;
}
/**
* @param {Entrypoint} entrypoint entrypoint to add
* @returns {boolean} returns true if entrypoint was added
*/
addAsyncEntrypoint(entrypoint) {
const size = this._asyncEntrypoints.size;
this._asyncEntrypoints.add(entrypoint);
return size !== this._asyncEntrypoints.size;
}
get asyncEntrypointsIterable() {
return this._asyncEntrypoints;
}
/**
* @returns {Array} an array containing the blocks
*/

View File

@ -85,6 +85,7 @@ const { getRuntimeKey } = require("./util/runtime");
/** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
/** @typedef {import("./DependencyTemplate")} DependencyTemplate */
/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
/** @typedef {import("./Module")} Module */
/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
/** @typedef {import("./ModuleFactory")} ModuleFactory */
@ -155,8 +156,6 @@ const { getRuntimeKey } = require("./util/runtime");
* @property {ChunkGraph} chunkGraph the chunk graph
*/
/** @typedef {{ name: string } & Omit<EntryDescription, "import">} EntryOptions */
/**
* @typedef {Object} EntryData
* @property {Dependency[]} dependencies dependencies of the entrypoint that should be evaluated at startup
@ -716,6 +715,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
};
/** @type {Map<string, Entrypoint>} */
this.entrypoints = new Map();
/** @type {Entrypoint[]} */
this.asyncEntrypoints = [];
/** @type {Set<Chunk>} */
this.chunks = new Set();
arrayToSetDeprecation(this.chunks, "Compilation.chunks");
@ -1751,11 +1752,10 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
for (const [name, { dependencies, includeDependencies, options }] of this
.entries) {
const chunk = this.addChunk(name);
chunk.name = name;
if (options.filename) {
chunk.filenameTemplate = options.filename;
}
const entrypoint = new Entrypoint(name);
const entrypoint = new Entrypoint(options);
if (!options.dependOn && !options.runtime) {
entrypoint.setRuntimeChunk(chunk);
}
@ -1951,7 +1951,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
this.logger.time("runtime requirements");
this.hooks.beforeRuntimeRequirements.call();
this.processRuntimeRequirements(this.entrypoints.values());
this.processRuntimeRequirements();
this.hooks.afterRuntimeRequirements.call();
this.logger.timeEnd("runtime requirements");
@ -2177,10 +2177,9 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
}
/**
* @param {Iterable<Entrypoint>} entrypoints the entrypoints
* @returns {void}
*/
processRuntimeRequirements(entrypoints) {
processRuntimeRequirements() {
const { chunkGraph } = this;
const additionalModuleRuntimeRequirements = this.hooks
@ -2232,7 +2231,11 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
/** @type {Set<Chunk>} */
const treeEntries = new Set();
for (const ep of entrypoints) {
for (const ep of this.entrypoints.values()) {
const chunk = ep.getRuntimeChunk();
if (chunk) treeEntries.add(chunk);
}
for (const ep of this.asyncEntrypoints) {
const chunk = ep.getRuntimeChunk();
if (chunk) treeEntries.add(chunk);
}
@ -2305,7 +2308,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
}
/**
* @param {string|ChunkGroupOptions} groupOptions options for the chunk group
* @param {string | ChunkGroupOptions} groupOptions options for the chunk group
* @param {Module} module the module the references the chunk group
* @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
* @param {string} request the request from which the the chunk group is referenced
@ -2316,6 +2319,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
groupOptions = { name: groupOptions };
}
const name = groupOptions.name;
if (name) {
const chunkGroup = this.namedChunkGroups.get(name);
if (chunkGroup !== undefined) {
@ -2339,6 +2343,49 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
return chunkGroup;
}
/**
* @param {EntryOptions} options options for the entrypoint
* @param {Module} module the module the references the chunk group
* @param {DependencyLocation} loc the location from with the chunk group is referenced (inside of module)
* @param {string} request the request from which the the chunk group is referenced
* @returns {Entrypoint} the new or existing entrypoint
*/
addAsyncEntrypoint(options, module, loc, request) {
const name = options.name;
if (name) {
const entrypoint = this.namedChunkGroups.get(name);
if (entrypoint instanceof Entrypoint) {
if (entrypoint !== undefined) {
if (module) {
entrypoint.addOrigin(module, loc, request);
}
return entrypoint;
}
} else if (entrypoint) {
throw new Error(
`Cannot add an async entrypoint with the name '${name}', because there is already an chunk group with this name`
);
}
}
const chunk = this.addChunk(name);
if (options.filename) {
chunk.filenameTemplate = options.filename;
}
const entrypoint = new Entrypoint(options, false);
entrypoint.setRuntimeChunk(chunk);
entrypoint.setEntrypointChunk(chunk);
if (name) {
this.namedChunkGroups.set(name, entrypoint);
}
this.chunkGroups.push(entrypoint);
this.asyncEntrypoints.push(entrypoint);
connectChunkGroupAndChunk(entrypoint, chunk);
if (module) {
entrypoint.addOrigin(module, loc, request);
}
return entrypoint;
}
/**
* This method first looks to see if a name is provided for a new chunk,
* and first looks to see if any named chunks already exist and reuse that chunk instead.

View File

@ -6,8 +6,8 @@
"use strict";
/** @typedef {import("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
/** @typedef {import("./Compilation").EntryOptions} EntryOptions */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
class EntryOptionPlugin {
/**

View File

@ -7,8 +7,8 @@
const EntryDependency = require("./dependencies/EntryDependency");
/** @typedef {import("./Compilation").EntryOptions} EntryOptions */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./Entrypoint").EntryOptions} EntryOptions */
class EntryPlugin {
/**

View File

@ -7,8 +7,11 @@
const ChunkGroup = require("./ChunkGroup");
/** @typedef {import("../declarations/WebpackOptions").EntryDescriptionNormalized} EntryDescription */
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {{ name?: string } & Omit<EntryDescription, "import">} EntryOptions */
/**
* Entrypoint serves as an encapsulation primitive for chunks that are
* a part of a single ChunkGroup. They represent all bundles that need to be loaded for a
@ -18,22 +21,30 @@ const ChunkGroup = require("./ChunkGroup");
class Entrypoint extends ChunkGroup {
/**
* Creates an instance of Entrypoint.
* @param {string} name the name of the entrypoint
* @param {EntryOptions | string} entryOptions the options for the entrypoint (or name)
* @param {boolean=} initial false, when the entrypoint is not initial loaded
*/
constructor(name) {
super(name);
constructor(entryOptions, initial = true) {
if (typeof entryOptions === "string") {
entryOptions = { name: entryOptions };
}
super({
name: entryOptions.name
});
this.options = entryOptions;
/** @type {Chunk=} */
this._runtimeChunk = undefined;
/** @type {Chunk=} */
this._entrypointChunk = undefined;
/** @type {boolean} */
this._initial = initial;
}
/**
* isInitial will always return true for Entrypoint ChunkGroup.
* @returns {true} returns true as all entrypoints are initial ChunkGroups
* @returns {boolean} true, when this chunk group will be loaded on initial page load
*/
isInitial() {
return true;
return this._initial;
}
/**

View File

@ -154,7 +154,7 @@ class FlagDependencyUsagePlugin {
};
/**
* @param {Module} module the module
* @param {DependenciesBlock} module the module
* @param {RuntimeSpec} runtime part of which runtime
* @returns {void}
*/
@ -166,7 +166,11 @@ class FlagDependencyUsagePlugin {
const queue = [module];
for (const block of queue) {
for (const b of block.blocks) {
queue.push(b);
if (b.groupOptions && b.groupOptions.entryOptions) {
processModule(b, b.groupOptions.entryOptions.runtime);
} else {
queue.push(b);
}
}
for (const dep of block.dependencies) {
const connection = moduleGraph.getConnection(dep);
@ -303,14 +307,23 @@ class FlagDependencyUsagePlugin {
);
if (!this.global) {
compilation.hooks.afterChunks.tap("FlagDependencyUsagePlugin", () => {
/** @type {Set<Chunk>} */
const runtimeChunks = new Set();
/** @type {Map<Chunk, string>} */
const runtimeChunks = new Map();
for (const entrypoint of compilation.entrypoints.values()) {
runtimeChunks.add(entrypoint.getRuntimeChunk());
runtimeChunks.set(
entrypoint.getRuntimeChunk(),
entrypoint.options.runtime
);
}
for (const entrypoint of compilation.asyncEntrypoints) {
runtimeChunks.set(
entrypoint.getRuntimeChunk(),
entrypoint.options.runtime
);
}
for (const runtimeChunk of runtimeChunks) {
const runtime = runtimeChunk.name;
for (const [runtimeChunk, runtimeName] of runtimeChunks) {
const runtime = runtimeName || runtimeChunk.name;
for (const chunk of runtimeChunk.getAllReferencedChunks()) {
chunk.runtime = mergeRuntime(chunk.runtime, runtime);
}

View File

@ -50,7 +50,7 @@ class LibManifestPlugin {
asyncLib.forEach(
Array.from(compilation.chunks),
(chunk, callback) => {
if (!chunk.isOnlyInitial()) {
if (!chunk.canBeInitial()) {
callback();
return;
}

View File

@ -203,7 +203,7 @@ class RuntimePlugin {
RuntimeGlobals.getChunkScriptFilename,
chunk =>
chunk.filenameTemplate ||
(chunk.isOnlyInitial()
(chunk.canBeInitial()
? compilation.outputOptions.filename
: compilation.outputOptions.chunkFilename),
false

View File

@ -28,7 +28,6 @@ const TemplatedPathPlugin = require("./TemplatedPathPlugin");
const UseStrictPlugin = require("./UseStrictPlugin");
const WarnCaseSensitiveModulesPlugin = require("./WarnCaseSensitiveModulesPlugin");
const URLPlugin = require("./dependencies/URLPlugin");
const DataUriPlugin = require("./schemes/DataUriPlugin");
const FileUriPlugin = require("./schemes/FileUriPlugin");
@ -43,6 +42,7 @@ const RequireContextPlugin = require("./dependencies/RequireContextPlugin");
const RequireEnsurePlugin = require("./dependencies/RequireEnsurePlugin");
const RequireIncludePlugin = require("./dependencies/RequireIncludePlugin");
const SystemPlugin = require("./dependencies/SystemPlugin");
const URLPlugin = require("./dependencies/URLPlugin");
const InferAsyncModulesPlugin = require("./async-modules/InferAsyncModulesPlugin");
@ -276,7 +276,6 @@ class WebpackOptionsApply extends OptionsApply {
new InferAsyncModulesPlugin().apply(compiler);
new URLPlugin().apply(compiler);
new DataUriPlugin().apply(compiler);
new FileUriPlugin().apply(compiler);
@ -307,6 +306,12 @@ class WebpackOptionsApply extends OptionsApply {
new ImportPlugin(options.module).apply(compiler);
new SystemPlugin(options.module).apply(compiler);
new ImportMetaPlugin().apply(compiler);
new URLPlugin().apply(compiler);
if (options.output.workerChunkLoading) {
const WorkerPlugin = require("./dependencies/WorkerPlugin");
new WorkerPlugin(options.output.workerChunkLoading).apply(compiler);
}
new DefaultStatsFactoryPlugin().apply(compiler);
new DefaultStatsPresetPlugin().apply(compiler);

View File

@ -178,10 +178,15 @@ const visitModules = (
/** @type {Map<string, ChunkGroupInfo>} */
const namedChunkGroups = new Map();
const ADD_AND_ENTER_MODULE = 0;
const ENTER_MODULE = 1;
const PROCESS_BLOCK = 2;
const LEAVE_MODULE = 3;
/** @type {Map<string, ChunkGroupInfo>} */
const namedAsyncEntrypoints = new Map();
const ADD_AND_ENTER_ENTRY_MODULE = 0;
const ADD_AND_ENTER_MODULE = 1;
const ENTER_MODULE = 2;
const PROCESS_BLOCK = 3;
const PROCESS_ENTRY_BLOCK = 4;
const LEAVE_MODULE = 5;
/** @type {QueueItem[]} */
let queue = [];
@ -293,77 +298,135 @@ const visitModules = (
let cgi = blockChunkGroups.get(b);
/** @type {ChunkGroup} */
let c;
/** @type {Entrypoint} */
let entrypoint;
const entryOptions = b.groupOptions && b.groupOptions.entryOptions;
if (cgi === undefined) {
const chunkName = (b.groupOptions && b.groupOptions.name) || b.chunkName;
cgi = namedChunkGroups.get(chunkName);
if (!cgi) {
c = compilation.addChunkInGroup(
b.groupOptions || b.chunkName,
module,
b.loc,
b.request
);
c.index = nextChunkGroupIndex++;
cgi = {
chunkGroup: c,
minAvailableModules: undefined,
minAvailableModulesOwned: undefined,
availableModulesToBeMerged: [],
skippedItems: undefined,
resultingAvailableModules: undefined,
children: undefined,
availableSources: undefined,
availableChildren: undefined,
preOrderIndex: 0,
postOrderIndex: 0
};
allCreatedChunkGroups.add(c);
chunkGroupInfoMap.set(c, cgi);
if (chunkName) {
namedChunkGroups.set(chunkName, cgi);
}
} else {
c = cgi.chunkGroup;
if (c.isInitial()) {
compilation.errors.push(
new AsyncDependencyToInitialChunkError(chunkName, module, b.loc)
if (entryOptions) {
cgi = namedAsyncEntrypoints.get(chunkName);
if (!cgi) {
entrypoint = compilation.addAsyncEntrypoint(
entryOptions,
module,
b.loc,
b.request
);
c = chunkGroup;
entrypoint.index = nextChunkGroupIndex++;
cgi = {
chunkGroup: entrypoint,
minAvailableModules: EMPTY_SET,
minAvailableModulesOwned: false,
availableModulesToBeMerged: [],
skippedItems: undefined,
resultingAvailableModules: undefined,
children: undefined,
availableSources: undefined,
availableChildren: undefined,
preOrderIndex: 0,
postOrderIndex: 0
};
chunkGroupInfoMap.set(entrypoint, cgi);
chunkGraph.connectBlockAndChunkGroup(b, entrypoint);
if (chunkName) {
namedAsyncEntrypoints.set(chunkName, cgi);
}
} else {
entrypoint = /** @type {Entrypoint} */ (cgi.chunkGroup);
// TODO merge entryOptions
entrypoint.addOrigin(module, b.loc, b.request);
chunkGraph.connectBlockAndChunkGroup(b, entrypoint);
}
// 2. We enqueue the DependenciesBlock for traversal
queueDelayed.push({
action: PROCESS_ENTRY_BLOCK,
block: b,
module: module,
chunk: entrypoint.chunks[0],
chunkGroup: entrypoint,
chunkGroupInfo: cgi
});
} else {
cgi = namedChunkGroups.get(chunkName);
if (!cgi) {
c = compilation.addChunkInGroup(
b.groupOptions || b.chunkName,
module,
b.loc,
b.request
);
c.index = nextChunkGroupIndex++;
cgi = {
chunkGroup: c,
minAvailableModules: undefined,
minAvailableModulesOwned: undefined,
availableModulesToBeMerged: [],
skippedItems: undefined,
resultingAvailableModules: undefined,
children: undefined,
availableSources: undefined,
availableChildren: undefined,
preOrderIndex: 0,
postOrderIndex: 0
};
allCreatedChunkGroups.add(c);
chunkGroupInfoMap.set(c, cgi);
if (chunkName) {
namedChunkGroups.set(chunkName, cgi);
}
} else {
c = cgi.chunkGroup;
if (c.isInitial()) {
compilation.errors.push(
new AsyncDependencyToInitialChunkError(chunkName, module, b.loc)
);
c = chunkGroup;
}
c.addOptions(b.groupOptions);
c.addOrigin(module, b.loc, b.request);
}
c.addOptions(b.groupOptions);
c.addOrigin(module, b.loc, b.request);
}
blockChunkGroups.set(b, cgi);
} else if (entryOptions) {
entrypoint = /** @type {Entrypoint} */ (cgi.chunkGroup);
} else {
c = cgi.chunkGroup;
}
// 2. We store the Block + Chunk Group mapping as dependency
// for the chunk group which is set in processQueue
let deps = chunkGroupDependencies.get(chunkGroup);
if (!deps) chunkGroupDependencies.set(chunkGroup, (deps = []));
deps.push({
block: b,
chunkGroup: c
});
if (c !== undefined) {
// 2. We store the Block + Chunk Group mapping as dependency
// for the chunk group which is set in processQueue
let deps = chunkGroupDependencies.get(chunkGroup);
if (!deps) chunkGroupDependencies.set(chunkGroup, (deps = []));
deps.push({
block: b,
chunkGroup: c
});
// 3. We enqueue the chunk group info creation/updating
let connectList = queueConnect.get(chunkGroupInfo);
if (connectList === undefined) {
connectList = new Set();
queueConnect.set(chunkGroupInfo, connectList);
// 3. We enqueue the chunk group info creation/updating
let connectList = queueConnect.get(chunkGroupInfo);
if (connectList === undefined) {
connectList = new Set();
queueConnect.set(chunkGroupInfo, connectList);
}
connectList.add(cgi);
// TODO check if this really need to be done for each traversal
// or if it is enough when it's queued when created
// 4. We enqueue the DependenciesBlock for traversal
queueDelayed.push({
action: PROCESS_BLOCK,
block: b,
module: module,
chunk: c.chunks[0],
chunkGroup: c,
chunkGroupInfo: cgi
});
} else {
chunkGroupInfo.chunkGroup.addAsyncEntrypoint(entrypoint);
}
connectList.add(cgi);
// 4. We enqueue the DependenciesBlock for traversal
queueDelayed.push({
action: PROCESS_BLOCK,
block: b,
module: module,
chunk: c.chunks[0],
chunkGroup: c,
chunkGroupInfo: cgi
});
};
/**
@ -424,11 +487,49 @@ const visitModules = (
// Traverse all Blocks
for (const b of block.blocks) {
if (b.isAsync(chunkGroup)) {
iteratorBlock(b);
} else {
processBlock(b);
iteratorBlock(b);
}
if (block.blocks.length > 0 && module !== block) {
blocksWithNestedBlocks.add(block);
}
};
/**
* @param {DependenciesBlock} block the block
* @returns {void}
*/
const processEntryBlock = block => {
statProcessedBlocks++;
// get prepared block info
const blockModules = blockModulesMap.get(block);
if (blockModules !== undefined) {
// Traverse all referenced modules
for (const refModule of blockModules) {
// enqueue, then add and enter to be in the correct order
// this is relevant with circular dependencies
queueBuffer.push({
action: ADD_AND_ENTER_ENTRY_MODULE,
block: refModule,
module: refModule,
chunk,
chunkGroup,
chunkGroupInfo
});
}
// Add buffered items in reverse order
if (queueBuffer.length > 0) {
for (let i = queueBuffer.length - 1; i >= 0; i--) {
queue.push(queueBuffer[i]);
}
queueBuffer.length = 0;
}
}
// Traverse all Blocks
for (const b of block.blocks) {
iteratorBlock(b);
}
if (block.blocks.length > 0 && module !== block) {
@ -447,6 +548,13 @@ const visitModules = (
chunkGroupInfo = queueItem.chunkGroupInfo;
switch (queueItem.action) {
case ADD_AND_ENTER_ENTRY_MODULE:
chunkGraph.connectChunkAndEntryModule(
chunk,
module,
/** @type {Entrypoint} */ (chunkGroup)
);
// fallthrough
case ADD_AND_ENTER_MODULE: {
if (chunkGraph.isModuleInChunk(module, chunk)) {
// already connected, skip it
@ -483,6 +591,10 @@ const visitModules = (
processBlock(block);
break;
}
case PROCESS_ENTRY_BLOCK: {
processEntryBlock(block);
break;
}
case LEAVE_MODULE: {
const index = chunkGroup.getModulePostOrderIndex(module);
if (index === undefined) {

View File

@ -188,6 +188,9 @@ const applyWebpackOptionsDefaults = options => {
if (options.output.chunkLoading) {
enabledChunkLoadingTypes.push(options.output.chunkLoading);
}
if (options.output.workerChunkLoading) {
enabledChunkLoadingTypes.push(options.output.workerChunkLoading);
}
for (const name of Object.keys(options.entry)) {
const desc = options.entry[name];
if (desc.chunkLoading) {
@ -646,6 +649,18 @@ const applyOutputDefaults = (
return false;
}
});
F(output, "workerChunkLoading", () => {
switch (output.chunkLoading) {
case "jsonp":
return "import-scripts";
case "require":
return "require";
case "async-node":
return "async-node";
default:
return false;
}
});
F(output, "devtoolNamespace", () => output.uniqueName);
F(output, "libraryTarget", () => (output.module ? "module" : "var"));
F(output, "path", () => path.join(process.cwd(), "dist"));

View File

@ -219,6 +219,7 @@ const getNormalizedWebpackOptions = config => {
chunkFilename: output.chunkFilename,
chunkFormat: output.chunkFormat,
chunkLoading: output.chunkLoading,
workerChunkLoading: output.workerChunkLoading,
chunkLoadingGlobal: output.chunkLoadingGlobal,
chunkLoadTimeout: output.chunkLoadTimeout,
compareBeforeEmit: output.compareBeforeEmit,

View File

@ -0,0 +1,88 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
const Dependency = require("../Dependency");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const ModuleDependency = require("./ModuleDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../AsyncDependenciesBlock")} AsyncDependenciesBlock */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../Entrypoint")} Entrypoint */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
class WorkerDependency extends ModuleDependency {
/**
* @param {string} request request
* @param {[number, number]} range range
*/
constructor(request, range) {
super(request);
this.range = range;
}
/**
* Returns list of exports referenced by this dependency
* @param {ModuleGraph} moduleGraph module graph
* @param {RuntimeSpec} runtime the runtime for which the module is analysed
* @returns {(string[] | ReferencedExport)[]} referenced exports
*/
getReferencedExports(moduleGraph, runtime) {
return Dependency.NO_EXPORTS_REFERENCED;
}
get type() {
return "new Worker()";
}
get category() {
return "esm";
}
}
WorkerDependency.Template = class WorkerDependencyTemplate extends ModuleDependency.Template {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(dependency, source, templateContext) {
const { chunkGraph, moduleGraph, runtimeRequirements } = templateContext;
const dep = /** @type {WorkerDependency} */ (dependency);
const block = /** @type {AsyncDependenciesBlock} */ (moduleGraph.getParentBlock(
dependency
));
const entrypoint = /** @type {Entrypoint} */ (chunkGraph.getBlockChunkGroup(
block
));
const chunk = entrypoint.getEntrypointChunk();
runtimeRequirements.add(RuntimeGlobals.publicPath);
runtimeRequirements.add(RuntimeGlobals.baseURI);
runtimeRequirements.add(RuntimeGlobals.getChunkScriptFilename);
source.replace(
dep.range[0],
dep.range[1] - 1,
`/* worker import */ ${RuntimeGlobals.publicPath} + ${
RuntimeGlobals.getChunkScriptFilename
}(${JSON.stringify(chunk.id)}), ${RuntimeGlobals.baseURI}`
);
}
};
makeSerializable(WorkerDependency, "webpack/lib/dependencies/WorkerDependency");
module.exports = WorkerDependency;

View File

@ -0,0 +1,259 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { pathToFileURL } = require("url");
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const CommentCompilationWarning = require("../CommentCompilationWarning");
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
const formatLocation = require("../formatLocation");
const EnableChunkLoadingPlugin = require("../javascript/EnableChunkLoadingPlugin");
const {
harmonySpecifierTag
} = require("./HarmonyImportDependencyParserPlugin");
const WorkerDependency = require("./WorkerDependency");
/** @typedef {import("estree").Expression} Expression */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("./HarmonyImportDependencyParserPlugin").HarmonySettings} HarmonySettings */
const getUrl = module => {
return pathToFileURL(module.resource).toString();
};
class WorkerPlugin {
constructor(chunkLoading) {
this._chunkLoading = chunkLoading;
}
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
if (this._chunkLoading) {
new EnableChunkLoadingPlugin(this._chunkLoading).apply(compiler);
}
compiler.hooks.thisCompilation.tap(
"WorkerPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
WorkerDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
WorkerDependency,
new WorkerDependency.Template()
);
/**
* @param {JavascriptParser} parser the parser
* @param {Expression} expr expression
* @returns {[BasicEvaluatedExpression, [number, number]]} parsed
*/
const parseModuleUrl = (parser, expr) => {
if (
expr.type !== "NewExpression" ||
expr.callee.type === "Super" ||
expr.arguments.length !== 2
)
return;
const [arg1, arg2] = expr.arguments;
if (arg1.type === "SpreadElement") return;
if (arg2.type === "SpreadElement") return;
const callee = parser.evaluateExpression(expr.callee);
if (!callee.isIdentifier() || callee.identifier !== "URL") return;
const arg2Value = parser.evaluateExpression(arg2);
if (
!arg2Value.isString() ||
!arg2Value.string.startsWith("file://") ||
arg2Value.string !== getUrl(parser.state.module)
) {
return;
}
const arg1Value = parser.evaluateExpression(arg1);
return [arg1Value, [arg1.range[0], arg2.range[1]]];
};
/**
* @param {JavascriptParser} parser the parser
* @param {Expression} expr expression
* @returns {object | undefined} parsed object
*/
const parseObjectLiteral = (parser, expr) => {
if (expr.type !== "ObjectExpression") return;
const obj = {};
for (const prop of expr.properties) {
if (prop.type === "Property") {
if (
!prop.method &&
!prop.computed &&
!prop.shorthand &&
prop.key.type === "Identifier" &&
!prop.value.type.endsWith("Pattern")
) {
const value = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (value.isCompileTimeValue())
obj[prop.key.name] = value.asCompileTimeValue();
}
}
}
return obj;
};
/**
* @param {JavascriptParser} parser the parser
* @param {object} parserOptions options
*/
const parserPlugin = (parser, parserOptions) => {
if (parserOptions.worker === false) return;
const handleNewWorker = expr => {
if (expr.arguments.length === 0 || expr.arguments.length > 2)
return;
const [arg1, arg2] = expr.arguments;
if (arg1.type === "SpreadElement") return;
if (arg2 && arg2.type === "SpreadElement") return;
const parsedUrl = parseModuleUrl(parser, arg1);
if (!parsedUrl) return;
const [url, range] = parsedUrl;
if (url.isString()) {
const options = arg2 && parseObjectLiteral(parser, arg2);
const {
options: importOptions,
errors: commentErrors
} = parser.parseCommentOptions(expr.range);
if (commentErrors) {
for (const e of commentErrors) {
const { comment } = e;
parser.state.module.addWarning(
new CommentCompilationWarning(
`Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
comment.loc
)
);
}
}
/** @type {EntryOptions} */
let entryOptions = {};
if (importOptions) {
if (importOptions.webpackIgnore !== undefined) {
if (typeof importOptions.webpackIgnore !== "boolean") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
expr.loc
)
);
} else {
if (importOptions.webpackIgnore) {
return false;
}
}
}
if (importOptions.webpackEntryOptions !== undefined) {
if (
typeof importOptions.webpackEntryOptions !== "object" ||
importOptions.webpackEntryOptions === null
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackEntryOptions\` expected a object, but received: ${importOptions.webpackEntryOptions}.`,
expr.loc
)
);
} else {
Object.assign(
entryOptions,
importOptions.webpackEntryOptions
);
}
}
if (importOptions.webpackChunkName !== undefined) {
if (typeof importOptions.webpackChunkName !== "string") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
expr.loc
)
);
} else {
entryOptions.name = importOptions.webpackChunkName;
}
}
}
if (
!Object.prototype.hasOwnProperty.call(entryOptions, "name") &&
options &&
options.name
) {
entryOptions.name = options.name;
}
if (!entryOptions.runtime) {
entryOptions.runtime = `${parser.state.module.identifier()}|${formatLocation(
expr.loc
)}`;
}
const block = new AsyncDependenciesBlock({
name: entryOptions.name,
entryOptions: {
chunkLoading: this._chunkLoading,
...entryOptions
}
});
block.loc = expr.loc;
const dep = new WorkerDependency(url.string, range);
dep.loc = expr.loc;
block.addDependency(dep);
parser.state.module.addBlock(block);
parser.walkExpression(expr.callee);
if (arg2) parser.walkExpression(arg2);
return true;
}
};
parser.hooks.new.for("Worker").tap("WorkerPlugin", handleNewWorker);
parser.hooks.new
.for("SharedWorker")
.tap("WorkerPlugin", handleNewWorker);
parser.hooks.call
.for("navigator.serviceWorker.register")
.tap("WorkerPlugin", handleNewWorker);
parser.hooks.new
.for(harmonySpecifierTag)
.tap("WorkerPlugin", expr => {
const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
if (
!settings ||
settings.source !== "worker_threads" ||
settings.ids.length !== 1 ||
settings.ids[0] !== "Worker"
) {
return;
}
return handleNewWorker(expr);
});
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("WorkerPlugin", parserPlugin);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("WorkerPlugin", parserPlugin);
}
);
}
}
module.exports = WorkerPlugin;

View File

@ -21,6 +21,7 @@ const Template = require("../Template");
const StringXor = require("../util/StringXor");
const { compareModulesByIdentifier } = require("../util/comparators");
const createHash = require("../util/createHash");
const { intersectRuntime } = require("../util/runtime");
const JavascriptGenerator = require("./JavascriptGenerator");
const JavascriptParser = require("./JavascriptParser");
@ -367,7 +368,7 @@ class JavascriptModulesPlugin {
return chunk.filenameTemplate;
} else if (chunk instanceof HotUpdateChunk) {
return outputOptions.hotUpdateChunkFilename;
} else if (chunk.hasRuntime() || chunk.isOnlyInitial()) {
} else if (chunk.canBeInitial()) {
return outputOptions.filename;
} else {
return outputOptions.chunkFilename;
@ -813,7 +814,14 @@ class JavascriptModulesPlugin {
result.allowInlineStartup &&
someInIterable(
moduleGraph.getIncomingConnections(entryModule),
c => c.originModule && c.isActive(chunk.runtime)
c =>
c.originModule &&
c.isActive(chunk.runtime) &&
someInIterable(
chunkGraph.getModuleRuntimes(c.originModule),
runtime =>
intersectRuntime(runtime, chunk.runtime) !== undefined
)
)
) {
buf2.push(

View File

@ -267,7 +267,7 @@ class JavascriptParser extends Parser {
),
/** @type {SyncBailHook<[ChainExpressionNode], boolean | void>} */
optionalChaining: new SyncBailHook(["optionalChaining"]),
/** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
/** @type {HookMap<SyncBailHook<[NewExpressionNode], boolean | void>>} */
new: new HookMap(() => new SyncBailHook(["expression"])),
/** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
expression: new HookMap(() => new SyncBailHook(["expression"])),
@ -2747,19 +2747,13 @@ class JavascriptParser extends Parser {
}
callHooksForExpression(hookMap, expr, ...args) {
const exprName = this.getMemberExpressionInfo(
return this.callHooksForExpressionWithFallback(
hookMap,
expr,
ALLOWED_MEMBER_TYPES_EXPRESSION
undefined,
undefined,
...args
);
if (exprName !== undefined) {
return this.callHooksForInfoWithFallback(
hookMap,
exprName.name,
undefined,
undefined,
...args
);
}
}
/**
@ -2784,9 +2778,10 @@ class JavascriptParser extends Parser {
ALLOWED_MEMBER_TYPES_EXPRESSION
);
if (exprName !== undefined) {
const members = exprName.getMembers();
return this.callHooksForInfoWithFallback(
hookMap,
exprName.name,
members.length === 0 ? exprName.rootInfo : exprName.name,
fallback &&
(name => fallback(name, exprName.rootInfo, exprName.getMembers)),
defined && (() => defined(exprName.name)),

View File

@ -75,8 +75,8 @@ class AbstractLibraryPlugin {
const getOptionsForChunk = chunk => {
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) === 0)
return false;
const entry = compilation.entries.get(chunk.name);
const library = entry && entry.options.library;
const options = chunk.getEntryOptions();
const library = options && options.library;
return this._parseOptionsCached(
library !== undefined ? library : compilation.outputOptions.library
);

View File

@ -29,6 +29,7 @@ class CommonJsChunkLoadingPlugin {
? "async-node"
: "require";
new StartupChunkDependenciesPlugin({
chunkLoading: chunkLoadingValue,
asyncChunkLoading: this._asyncChunkLoading
}).apply(compiler);
compiler.hooks.thisCompilation.tap(
@ -36,9 +37,9 @@ class CommonJsChunkLoadingPlugin {
compilation => {
const globalChunkLoading = compilation.outputOptions.chunkLoading;
const isEnabledForChunk = chunk => {
const entry = compilation.entries.get(chunk.name);
const options = chunk.getEntryOptions();
const chunkLoading =
(entry && entry.options.chunkLoading) || globalChunkLoading;
(options && options.chunkLoading) || globalChunkLoading;
return chunkLoading === chunkLoadingValue;
};
const onceForChunkSet = new WeakSet();

View File

@ -30,6 +30,13 @@ class RemoveParentModulesPlugin {
queue.enqueue(child);
}
}
for (const chunkGroup of compilation.asyncEntrypoints) {
// initialize available modules for chunks without parents
availableModulesMap.set(chunkGroup, new Set());
for (const child of chunkGroup.childrenIterable) {
queue.enqueue(child);
}
}
while (queue.length > 0) {
const chunkGroup = queue.dequeue();

View File

@ -101,6 +101,9 @@ class GetChunkFilenameRuntimeModule extends RuntimeModule {
}
}
}
for (const entrypoint of chunk.getAllReferencedAsyncEntrypoints()) {
addChunk(entrypoint.chunks[entrypoint.chunks.length - 1]);
}
/** @type {Map<string, Set<string | number>>} */
const staticUrls = new Map();

View File

@ -12,8 +12,9 @@ const StartupEntrypointRuntimeModule = require("./StartupEntrypointRuntimeModule
class StartupChunkDependenciesPlugin {
constructor(options) {
this.chunkLoading = options.chunkLoading;
this.asyncChunkLoading =
options && typeof options.asyncChunkLoading === "boolean"
typeof options.asyncChunkLoading === "boolean"
? options.asyncChunkLoading
: true;
}
@ -27,9 +28,17 @@ class StartupChunkDependenciesPlugin {
compiler.hooks.thisCompilation.tap(
"StartupChunkDependenciesPlugin",
compilation => {
const globalChunkLoading = compilation.outputOptions.chunkLoading;
const isEnabledForChunk = chunk => {
const options = chunk.getEntryOptions();
const chunkLoading =
(options && options.chunkLoading) || globalChunkLoading;
return chunkLoading === this.chunkLoading;
};
compilation.hooks.additionalTreeRuntimeRequirements.tap(
"StartupChunkDependenciesPlugin",
(chunk, set) => {
if (!isEnabledForChunk(chunk)) return;
if (compilation.chunkGraph.hasChunkEntryDependentChunks(chunk)) {
set.add(RuntimeGlobals.startup);
set.add(RuntimeGlobals.ensureChunk);
@ -46,6 +55,7 @@ class StartupChunkDependenciesPlugin {
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.startupEntrypoint)
.tap("StartupChunkDependenciesPlugin", (chunk, set) => {
if (!isEnabledForChunk(chunk)) return;
set.add(RuntimeGlobals.ensureChunk);
set.add(RuntimeGlobals.ensureChunkIncludeEntries);
compilation.addRuntimeModule(

View File

@ -46,6 +46,7 @@ const { makePathsRelative, parseResource } = require("../util/identifier");
/**
* @typedef {Object} UsualContext
* @property {string} type
* @property {function(string): string} makePathsRelative
* @property {Compilation} compilation
* @property {Set<Module>} rootModules
* @property {Map<string,Chunk[]>} compilationFileToChunks
@ -247,7 +248,11 @@ const EXTRACT_ERROR = {
/** @type {SimpleExtractors} */
const SIMPLE_EXTRACTORS = {
compilation: {
_: (object, compilation) => {
_: (object, compilation, context) => {
context.makePathsRelative = makePathsRelative.bindContextCache(
compilation.compiler.context,
compilation.compiler.root
);
if (compilation.name) {
object.name = compilation.name;
}
@ -975,7 +980,7 @@ const SIMPLE_EXTRACTORS = {
}
},
chunk: {
_: (object, chunk, { compilation: { chunkGraph } }) => {
_: (object, chunk, { makePathsRelative, compilation: { chunkGraph } }) => {
const childIdByOrder = chunk.getChildIdsByOrders(chunkGraph);
Object.assign(object, {
@ -992,8 +997,8 @@ const SIMPLE_EXTRACTORS = {
chunk.runtime === undefined
? undefined
: typeof chunk.runtime === "string"
? [chunk.runtime]
: Array.from(chunk.runtime.sort()),
? [makePathsRelative(chunk.runtime)]
: Array.from(chunk.runtime.sort(), makePathsRelative),
files: Array.from(chunk.files),
auxiliaryFiles: Array.from(chunk.auxiliaryFiles).sort(compareIds),
hash: chunk.renderedHash,

View File

@ -150,6 +150,8 @@ module.exports = {
require("../dependencies/WebAssemblyExportImportedDependency"),
"dependencies/WebAssemblyImportDependency": () =>
require("../dependencies/WebAssemblyImportDependency"),
"dependencies/WorkerDependency": () =>
require("../dependencies/WorkerDependency"),
"optimize/ConcatenatedModule": () =>
require("../optimize/ConcatenatedModule"),
DelegatedModule: () => require("../DelegatedModule"),

View File

@ -8,7 +8,7 @@
const SortableSet = require("./SortableSet");
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compilation").EntryOptions} EntryOptions */
/** @typedef {import("../Entrypoint").EntryOptions} EntryOptions */
/** @typedef {string | SortableSet<string> | undefined} RuntimeSpec */

View File

@ -23,9 +23,9 @@ class JsonpChunkLoadingPlugin {
compilation => {
const globalChunkLoading = compilation.outputOptions.chunkLoading;
const isEnabledForChunk = chunk => {
const entry = compilation.entries.get(chunk.name);
const options = chunk.getEntryOptions();
const chunkLoading =
(entry && entry.options.chunkLoading) || globalChunkLoading;
(options && options.chunkLoading) || globalChunkLoading;
return chunkLoading === "jsonp";
};
const onceForChunkSet = new WeakSet();
@ -90,10 +90,8 @@ class JsonpChunkLoadingPlugin {
if (withDefer) {
set.add(RuntimeGlobals.startup);
set.add(RuntimeGlobals.startupNoDefault);
handler(chunk, set);
}
if (withDefer) {
set.add(RuntimeGlobals.require);
handler(chunk, set);
}
}
);

View File

@ -6,6 +6,7 @@
"use strict";
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../ChunkGroup")} ChunkGroup */
/** @typedef {(string|number)[]} EntryItem */
@ -52,10 +53,22 @@ exports.getEntryInfo = (chunkGraph, chunk, chunkFilter) => {
});
};
/**
* @param {Compilation} compilation the compilation
* @param {Chunk} chunk the chunk
* @returns {boolean} true, when other chunks have this chunk as runtime chunk
*/
exports.needEntryDeferringCode = (compilation, chunk) => {
for (const entrypoint of compilation.entrypoints.values()) {
if (entrypoint.getRuntimeChunk() === chunk) {
if (entrypoint.chunks.some(c => c !== chunk)) return true;
if (entrypoint.chunks.length > 1 || entrypoint.chunks[0] !== chunk)
return true;
}
}
for (const entrypoint of compilation.asyncEntrypoints) {
if (entrypoint.getRuntimeChunk() === chunk) {
if (entrypoint.chunks.length > 1 || entrypoint.chunks[0] !== chunk)
return true;
}
}
return false;

View File

@ -19,6 +19,7 @@ class ImportScriptsChunkLoadingPlugin {
*/
apply(compiler) {
new StartupChunkDependenciesPlugin({
chunkLoading: "import-scripts",
asyncChunkLoading: true
}).apply(compiler);
compiler.hooks.thisCompilation.tap(
@ -26,9 +27,9 @@ class ImportScriptsChunkLoadingPlugin {
compilation => {
const globalChunkLoading = compilation.outputOptions.chunkLoading;
const isEnabledForChunk = chunk => {
const entry = compilation.entries.get(chunk.name);
const options = chunk.getEntryOptions();
const chunkLoading =
(entry && entry.options.chunkLoading) || globalChunkLoading;
(options && options.chunkLoading) || globalChunkLoading;
return chunkLoading === "import-scripts";
};
const onceForChunkSet = new WeakSet();

View File

@ -1862,6 +1862,9 @@
},
"webassemblyModuleFilename": {
"$ref": "#/definitions/WebassemblyModuleFilename"
},
"workerChunkLoading": {
"$ref": "#/definitions/ChunkLoading"
}
}
},
@ -1984,6 +1987,9 @@
},
"webassemblyModuleFilename": {
"$ref": "#/definitions/WebassemblyModuleFilename"
},
"workerChunkLoading": {
"$ref": "#/definitions/ChunkLoading"
}
}
},

View File

@ -296,6 +296,54 @@ const describeCases = config => {
moduleScope.window = globalContext;
moduleScope.self = globalContext;
moduleScope.URL = URL;
moduleScope.Worker = class Worker {
constructor(url, options) {
expect(url).toBeInstanceOf(URL);
expect(url.origin).toBe("https://test.cases");
expect(url.pathname.startsWith("/path/")).toBe(
true
);
const file = url.pathname.slice(6);
const workerBootstrap = `
const { parentPort } = require("worker_threads");
const { URL } = require("url");
const path = require("path");
global.self = global;
self.URL = URL;
self.importScripts = url => {
require(path.resolve(${JSON.stringify(outputDirectory)}, \`./\${url}\`));
};
parentPort.on("message", data => {
if(self.onmessage) self.onmessage({
data
});
});
self.postMessage = data => {
parentPort.postMessage(data);
};
require(${JSON.stringify(path.resolve(outputDirectory, file))});
`;
// eslint-disable-next-line node/no-unsupported-features/node-builtins
this.worker = new (require("worker_threads").Worker)(
workerBootstrap,
{
eval: true
}
);
}
set onmessage(value) {
this.worker.on("message", data => {
value({
data
});
});
}
postMessage(data) {
this.worker.postMessage(data);
}
};
runInNewContext = true;
}
if (testConfig.moduleScope) {

View File

@ -225,6 +225,7 @@ describe("Defaults", () => {
"ecmaVersion": 6,
"enabledChunkLoadingTypes": Array [
"jsonp",
"import-scripts",
],
"enabledLibraryTypes": Array [],
"filename": "[name].js",
@ -250,6 +251,7 @@ describe("Defaults", () => {
"strictModuleExceptionHandling": false,
"uniqueName": "webpack",
"webassemblyModuleFilename": "[hash].module.wasm",
"workerChunkLoading": "import-scripts",
},
"parallelism": 100,
"performance": false,
@ -983,11 +985,16 @@ describe("Defaults", () => {
+ "chunkLoading": "require",
@@ ... @@
- "jsonp",
- "import-scripts",
+ "require",
+ "require",
@@ ... @@
- "globalObject": "self",
+ "globalObject": "global",
@@ ... @@
- "workerChunkLoading": "import-scripts",
+ "workerChunkLoading": "require",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
@ -1067,7 +1074,9 @@ describe("Defaults", () => {
+ "chunkLoading": "import-scripts",
@@ ... @@
- "jsonp",
+ "import-scripts",
@@ ... @@
- "workerChunkLoading": "import-scripts",
+ "workerChunkLoading": false,
@@ ... @@
+ "worker",
@@ ... @@
@ -1095,11 +1104,16 @@ describe("Defaults", () => {
+ "chunkLoading": "require",
@@ ... @@
- "jsonp",
- "import-scripts",
+ "require",
+ "require",
@@ ... @@
- "globalObject": "self",
+ "globalObject": "global",
@@ ... @@
- "workerChunkLoading": "import-scripts",
+ "workerChunkLoading": "require",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],
@ -1183,11 +1197,16 @@ describe("Defaults", () => {
+ "chunkLoading": "require",
@@ ... @@
- "jsonp",
- "import-scripts",
+ "require",
+ "require",
@@ ... @@
- "globalObject": "self",
+ "globalObject": "global",
@@ ... @@
- "workerChunkLoading": "import-scripts",
+ "workerChunkLoading": "require",
@@ ... @@
- "aliasFields": Array [
- "browser",
- ],

View File

@ -72,10 +72,12 @@ describe("Stats", () => {
expect(
stats.toJson({
all: false,
errorsCount: true,
chunkGroups: true
})
).toMatchInlineSnapshot(`
Object {
"errorsCount": 0,
"namedChunkGroups": Object {
"entryA": Object {
"assets": Array [
@ -126,10 +128,12 @@ describe("Stats", () => {
expect(
stats.toJson({
all: false,
errorsCount: true,
chunkGroups: true
})
).toMatchInlineSnapshot(`
Object {
"errorsCount": 0,
"namedChunkGroups": Object {
"chunkB": Object {
"assets": Array [
@ -197,6 +201,7 @@ describe("Stats", () => {
expect(
stats.toJson({
all: false,
errorsCount: true,
assets: true
})
).toMatchInlineSnapshot(`
@ -271,6 +276,7 @@ describe("Stats", () => {
"entryB.js",
],
},
"errorsCount": 0,
"filteredAssets": undefined,
}
`);

View File

@ -2678,6 +2678,40 @@ Object {
"multiple": false,
"simpleType": "string",
},
"output-worker-chunk-loading": Object {
"configs": Array [
Object {
"description": "The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).",
"multiple": false,
"path": "output.workerChunkLoading",
"type": "enum",
"values": Array [
false,
],
},
Object {
"description": "The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).",
"multiple": false,
"path": "output.workerChunkLoading",
"type": "enum",
"values": Array [
"jsonp",
"import-scripts",
"require",
"async-node",
],
},
Object {
"description": "The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).",
"multiple": false,
"path": "output.workerChunkLoading",
"type": "string",
},
],
"description": "The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).",
"multiple": false,
"simpleType": "string",
},
"parallelism": Object {
"configs": Array [
Object {

View File

@ -2987,9 +2987,9 @@ webpack x.x.x compiled successfully in X ms"
exports[`StatsTestCases should print correct stats for split-chunks 1`] = `
"default:
Entrypoint main 9.93 KiB = default/main.js
Entrypoint a 10.5 KiB = default/a.js
Entrypoint b 2.26 KiB = default/b.js
Entrypoint c 2.26 KiB = default/c.js
Entrypoint a 10.2 KiB = default/a.js
Entrypoint b 1.98 KiB = default/b.js
Entrypoint c 1.98 KiB = default/c.js
chunk (runtime: b) default/b.js (b) 152 bytes [entry] [rendered]
> ./b b
dependent modules 80 bytes [dependent] 4 modules
@ -3259,7 +3259,7 @@ name-too-long:
custom-chunks-filter:
Entrypoint main 9.94 KiB = custom-chunks-filter/main.js
Entrypoint a 10.5 KiB = custom-chunks-filter/a.js
Entrypoint a 10.2 KiB = custom-chunks-filter/a.js
Entrypoint b 6.47 KiB = custom-chunks-filter/282.js 204 bytes custom-chunks-filter/954.js 204 bytes custom-chunks-filter/568.js 204 bytes custom-chunks-filter/767.js 204 bytes custom-chunks-filter/b.js 5.68 KiB
Entrypoint c 6.47 KiB = custom-chunks-filter/282.js 204 bytes custom-chunks-filter/769.js 204 bytes custom-chunks-filter/568.js 204 bytes custom-chunks-filter/767.js 204 bytes custom-chunks-filter/c.js 5.68 KiB
chunk (runtime: b) custom-chunks-filter/b.js (b) 72 bytes (javascript) 2.6 KiB (runtime) ={282}= ={568}= ={767}= ={954}= [entry] [rendered]

View File

@ -0,0 +1,34 @@
import { Worker } from "worker_threads";
it("should allow to create a WebWorker", async () => {
const worker = new Worker(new URL("./worker.js", import.meta.url), {
name: "MyWorker"
});
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.on("message", data => {
resolve(data);
});
});
expect(result).toBe("data: OK, thanks");
worker.terminate();
});
it("should allow to create another WebWorker", async () => {
const worker = new Worker(new URL("./worker.js", import.meta.url), {
name: "MyWorker"
});
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.on("message", data => {
resolve(data);
});
});
expect(result).toBe("data: OK, thanks");
worker.terminate();
});
it("should allow to share chunks", async () => {
const { upper } = await import("./module");
expect(upper("ok")).toBe("OK");
});

View File

@ -0,0 +1,3 @@
export function upper(str) {
return str.toUpperCase();
}

View File

@ -0,0 +1,5 @@
module.exports = {
findBundle: function(i, options) {
return ["main.js"];
}
};

View File

@ -0,0 +1,5 @@
module.exports = {
output: {
filename: "[name].js"
}
};

View File

@ -0,0 +1,6 @@
import { parentPort } from "worker_threads";
parentPort.on("message", async data => {
const { upper } = await import("./module");
parentPort.postMessage(`data: ${upper(data)}, thanks`);
});

View File

@ -0,0 +1,18 @@
import { Worker } from "worker_threads";
it("should allow to create a WebWorker", async () => {
const worker = new Worker(new URL("./worker.js", import.meta.url));
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.on("message", data => {
resolve(data);
});
});
expect(result).toBe("data: OK, thanks");
worker.terminate();
});
it("should allow to share chunks", async () => {
const { upper } = await import("./module");
expect(upper("ok")).toBe("OK");
});

View File

@ -0,0 +1,3 @@
export function upper(str) {
return str.toUpperCase();
}

View File

@ -0,0 +1,5 @@
module.exports = {
findBundle: function(i, options) {
return ["main.js"];
}
};

View File

@ -0,0 +1,5 @@
module.exports = {
output: {
filename: "[name].js"
}
};

View File

@ -0,0 +1,6 @@
import { parentPort } from "worker_threads";
parentPort.on("message", async data => {
const { upper } = await import("./module");
parentPort.postMessage(`data: ${upper(data)}, thanks`);
});

View File

@ -0,0 +1,23 @@
it("should allow to create a WebWorker", async () => {
const worker = new Worker(new URL("./worker.js", import.meta.url), {
type: "module"
});
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.onmessage = event => {
resolve(event.data);
};
});
expect(result).toBe("data: OK, thanks");
});
it("should allow to share chunks", async () => {
const promise = import("./module");
const script = document.head._children[0];
const src = script.src;
const file = src.slice(src.lastIndexOf("/"));
__non_webpack_require__(`./${file}`);
script.onload();
const { upper } = await promise;
expect(upper("ok")).toBe("OK");
});

View File

@ -0,0 +1,3 @@
export function upper(str) {
return str.toUpperCase();
}

View File

@ -0,0 +1,5 @@
module.exports = {
findBundle: function(i, options) {
return ["main.js"];
}
};

View File

@ -0,0 +1,6 @@
module.exports = {
output: {
filename: "[name].js"
},
target: "web"
};

View File

@ -0,0 +1,4 @@
onmessage = async event => {
const { upper } = await import("./module");
postMessage(`data: ${upper(event.data)}, thanks`);
};

54
types.d.ts vendored
View File

@ -262,12 +262,15 @@ declare abstract class AsyncDependenciesBlock extends DependenciesBlock {
preloadOrder?: number;
prefetchOrder?: number;
name?: string;
entryOptions?: { name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>;
};
loc: SyntheticDependencyLocation | RealDependencyLocation;
request: string;
parent: DependenciesBlock;
chunkName: string;
isAsync(parentChunkGroup: ChunkGroup): boolean;
module: any;
}
declare abstract class AsyncQueue<T, K, R> {
@ -617,6 +620,10 @@ declare class Chunk {
hasRuntime(): boolean;
canBeInitial(): boolean;
isOnlyInitial(): boolean;
getEntryOptions(): { name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>;
addGroup(chunkGroup: ChunkGroup): void;
removeGroup(chunkGroup: ChunkGroup): void;
isInGroup(chunkGroup: ChunkGroup): boolean;
@ -628,6 +635,7 @@ declare class Chunk {
getAllAsyncChunks(): Set<Chunk>;
getAllInitialChunks(): Set<Chunk>;
getAllReferencedChunks(): Set<Chunk>;
getAllReferencedAsyncEntrypoints(): Set<Entrypoint>;
hasAsyncChunks(): boolean;
getChildIdsByOrders(
chunkGraph: ChunkGraph,
@ -848,6 +856,8 @@ declare abstract class ChunkGroup {
hasParent(parent: ChunkGroup): boolean;
readonly parentsIterable: SortableSet<ChunkGroup>;
removeParent(chunkGroup: ChunkGroup): boolean;
addAsyncEntrypoint(entrypoint: Entrypoint): boolean;
readonly asyncEntrypointsIterable: SortableSet<ChunkGroup>;
getBlocks(): any[];
getNumberOfBlocks(): number;
hasBlock(block?: any): boolean;
@ -1049,7 +1059,7 @@ declare class Compilation {
addEntry: SyncHook<
[
Dependency,
{ name: string } & Pick<
{ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>
@ -1059,7 +1069,7 @@ declare class Compilation {
failedEntry: SyncHook<
[
Dependency,
{ name: string } & Pick<
{ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>,
@ -1070,7 +1080,7 @@ declare class Compilation {
succeedEntry: SyncHook<
[
Dependency,
{ name: string } & Pick<
{ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>,
@ -1227,6 +1237,7 @@ declare class Compilation {
entries: Map<string, EntryData>;
globalEntry: EntryData;
entrypoints: Map<string, Entrypoint>;
asyncEntrypoints: Entrypoint[];
chunks: Set<Chunk>;
chunkGroups: ChunkGroup[];
namedChunkGroups: Map<string, ChunkGroup>;
@ -1306,7 +1317,7 @@ declare class Compilation {
entry: Dependency,
optionsOrName:
| string
| ({ name: string } & Pick<
| ({ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>),
@ -1315,7 +1326,7 @@ declare class Compilation {
addInclude(
context: string,
dependency: Dependency,
options: { name: string } & Pick<
options: { name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>,
@ -1333,7 +1344,7 @@ declare class Compilation {
blocks: DependenciesBlock[]
): void;
codeGeneration(callback?: any): void;
processRuntimeRequirements(entrypoints: Iterable<Entrypoint>): void;
processRuntimeRequirements(): void;
addRuntimeModule(chunk: Chunk, module: RuntimeModule): void;
addChunkInGroup(
groupOptions:
@ -1343,6 +1354,15 @@ declare class Compilation {
loc: SyntheticDependencyLocation | RealDependencyLocation,
request: string
): ChunkGroup;
addAsyncEntrypoint(
options: { name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>,
module: Module,
loc: SyntheticDependencyLocation | RealDependencyLocation,
request: string
): Entrypoint;
/**
* This method first looks to see if a name is provided for a new chunk,
@ -2416,7 +2436,7 @@ declare interface EntryData {
/**
* options of the entrypoint
*/
options: { name: string } & Pick<
options: { name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>;
@ -2513,7 +2533,7 @@ declare class EntryPlugin {
entry: string,
options:
| string
| ({ name: string } & Pick<
| ({ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>)
@ -2522,7 +2542,7 @@ declare class EntryPlugin {
entry: string;
options:
| string
| ({ name: string } & Pick<
| ({ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>);
@ -2535,7 +2555,7 @@ declare class EntryPlugin {
entry: string,
options:
| string
| ({ name: string } & Pick<
| ({ name?: string } & Pick<
EntryDescriptionNormalized,
"filename" | "chunkLoading" | "dependOn" | "library" | "runtime"
>)
@ -3624,7 +3644,7 @@ declare class JavascriptParser extends Parser {
>
>;
optionalChaining: SyncBailHook<[ChainExpression], boolean | void>;
new: HookMap<SyncBailHook<[Expression], boolean | void>>;
new: HookMap<SyncBailHook<[NewExpression], boolean | void>>;
expression: HookMap<SyncBailHook<[Expression], boolean | void>>;
expressionMemberChain: HookMap<
SyncBailHook<[Expression, string[]], boolean | void>
@ -5781,6 +5801,11 @@ declare interface Output {
* The filename of WebAssembly modules as relative path inside the `output.path` directory.
*/
webassemblyModuleFilename?: string;
/**
* The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).
*/
workerChunkLoading?: DevTool;
}
declare interface OutputFileSystem {
writeFile: (
@ -5990,6 +6015,11 @@ declare interface OutputNormalized {
* The filename of WebAssembly modules as relative path inside the `output.path` directory.
*/
webassemblyModuleFilename?: string;
/**
* The method of loading chunks (methods included by default are 'jsonp' (web), 'importScripts' (WebWorker), 'require' (sync node.js), 'async-node' (async node.js), but others might be added by plugins).
*/
workerChunkLoading?: DevTool;
}
declare interface ParsedIdentifier {
request: string;