mirror of https://github.com/webpack/webpack.git
added WebAssembly Proof of Concept
This commit is contained in:
parent
b7c746d73f
commit
41a1d602e1
|
@ -0,0 +1,507 @@
|
|||
This very simple example shows usage of WebAssembly.
|
||||
|
||||
WebAssembly modules can be imported like other modules. Their download and compilation happens in parallel to the download and evaluation of the javascript chunk.
|
||||
|
||||
# example.js
|
||||
|
||||
``` javascript
|
||||
import("./add.wasm").then(addModule => {
|
||||
console.log(addModule.add(22, 2200));
|
||||
import("./math").then(math => {
|
||||
console.log(math.add(10, 101));
|
||||
console.log(math.factorial(15));
|
||||
console.log(math.factorialJavascript(15));
|
||||
console.log(math.fibonacci(15));
|
||||
console.log(math.fibonacciJavascript(15));
|
||||
timed("wasm factorial", () => math.factorial(1500));
|
||||
timed("js factorial", () => math.factorialJavascript(1500));
|
||||
timed("wasm fibonacci", () => math.fibonacci(22));
|
||||
timed("js fibonacci", () => math.fibonacciJavascript(22));
|
||||
});
|
||||
});
|
||||
|
||||
function timed(name, fn) {
|
||||
if(!console.time || !console.timeEnd)
|
||||
return fn();
|
||||
// warmup
|
||||
for(var i = 0; i < 10; i++)
|
||||
fn();
|
||||
console.time(name)
|
||||
for(var i = 0; i < 5000; i++)
|
||||
fn();
|
||||
console.timeEnd(name)
|
||||
}
|
||||
```
|
||||
|
||||
# math.js
|
||||
|
||||
``` javascript
|
||||
import { add } from "./add.wasm";
|
||||
import { factorial } from "./factorial.wasm";
|
||||
import { fibonacci } from "./fibonacci.wasm";
|
||||
|
||||
export { add, factorial, fibonacci };
|
||||
|
||||
export function factorialJavascript(i) {
|
||||
if(i < 1) return 1;
|
||||
return i * factorialJavascript(i - 1);
|
||||
}
|
||||
|
||||
export function fibonacciJavascript(i) {
|
||||
if(i < 2) return 1;
|
||||
return fibonacciJavascript(i - 1) + fibonacciJavascript(i - 2);
|
||||
}
|
||||
```
|
||||
|
||||
# js/output.js
|
||||
|
||||
<details><summary><code>/******/ (function(modules) { /* webpackBootstrap */ })</code></summary>
|
||||
|
||||
``` javascript
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // install a JSONP callback for chunk loading
|
||||
/******/ function webpackJsonpCallback(data) {
|
||||
/******/ var chunkIds = data[0], moreModules = data[1], executeModules = data[2];
|
||||
/******/ // add "moreModules" to the modules object,
|
||||
/******/ // then flag all "chunkIds" as loaded and fire callback
|
||||
/******/ var moduleId, chunkId, i = 0, resolves = [], result;
|
||||
/******/ for(;i < chunkIds.length; i++) {
|
||||
/******/ chunkId = chunkIds[i];
|
||||
/******/ if(installedChunks[chunkId]) {
|
||||
/******/ resolves.push(installedChunks[chunkId][0]);
|
||||
/******/ }
|
||||
/******/ installedChunks[chunkId] = 0;
|
||||
/******/ }
|
||||
/******/ for(moduleId in moreModules) {
|
||||
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
|
||||
/******/ modules[moduleId] = moreModules[moduleId];
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
|
||||
/******/ while(resolves.length) {
|
||||
/******/ resolves.shift()();
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // object to store loaded and loading chunks
|
||||
/******/ var installedChunks = {
|
||||
/******/ 2: 0
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ var scheduledModules = [];
|
||||
/******/
|
||||
/******/ // object to store loaded and loading wasm modules
|
||||
/******/ var installedWasmModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // This file contains only the entry chunk.
|
||||
/******/ // The chunk loading function for additional chunks
|
||||
/******/ __webpack_require__.e = function requireEnsure(chunkId) {
|
||||
/******/ var promises = [];
|
||||
/******/
|
||||
/******/
|
||||
/******/ // JSONP chunk loading for javascript
|
||||
/******/
|
||||
/******/ var installedChunkData = installedChunks[chunkId];
|
||||
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
|
||||
/******/
|
||||
/******/ // a Promise means "currently loading".
|
||||
/******/ if(installedChunkData) {
|
||||
/******/ promises.push(installedChunkData[2]);
|
||||
/******/ } else {
|
||||
/******/ // setup Promise in chunk cache
|
||||
/******/ var promise = new Promise(function(resolve, reject) {
|
||||
/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
|
||||
/******/ installedChunkData[2] = promise;
|
||||
/******/ });
|
||||
/******/
|
||||
/******/ // start chunk loading
|
||||
/******/ var head = document.getElementsByTagName('head')[0];
|
||||
/******/ var script = document.createElement('script');
|
||||
/******/ script.charset = 'utf-8';
|
||||
/******/ script.timeout = 120000;
|
||||
/******/
|
||||
/******/ if (__webpack_require__.nc) {
|
||||
/******/ script.setAttribute("nonce", __webpack_require__.nc);
|
||||
/******/ }
|
||||
/******/ script.src = __webpack_require__.p + "" + chunkId + ".output.js";
|
||||
/******/ var timeout = setTimeout(function(){
|
||||
/******/ onScriptComplete({ type: 'timeout', target: script });
|
||||
/******/ }, 120000);
|
||||
/******/ script.onerror = script.onload = onScriptComplete;
|
||||
/******/ function onScriptComplete(event) {
|
||||
/******/ // avoid mem leaks in IE.
|
||||
/******/ script.onerror = script.onload = null;
|
||||
/******/ clearTimeout(timeout);
|
||||
/******/ var chunk = installedChunks[chunkId];
|
||||
/******/ if(chunk !== 0) {
|
||||
/******/ if(chunk) {
|
||||
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
|
||||
/******/ var realSrc = event && event.target && event.target.src;
|
||||
/******/ var error = new Error('Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')');
|
||||
/******/ error.type = errorType;
|
||||
/******/ error.request = realSrc;
|
||||
/******/ chunk[1](error);
|
||||
/******/ }
|
||||
/******/ installedChunks[chunkId] = undefined;
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/ head.appendChild(script);
|
||||
/******/ promises.push(promise);
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // Fetch + compile chunk loading for webassembly
|
||||
/******/
|
||||
/******/ var wasmModules = {"0":[1,3,4],"1":[1]}[chunkId] || [];
|
||||
/******/
|
||||
/******/ wasmModules.forEach(function(wasmModuleId) {
|
||||
/******/ var installedWasmModuleData = installedWasmModules[wasmModuleId];
|
||||
/******/
|
||||
/******/ // a Promise means "currently loading" or "already loaded".
|
||||
/******/ if(installedWasmModuleData) {
|
||||
/******/ promises.push(installedWasmModuleData);
|
||||
/******/ } else {
|
||||
/******/ var promise = installedWasmModules[wasmModuleId] = fetch(__webpack_require__.p + "" + {"1":"80925f35a6f1cf550d38","3":"419044e1f2fdca0c3e4a","4":"353120d3a2f1efbb00e2"}[wasmModuleId] + ".wasm")
|
||||
/******/ .then(function(response) { return response.arrayBuffer(); })
|
||||
/******/ .then(function(bytes) { return WebAssembly.compile(bytes); })
|
||||
/******/ .then(function(module) { __webpack_require__.w[wasmModuleId] = module; })
|
||||
/******/ promises.push(promise);
|
||||
/******/ }
|
||||
/******/ });
|
||||
/******/ return Promise.all(promises);
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, {
|
||||
/******/ configurable: false,
|
||||
/******/ enumerable: true,
|
||||
/******/ get: getter
|
||||
/******/ });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "js/";
|
||||
/******/
|
||||
/******/ // on error function for async loading
|
||||
/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
|
||||
/******/
|
||||
/******/ // object with all compiled WebAssmbly.Modules
|
||||
/******/ __webpack_require__.w = {};
|
||||
/******/
|
||||
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
|
||||
/******/ var parentJsonpFunction = jsonpArray.push.bind(jsonpArray);
|
||||
/******/ jsonpArray.push = webpackJsonpCallback;
|
||||
/******/ jsonpArray = jsonpArray.slice();
|
||||
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
``` javascript
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/*!********************!*\
|
||||
!*** ./example.js ***!
|
||||
\********************/
|
||||
/*! no static exports found */
|
||||
/*! all exports used */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
__webpack_require__.e/* import() */(1).then(__webpack_require__.bind(null, /*! ./add.wasm */1)).then(addModule => {
|
||||
console.log(addModule.add(22, 2200));
|
||||
__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, /*! ./math */2)).then(math => {
|
||||
console.log(math.add(10, 101));
|
||||
console.log(math.factorial(15));
|
||||
console.log(math.factorialJavascript(15));
|
||||
console.log(math.fibonacci(15));
|
||||
console.log(math.fibonacciJavascript(15));
|
||||
timed("wasm factorial", () => math.factorial(1500));
|
||||
timed("js factorial", () => math.factorialJavascript(1500));
|
||||
timed("wasm fibonacci", () => math.fibonacci(22));
|
||||
timed("js fibonacci", () => math.fibonacciJavascript(22));
|
||||
});
|
||||
});
|
||||
|
||||
function timed(name, fn) {
|
||||
if(!console.time || !console.timeEnd)
|
||||
return fn();
|
||||
// warmup
|
||||
for(var i = 0; i < 10; i++)
|
||||
fn();
|
||||
console.time(name)
|
||||
for(var i = 0; i < 5000; i++)
|
||||
fn();
|
||||
console.timeEnd(name)
|
||||
}
|
||||
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
||||
```
|
||||
|
||||
# js/0.output.js
|
||||
|
||||
``` javascript
|
||||
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0,1],[
|
||||
/* 0 */,
|
||||
/* 1 */
|
||||
/*!******************!*\
|
||||
!*** ./add.wasm ***!
|
||||
\******************/
|
||||
/*! no static exports found */
|
||||
/*! all exports used */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// Instanciate WebAssembly module
|
||||
var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});
|
||||
|
||||
// export exports from WebAssmbly module
|
||||
module.exports = instance.exports;
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/*!*****************!*\
|
||||
!*** ./math.js ***!
|
||||
\*****************/
|
||||
/*! exports provided: add, factorial, fibonacci, factorialJavascript, fibonacciJavascript */
|
||||
/*! all exports used */
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "factorialJavascript", function() { return factorialJavascript; });
|
||||
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fibonacciJavascript", function() { return fibonacciJavascript; });
|
||||
/* harmony import */ var _add_wasm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./add.wasm */1);
|
||||
/* harmony import */ var _add_wasm__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_add_wasm__WEBPACK_IMPORTED_MODULE_0__);
|
||||
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "add", function() { return _add_wasm__WEBPACK_IMPORTED_MODULE_0__["add"]; });
|
||||
|
||||
/* harmony import */ var _factorial_wasm__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./factorial.wasm */3);
|
||||
/* harmony import */ var _factorial_wasm__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_factorial_wasm__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "factorial", function() { return _factorial_wasm__WEBPACK_IMPORTED_MODULE_1__["factorial"]; });
|
||||
|
||||
/* harmony import */ var _fibonacci_wasm__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./fibonacci.wasm */4);
|
||||
/* harmony import */ var _fibonacci_wasm__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_fibonacci_wasm__WEBPACK_IMPORTED_MODULE_2__);
|
||||
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "fibonacci", function() { return _fibonacci_wasm__WEBPACK_IMPORTED_MODULE_2__["fibonacci"]; });
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function factorialJavascript(i) {
|
||||
if(i < 1) return 1;
|
||||
return i * factorialJavascript(i - 1);
|
||||
}
|
||||
|
||||
function fibonacciJavascript(i) {
|
||||
if(i < 2) return 1;
|
||||
return fibonacciJavascript(i - 1) + fibonacciJavascript(i - 2);
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 3 */
|
||||
/*!************************!*\
|
||||
!*** ./factorial.wasm ***!
|
||||
\************************/
|
||||
/*! no static exports found */
|
||||
/*! exports used: factorial */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// Instanciate WebAssembly module
|
||||
var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});
|
||||
|
||||
// export exports from WebAssmbly module
|
||||
module.exports = instance.exports;
|
||||
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/*!************************!*\
|
||||
!*** ./fibonacci.wasm ***!
|
||||
\************************/
|
||||
/*! no static exports found */
|
||||
/*! exports used: fibonacci */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// Instanciate WebAssembly module
|
||||
var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});
|
||||
|
||||
// export exports from WebAssmbly module
|
||||
module.exports = instance.exports;
|
||||
|
||||
/***/ })
|
||||
]]);
|
||||
```
|
||||
|
||||
# js/1.output.js
|
||||
|
||||
``` javascript
|
||||
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
|
||||
/* 0 */,
|
||||
/* 1 */
|
||||
/*!******************!*\
|
||||
!*** ./add.wasm ***!
|
||||
\******************/
|
||||
/*! no static exports found */
|
||||
/*! all exports used */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// Instanciate WebAssembly module
|
||||
var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});
|
||||
|
||||
// export exports from WebAssmbly module
|
||||
module.exports = instance.exports;
|
||||
|
||||
/***/ })
|
||||
]]);
|
||||
```
|
||||
|
||||
# Info
|
||||
|
||||
## Uncompressed
|
||||
|
||||
```
|
||||
Hash: 0878b6979a580265faba
|
||||
Version: webpack 3.8.1
|
||||
Asset Size Chunks Chunk Names
|
||||
0.output.js 3.55 kB 0, 1 [emitted]
|
||||
80925f35a6f1cf550d38.wasm 41 bytes 0, 1, 1 [emitted]
|
||||
419044e1f2fdca0c3e4a.wasm 83 bytes 0, 1 [emitted]
|
||||
353120d3a2f1efbb00e2.wasm 95 bytes 0, 1 [emitted]
|
||||
1.output.js 486 bytes 1 [emitted]
|
||||
output.js 8.9 kB 2 [emitted] main
|
||||
Entrypoint main = output.js
|
||||
chunk {0} 0.output.js, 80925f35a6f1cf550d38.wasm, 419044e1f2fdca0c3e4a.wasm, 353120d3a2f1efbb00e2.wasm 634 bytes {2} [rendered]
|
||||
> [0] ./example.js 3:1-17
|
||||
[1] ./add.wasm 41 bytes {0} {1} [built]
|
||||
import() ./add.wasm [0] ./example.js 1:0-20
|
||||
harmony side effect evaluation ./add.wasm [2] ./math.js 1:0-33
|
||||
harmony export imported specifier ./add.wasm [2] ./math.js 5:0-37
|
||||
[2] ./math.js 415 bytes {0} [built]
|
||||
[exports: add, factorial, fibonacci, factorialJavascript, fibonacciJavascript]
|
||||
import() ./math [0] ./example.js 3:1-17
|
||||
[3] ./factorial.wasm 83 bytes {0} [built]
|
||||
[only some exports used: factorial]
|
||||
harmony side effect evaluation ./factorial.wasm [2] ./math.js 2:0-45
|
||||
harmony export imported specifier ./factorial.wasm [2] ./math.js 5:0-37
|
||||
[4] ./fibonacci.wasm 95 bytes {0} [built]
|
||||
[only some exports used: fibonacci]
|
||||
harmony side effect evaluation ./fibonacci.wasm [2] ./math.js 3:0-45
|
||||
harmony export imported specifier ./fibonacci.wasm [2] ./math.js 5:0-37
|
||||
chunk {1} 1.output.js, 80925f35a6f1cf550d38.wasm 41 bytes {2} [rendered]
|
||||
> [0] ./example.js 1:0-20
|
||||
[1] ./add.wasm 41 bytes {0} {1} [built]
|
||||
import() ./add.wasm [0] ./example.js 1:0-20
|
||||
harmony side effect evaluation ./add.wasm [2] ./math.js 1:0-33
|
||||
harmony export imported specifier ./add.wasm [2] ./math.js 5:0-37
|
||||
chunk {2} output.js (main) 788 bytes [entry] [rendered]
|
||||
> main [0] ./example.js
|
||||
[0] ./example.js 788 bytes {2} [built]
|
||||
```
|
||||
|
||||
## Minimized (uglify-js, no zip)
|
||||
|
||||
```
|
||||
Hash: 0878b6979a580265faba
|
||||
Version: webpack 3.8.1
|
||||
Asset Size Chunks Chunk Names
|
||||
0.output.js 772 bytes 0, 1 [emitted]
|
||||
80925f35a6f1cf550d38.wasm 41 bytes 0, 1, 1 [emitted]
|
||||
419044e1f2fdca0c3e4a.wasm 83 bytes 0, 1 [emitted]
|
||||
353120d3a2f1efbb00e2.wasm 95 bytes 0, 1 [emitted]
|
||||
1.output.js 155 bytes 1 [emitted]
|
||||
output.js 8.74 kB 2 [emitted] main
|
||||
Entrypoint main = output.js
|
||||
chunk {0} 0.output.js, 80925f35a6f1cf550d38.wasm, 419044e1f2fdca0c3e4a.wasm, 353120d3a2f1efbb00e2.wasm 634 bytes {2} [rendered]
|
||||
> [0] ./example.js 3:1-17
|
||||
[1] ./add.wasm 41 bytes {0} {1} [built]
|
||||
import() ./add.wasm [0] ./example.js 1:0-20
|
||||
harmony side effect evaluation ./add.wasm [2] ./math.js 1:0-33
|
||||
harmony export imported specifier ./add.wasm [2] ./math.js 5:0-37
|
||||
[2] ./math.js 415 bytes {0} [built]
|
||||
[exports: add, factorial, fibonacci, factorialJavascript, fibonacciJavascript]
|
||||
import() ./math [0] ./example.js 3:1-17
|
||||
[3] ./factorial.wasm 83 bytes {0} [built]
|
||||
[only some exports used: factorial]
|
||||
harmony side effect evaluation ./factorial.wasm [2] ./math.js 2:0-45
|
||||
harmony export imported specifier ./factorial.wasm [2] ./math.js 5:0-37
|
||||
[4] ./fibonacci.wasm 95 bytes {0} [built]
|
||||
[only some exports used: fibonacci]
|
||||
harmony side effect evaluation ./fibonacci.wasm [2] ./math.js 3:0-45
|
||||
harmony export imported specifier ./fibonacci.wasm [2] ./math.js 5:0-37
|
||||
chunk {1} 1.output.js, 80925f35a6f1cf550d38.wasm 41 bytes {2} [rendered]
|
||||
> [0] ./example.js 1:0-20
|
||||
[1] ./add.wasm 41 bytes {0} {1} [built]
|
||||
import() ./add.wasm [0] ./example.js 1:0-20
|
||||
harmony side effect evaluation ./add.wasm [2] ./math.js 1:0-33
|
||||
harmony export imported specifier ./add.wasm [2] ./math.js 5:0-37
|
||||
chunk {2} output.js (main) 788 bytes [entry] [rendered]
|
||||
> main [0] ./example.js
|
||||
[0] ./example.js 788 bytes {2} [built]
|
||||
|
||||
ERROR in output.js from UglifyJs
|
||||
Unexpected token: operator (>) [output.js:194,95]
|
||||
```
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
require("../build-common");
|
|
@ -0,0 +1,26 @@
|
|||
import("./add.wasm").then(addModule => {
|
||||
console.log(addModule.add(22, 2200));
|
||||
import("./math").then(math => {
|
||||
console.log(math.add(10, 101));
|
||||
console.log(math.factorial(15));
|
||||
console.log(math.factorialJavascript(15));
|
||||
console.log(math.fibonacci(15));
|
||||
console.log(math.fibonacciJavascript(15));
|
||||
timed("wasm factorial", () => math.factorial(1500));
|
||||
timed("js factorial", () => math.factorialJavascript(1500));
|
||||
timed("wasm fibonacci", () => math.fibonacci(22));
|
||||
timed("js fibonacci", () => math.fibonacciJavascript(22));
|
||||
});
|
||||
});
|
||||
|
||||
function timed(name, fn) {
|
||||
if(!console.time || !console.timeEnd)
|
||||
return fn();
|
||||
// warmup
|
||||
for(var i = 0; i < 10; i++)
|
||||
fn();
|
||||
console.time(name)
|
||||
for(var i = 0; i < 5000; i++)
|
||||
fn();
|
||||
console.timeEnd(name)
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<script src="js/output.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
import { add } from "./add.wasm";
|
||||
import { factorial } from "./factorial.wasm";
|
||||
import { fibonacci } from "./fibonacci.wasm";
|
||||
|
||||
export { add, factorial, fibonacci };
|
||||
|
||||
export function factorialJavascript(i) {
|
||||
if(i < 1) return 1;
|
||||
return i * factorialJavascript(i - 1);
|
||||
}
|
||||
|
||||
export function fibonacciJavascript(i) {
|
||||
if(i < 2) return 1;
|
||||
return fibonacciJavascript(i - 1) + fibonacciJavascript(i - 2);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
This very simple example shows usage of WebAssembly.
|
||||
|
||||
WebAssembly modules can be imported like other modules. Their download and compilation happens in parallel to the download and evaluation of the javascript chunk.
|
||||
|
||||
# example.js
|
||||
|
||||
``` javascript
|
||||
{{example.js}}
|
||||
```
|
||||
|
||||
# math.js
|
||||
|
||||
``` javascript
|
||||
{{math.js}}
|
||||
```
|
||||
|
||||
# js/output.js
|
||||
|
||||
``` javascript
|
||||
{{js/output.js}}
|
||||
```
|
||||
|
||||
# js/0.output.js
|
||||
|
||||
``` javascript
|
||||
{{js/0.output.js}}
|
||||
```
|
||||
|
||||
# js/1.output.js
|
||||
|
||||
``` javascript
|
||||
{{js/1.output.js}}
|
||||
```
|
||||
|
||||
# Info
|
||||
|
||||
## Uncompressed
|
||||
|
||||
```
|
||||
{{stdout}}
|
||||
```
|
||||
|
||||
## Minimized (uglify-js, no zip)
|
||||
|
||||
```
|
||||
{{min:stdout}}
|
||||
```
|
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
output: {
|
||||
webassemblyModuleFilename: "[modulehash].wasm",
|
||||
publicPath: "js/"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.wasm$/,
|
||||
type: "webassembly/experimental"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
35
lib/Chunk.js
35
lib/Chunk.js
|
@ -490,7 +490,7 @@ class Chunk {
|
|||
hash.update(`${this.id} `);
|
||||
hash.update(this.ids ? this.ids.join(",") : "");
|
||||
hash.update(`${this.name || ""} `);
|
||||
this._modules.forEach(m => m.updateHash(hash));
|
||||
this._modules.forEach(m => hash.update(m.hash));
|
||||
}
|
||||
|
||||
canBeIntegrated(otherChunk) {
|
||||
|
@ -538,12 +538,12 @@ class Chunk {
|
|||
}
|
||||
|
||||
getChunkMaps(includeEntries, realHash) {
|
||||
const chunksProcessed = [];
|
||||
const chunkHashMap = {};
|
||||
const chunkNameMap = {};
|
||||
const chunksProcessed = new Set();
|
||||
const chunkHashMap = Object.create(null);
|
||||
const chunkNameMap = Object.create(null);
|
||||
const addChunk = chunk => {
|
||||
if(chunksProcessed.indexOf(chunk) >= 0) return;
|
||||
chunksProcessed.push(chunk);
|
||||
if(chunksProcessed.has(chunk)) return;
|
||||
chunksProcessed.add(chunk);
|
||||
if(!chunk.hasRuntime() || includeEntries) {
|
||||
chunkHashMap[chunk.id] = realHash ? chunk.hash : chunk.renderedHash;
|
||||
if(chunk.name)
|
||||
|
@ -558,6 +558,29 @@ class Chunk {
|
|||
};
|
||||
}
|
||||
|
||||
getChunkModuleMaps(includeEntries, filterFn) {
|
||||
const chunksProcessed = new Set();
|
||||
const chunkModuleIdMap = Object.create(null);
|
||||
const chunkModuleHashMap = Object.create(null);
|
||||
(function addChunk(chunk) {
|
||||
if(chunksProcessed.has(chunk)) return;
|
||||
chunksProcessed.add(chunk);
|
||||
if(!chunk.hasRuntime() || includeEntries) {
|
||||
const array = chunk.getModules().filter(filterFn);
|
||||
if(array.length > 0)
|
||||
chunkModuleIdMap[chunk.id] = array.map(m => m.id).sort();
|
||||
for(const m of array) {
|
||||
chunkModuleHashMap[m.id] = m.renderedHash;
|
||||
}
|
||||
}
|
||||
chunk._chunks.forEach(addChunk);
|
||||
}(this));
|
||||
return {
|
||||
id: chunkModuleIdMap,
|
||||
hash: chunkModuleHashMap
|
||||
};
|
||||
}
|
||||
|
||||
sortModules(sortByFn) {
|
||||
this._modules.sortWith(sortByFn || sortById);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,53 @@ module.exports = class ChunkTemplate extends Template {
|
|||
super(outputOptions);
|
||||
}
|
||||
|
||||
render(chunk, moduleTemplate, dependencyTemplates) {
|
||||
const moduleSources = this.renderChunkModules(chunk, moduleTemplate, dependencyTemplates);
|
||||
getRenderManifest(options) {
|
||||
const chunk = options.chunk;
|
||||
const outputOptions = options.outputOptions;
|
||||
const moduleTemplates = options.moduleTemplates;
|
||||
const dependencyTemplates = options.dependencyTemplates;
|
||||
|
||||
const result = [];
|
||||
|
||||
let filenameTemplate;
|
||||
if(chunk.isInitial()) {
|
||||
if(chunk.filenameTemplate)
|
||||
filenameTemplate = chunk.filenameTemplate;
|
||||
else
|
||||
filenameTemplate = outputOptions.filename;
|
||||
} else {
|
||||
filenameTemplate = outputOptions.chunkFilename;
|
||||
}
|
||||
|
||||
result.push({
|
||||
render: () => this.renderJavascript(chunk, moduleTemplates.javascript, dependencyTemplates),
|
||||
filenameTemplate,
|
||||
pathOptions: {
|
||||
chunk
|
||||
},
|
||||
identifier: `chunk${chunk.id}`,
|
||||
hash: chunk.hash
|
||||
});
|
||||
|
||||
for(const module of chunk.modules.filter(m => m.type && m.type.startsWith("webassembly"))) {
|
||||
const filenameTemplate = outputOptions.webassemblyModuleFilename;
|
||||
|
||||
result.push({
|
||||
render: () => this.renderWebAssembly(module, moduleTemplates.webassembly, dependencyTemplates),
|
||||
filenameTemplate,
|
||||
pathOptions: {
|
||||
module
|
||||
},
|
||||
identifier: `webassemblyModule${module.id}`,
|
||||
hash: module.hash
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
renderJavascript(chunk, moduleTemplate, dependencyTemplates) {
|
||||
const moduleSources = this.renderChunkModules(chunk, m => true, moduleTemplate, dependencyTemplates);
|
||||
const core = this.applyPluginsWaterfall("modules", moduleSources, chunk, moduleTemplate, dependencyTemplates);
|
||||
let source = this.applyPluginsWaterfall("render", core, chunk, moduleTemplate, dependencyTemplates);
|
||||
if(chunk.hasEntryModule()) {
|
||||
|
@ -23,6 +68,10 @@ module.exports = class ChunkTemplate extends Template {
|
|||
return new ConcatSource(source, ";");
|
||||
}
|
||||
|
||||
renderWebAssembly(module, moduleTemplate, dependencyTemplates) {
|
||||
return moduleTemplate.render(module, dependencyTemplates);
|
||||
}
|
||||
|
||||
updateHash(hash) {
|
||||
hash.update("ChunkTemplate");
|
||||
hash.update("2");
|
||||
|
|
|
@ -70,7 +70,10 @@ class Compilation extends Tapable {
|
|||
this.mainTemplate = new MainTemplate(this.outputOptions);
|
||||
this.chunkTemplate = new ChunkTemplate(this.outputOptions);
|
||||
this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(this.outputOptions);
|
||||
this.moduleTemplate = new ModuleTemplate(this.outputOptions);
|
||||
this.moduleTemplates = {
|
||||
javascript: new ModuleTemplate(this.outputOptions),
|
||||
webassembly: new ModuleTemplate(this.outputOptions)
|
||||
};
|
||||
|
||||
this.semaphore = new Semaphore(options.parallelism || 100);
|
||||
|
||||
|
@ -99,6 +102,15 @@ class Compilation extends Tapable {
|
|||
this._rebuildingModules = new Map();
|
||||
}
|
||||
|
||||
// TODO add deprecation getter/setters
|
||||
get moduleTemplate() {
|
||||
return this.moduleTemplates.javascript;
|
||||
}
|
||||
|
||||
set moduleTemplate(value) {
|
||||
this.moduleTemplate.javascript = value;
|
||||
}
|
||||
|
||||
getStats() {
|
||||
return new Stats(this);
|
||||
}
|
||||
|
@ -1346,6 +1358,14 @@ class Compilation extends Tapable {
|
|||
this.children.forEach(child => hash.update(child.hash));
|
||||
this.warnings.forEach(warning => hash.update(`${warning.message}`));
|
||||
this.errors.forEach(error => hash.update(`${error.message}`));
|
||||
const modules = this.modules;
|
||||
for(let i = 0; i < modules.length; i++) {
|
||||
const module = modules[i];
|
||||
const moduleHash = crypto.createHash(hashFunction);
|
||||
module.updateHash(moduleHash);
|
||||
module.hash = moduleHash.digest(hashDigest);
|
||||
module.renderedHash = module.hash.substr(0, hashDigestLength);
|
||||
}
|
||||
// clone needed as sort below is inplace mutation
|
||||
const chunks = this.chunks.slice();
|
||||
/**
|
||||
|
@ -1407,29 +1427,30 @@ class Compilation extends Tapable {
|
|||
|
||||
createChunkAssets() {
|
||||
const outputOptions = this.outputOptions;
|
||||
const filename = outputOptions.filename;
|
||||
const chunkFilename = outputOptions.chunkFilename;
|
||||
for(let i = 0; i < this.chunks.length; i++) {
|
||||
const chunk = this.chunks[i];
|
||||
chunk.files = [];
|
||||
const chunkHash = chunk.hash;
|
||||
let source;
|
||||
let file;
|
||||
const filenameTemplate = chunk.filenameTemplate ? chunk.filenameTemplate :
|
||||
chunk.isInitial() ? filename :
|
||||
chunkFilename;
|
||||
let filenameTemplate;
|
||||
try {
|
||||
const useChunkHash = !chunk.hasRuntime() || (this.mainTemplate.useChunkHash && this.mainTemplate.useChunkHash(chunk));
|
||||
const usedHash = useChunkHash ? chunkHash : this.fullHash;
|
||||
const cacheName = "c" + chunk.id;
|
||||
const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate;
|
||||
const manifest = template.getRenderManifest({
|
||||
chunk,
|
||||
hash: this.hash,
|
||||
fullHash: this.fullHash,
|
||||
outputOptions,
|
||||
moduleTemplates: this.moduleTemplates,
|
||||
dependencyTemplates: this.dependencyTemplates
|
||||
}); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
|
||||
for(const fileManifest of manifest) {
|
||||
const cacheName = fileManifest.identifier;
|
||||
const usedHash = fileManifest.hash;
|
||||
filenameTemplate = fileManifest.filenameTemplate;
|
||||
if(this.cache && this.cache[cacheName] && this.cache[cacheName].hash === usedHash) {
|
||||
source = this.cache[cacheName].source;
|
||||
} else {
|
||||
if(chunk.hasRuntime()) {
|
||||
source = this.mainTemplate.render(this.hash, chunk, this.moduleTemplate, this.dependencyTemplates);
|
||||
} else {
|
||||
source = this.chunkTemplate.render(chunk, this.moduleTemplate, this.dependencyTemplates);
|
||||
}
|
||||
source = fileManifest.render();
|
||||
if(this.cache) {
|
||||
this.cache[cacheName] = {
|
||||
hash: usedHash,
|
||||
|
@ -1437,15 +1458,13 @@ class Compilation extends Tapable {
|
|||
};
|
||||
}
|
||||
}
|
||||
file = this.getPath(filenameTemplate, {
|
||||
noChunkHash: !useChunkHash,
|
||||
chunk
|
||||
});
|
||||
if(this.assets[file])
|
||||
file = this.getPath(filenameTemplate, fileManifest.pathOptions);
|
||||
if(this.assets[file] && this.assets[file] !== source)
|
||||
throw new Error(`Conflict: Multiple assets emit to the same filename ${file}`);
|
||||
this.assets[file] = source;
|
||||
chunk.files.push(file);
|
||||
this.applyPlugins2("chunk-asset", chunk, file);
|
||||
}
|
||||
} catch(err) {
|
||||
this.errors.push(new ChunkRenderError(chunk, file || filenameTemplate, err));
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class ContextModule extends Module {
|
|||
// resolveDependencies: (fs: FS, options: ContextOptions, (err: Error?, dependencies: Dependency[]) => void) => void
|
||||
// options: ContextOptions
|
||||
constructor(resolveDependencies, options) {
|
||||
super();
|
||||
super("javascript/dynamic");
|
||||
|
||||
// Info from Factory
|
||||
this.resolveDependencies = resolveDependencies;
|
||||
|
|
|
@ -13,7 +13,7 @@ const DelegatedExportsDependency = require("./dependencies/DelegatedExportsDepen
|
|||
|
||||
class DelegatedModule extends Module {
|
||||
constructor(sourceRequest, data, type, userRequest, originalRequest) {
|
||||
super();
|
||||
super("javascript/dynamic");
|
||||
|
||||
// Info from Factory
|
||||
this.sourceRequest = sourceRequest;
|
||||
|
|
|
@ -9,7 +9,7 @@ const RawSource = require("webpack-sources").RawSource;
|
|||
|
||||
class DllModule extends Module {
|
||||
constructor(context, dependencies, name, type) {
|
||||
super();
|
||||
super("javascript/dynamic");
|
||||
|
||||
// Info from Factory
|
||||
this.context = context;
|
||||
|
|
|
@ -11,7 +11,7 @@ const Template = require("./Template");
|
|||
|
||||
class ExternalModule extends Module {
|
||||
constructor(request, type, userRequest) {
|
||||
super();
|
||||
super("javascript/dynamic");
|
||||
|
||||
// Info from Factory
|
||||
this.request = request;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
class FetchCompileWasmMainTemplatePlugin {
|
||||
|
||||
apply(mainTemplate) {
|
||||
mainTemplate.plugin("local-vars", function(source, chunk) {
|
||||
return this.asString([
|
||||
source,
|
||||
"",
|
||||
"// object to store loaded and loading wasm modules",
|
||||
"var installedWasmModules = {};",
|
||||
]);
|
||||
});
|
||||
mainTemplate.plugin("require-ensure", function(source, chunk, hash) {
|
||||
const webassemblyModuleFilename = this.outputOptions.webassemblyModuleFilename;
|
||||
const chunkModuleMaps = chunk.getChunkModuleMaps(false, m => m.type.startsWith("webassembly"));
|
||||
if(Object.keys(chunkModuleMaps.id).length === 0) return source;
|
||||
const wasmModuleSrcPath = this.applyPluginsWaterfall("asset-path", JSON.stringify(webassemblyModuleFilename), {
|
||||
hash: `" + ${this.renderCurrentHashCode(hash)} + "`,
|
||||
hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`,
|
||||
module: {
|
||||
id: "\" + wasmModuleId + \"",
|
||||
hash: `" + ${JSON.stringify(chunkModuleMaps.hash)}[wasmModuleId] + "`,
|
||||
hashWithLength(length) {
|
||||
const shortChunkHashMap = Object.create(null);
|
||||
Object.keys(chunkModuleMaps.hash).forEach(wasmModuleId => {
|
||||
if(typeof chunkModuleMaps.hash[wasmModuleId] === "string")
|
||||
shortChunkHashMap[wasmModuleId] = chunkModuleMaps.hash[wasmModuleId].substr(0, length);
|
||||
});
|
||||
return `" + ${JSON.stringify(shortChunkHashMap)}[wasmModuleId] + "`;
|
||||
}
|
||||
}
|
||||
});
|
||||
return this.asString([
|
||||
source,
|
||||
"",
|
||||
"// Fetch + compile chunk loading for webassembly",
|
||||
"",
|
||||
`var wasmModules = ${JSON.stringify(chunkModuleMaps.id)}[chunkId] || [];`,
|
||||
"",
|
||||
"wasmModules.forEach(function(wasmModuleId) {",
|
||||
this.indent([
|
||||
"var installedWasmModuleData = installedWasmModules[wasmModuleId];",
|
||||
"",
|
||||
"// a Promise means \"currently loading\" or \"already loaded\".",
|
||||
"if(installedWasmModuleData) {",
|
||||
this.indent([
|
||||
"promises.push(installedWasmModuleData);"
|
||||
]),
|
||||
"} else {",
|
||||
this.indent([
|
||||
`var promise = installedWasmModules[wasmModuleId] = fetch(${this.requireFn}.p + ${wasmModuleSrcPath})`,
|
||||
this.indent([
|
||||
".then(function(response) { return response.arrayBuffer(); })",
|
||||
".then(function(bytes) { return WebAssembly.compile(bytes); })",
|
||||
`.then(function(module) { ${this.requireFn}.w[wasmModuleId] = module; })`
|
||||
]),
|
||||
"promises.push(promise);"
|
||||
]),
|
||||
"}",
|
||||
]),
|
||||
"});",
|
||||
]);
|
||||
});
|
||||
mainTemplate.plugin("require-extensions", function(source, chunk) {
|
||||
return this.asString([
|
||||
source,
|
||||
"",
|
||||
"// object with all compiled WebAssmbly.Modules",
|
||||
`${this.requireFn}.w = {};`
|
||||
]);
|
||||
});
|
||||
mainTemplate.plugin("hash", function(hash) {
|
||||
hash.update("jsonp");
|
||||
hash.update("5");
|
||||
hash.update(`${this.outputOptions.filename}`);
|
||||
hash.update(`${this.outputOptions.chunkFilename}`);
|
||||
hash.update(`${this.outputOptions.jsonpFunction}`);
|
||||
hash.update(`${this.outputOptions.hotUpdateFunction}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = FetchCompileWasmMainTemplatePlugin;
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const RawSource = require("webpack-sources").RawSource;
|
||||
|
||||
class FetchCompileWasmModuleTemplatePlugin {
|
||||
apply(moduleTemplate) {
|
||||
moduleTemplate.plugin("module", function(moduleSource, module, chunk) {
|
||||
if(module.type && module.type.startsWith("webassembly")) {
|
||||
if(chunk.isInitial())
|
||||
throw new Error("Sync WebAsssmbly compilation is not yet implemented");
|
||||
const source = new RawSource([
|
||||
"\"use strict\";",
|
||||
"",
|
||||
"// Instanciate WebAssembly module",
|
||||
"var instance = new WebAssembly.Instance(__webpack_require__.w[module.i], {});",
|
||||
"",
|
||||
"// export exports from WebAssmbly module",
|
||||
// TODO rewrite this to getters depending on exports to support circular dependencies
|
||||
"module.exports = instance.exports;"
|
||||
].join("\n"));
|
||||
return source;
|
||||
} else {
|
||||
return moduleSource;
|
||||
}
|
||||
});
|
||||
|
||||
moduleTemplate.plugin("hash", function(hash) {
|
||||
hash.update("FetchCompileWasmModuleTemplatePlugin");
|
||||
hash.update("1");
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = FetchCompileWasmModuleTemplatePlugin;
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const FetchCompileWasmMainTemplatePlugin = require("./FetchCompileWasmMainTemplatePlugin");
|
||||
const FetchCompileWasmModuleTemplatePlugin = require("./FetchCompileWasmModuleTemplatePlugin");
|
||||
|
||||
class FetchCompileWasmTemplatePlugin {
|
||||
apply(compiler) {
|
||||
compiler.plugin("this-compilation", (compilation) => {
|
||||
compilation.mainTemplate.apply(new FetchCompileWasmMainTemplatePlugin());
|
||||
compilation.moduleTemplates.javascript.apply(new FetchCompileWasmModuleTemplatePlugin());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FetchCompileWasmTemplatePlugin;
|
|
@ -12,7 +12,8 @@ class FunctionModuleTemplatePlugin {
|
|||
moduleTemplate.plugin("render", (moduleSource, module) => {
|
||||
const source = new ConcatSource();
|
||||
const defaultArguments = [module.moduleArgument || "module", module.exportsArgument || "exports"];
|
||||
if((module.arguments && module.arguments.length !== 0) || module.hasDependencies(d => d.requireWebpackRequire !== false)) {
|
||||
// TODO remove HACK checking type for javascript
|
||||
if(!module.type || !module.type.startsWith("javascript") || (module.arguments && module.arguments.length !== 0) || module.hasDependencies(d => d.requireWebpackRequire !== false)) {
|
||||
defaultArguments.push("__webpack_require__");
|
||||
}
|
||||
source.add("/***/ (function(" + defaultArguments.concat(module.arguments || []).join(", ") + ") {\n\n");
|
||||
|
|
|
@ -17,7 +17,7 @@ module.exports = class HotUpdateChunkTemplate extends Template {
|
|||
hotUpdateChunk.id = id;
|
||||
hotUpdateChunk.setModules(modules);
|
||||
hotUpdateChunk.removedModules = removedModules;
|
||||
const modulesSource = this.renderChunkModules(hotUpdateChunk, moduleTemplate, dependencyTemplates);
|
||||
const modulesSource = this.renderChunkModules(hotUpdateChunk, () => true, moduleTemplate, dependencyTemplates);
|
||||
const core = this.applyPluginsWaterfall("modules", modulesSource, modules, removedModules, moduleTemplate, dependencyTemplates);
|
||||
const source = this.applyPluginsWaterfall("render", core, modules, removedModules, hash, id, moduleTemplate, dependencyTemplates);
|
||||
return source;
|
||||
|
|
|
@ -19,7 +19,7 @@ class JsonpMainTemplatePlugin {
|
|||
return mainTemplate.asString([
|
||||
source,
|
||||
"",
|
||||
"// objects to store loaded and loading chunks",
|
||||
"// object to store loaded and loading chunks",
|
||||
"var installedChunks = {",
|
||||
mainTemplate.indent(
|
||||
chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n")
|
||||
|
@ -93,36 +93,40 @@ class JsonpMainTemplatePlugin {
|
|||
"};",
|
||||
]);
|
||||
});
|
||||
mainTemplate.plugin("require-ensure", (_, chunk, hash) => {
|
||||
mainTemplate.plugin("require-ensure", (source, chunk, hash) => {
|
||||
return mainTemplate.asString([
|
||||
source,
|
||||
"",
|
||||
"// JSONP chunk loading for javascript",
|
||||
"",
|
||||
"var installedChunkData = installedChunks[chunkId];",
|
||||
"if(installedChunkData === 0) {",
|
||||
"if(installedChunkData !== 0) { // 0 means \"already installed\".",
|
||||
mainTemplate.indent([
|
||||
"return new Promise(function(resolve) { resolve(); });"
|
||||
]),
|
||||
"}",
|
||||
"",
|
||||
"// a Promise means \"currently loading\".",
|
||||
"if(installedChunkData) {",
|
||||
mainTemplate.indent([
|
||||
"return installedChunkData[2];"
|
||||
"promises.push(installedChunkData[2]);"
|
||||
]),
|
||||
"}",
|
||||
"",
|
||||
"} else {",
|
||||
mainTemplate.indent([
|
||||
"// setup Promise in chunk cache",
|
||||
"var promise = new Promise(function(resolve, reject) {",
|
||||
mainTemplate.indent([
|
||||
"installedChunkData = installedChunks[chunkId] = [resolve, reject];"
|
||||
]),
|
||||
"});",
|
||||
"installedChunkData[2] = promise;",
|
||||
"});",
|
||||
"",
|
||||
"// start chunk loading",
|
||||
"var head = document.getElementsByTagName('head')[0];",
|
||||
mainTemplate.applyPluginsWaterfall("jsonp-script", "", chunk, hash),
|
||||
"head.appendChild(script);",
|
||||
"",
|
||||
"return promise;"
|
||||
"promises.push(promise);"
|
||||
]),
|
||||
"}",
|
||||
]),
|
||||
"}",
|
||||
]);
|
||||
});
|
||||
mainTemplate.plugin("require-extensions", (source, chunk) => {
|
||||
|
|
|
@ -20,6 +20,7 @@ const Template = require("./Template");
|
|||
// __webpack_require__.o = Object.prototype.hasOwnProperty.call
|
||||
// __webpack_require__.n = compatibility get default export
|
||||
// __webpack_require__.h = the webpack hash
|
||||
// __webpack_require__.w = an object containing all installed WebAssembly.Modules keys by module id
|
||||
// __webpack_require__.oe = the uncatched error handler for the webpack runtime
|
||||
// __webpack_require__.nc = the script nonce
|
||||
|
||||
|
@ -41,7 +42,7 @@ module.exports = class MainTemplate extends Template {
|
|||
source.add("/******/ })\n");
|
||||
source.add("/************************************************************************/\n");
|
||||
source.add("/******/ (");
|
||||
const modules = this.renderChunkModules(chunk, moduleTemplate, dependencyTemplates, "/******/ ");
|
||||
const modules = this.renderChunkModules(chunk, () => true, moduleTemplate, dependencyTemplates, "/******/ ");
|
||||
source.add(this.applyPluginsWaterfall("modules", modules, chunk, hash, moduleTemplate, dependencyTemplates));
|
||||
source.add(")");
|
||||
return source;
|
||||
|
@ -103,7 +104,9 @@ module.exports = class MainTemplate extends Template {
|
|||
buf.push("// This file contains only the entry chunk.");
|
||||
buf.push("// The chunk loading function for additional chunks");
|
||||
buf.push(`${this.requireFn}.e = function requireEnsure(chunkId) {`);
|
||||
buf.push(this.indent(this.applyPluginsWaterfall("require-ensure", "throw new Error('Not chunk loading available');", chunk, hash, "chunkId")));
|
||||
buf.push(this.indent("var promises = [];"));
|
||||
buf.push(this.indent(this.applyPluginsWaterfall("require-ensure", "", chunk, hash, "chunkId")));
|
||||
buf.push(this.indent("return Promise.all(promises);"));
|
||||
buf.push("};");
|
||||
}
|
||||
buf.push("");
|
||||
|
@ -162,6 +165,41 @@ module.exports = class MainTemplate extends Template {
|
|||
this.requireFn = "__webpack_require__";
|
||||
}
|
||||
|
||||
getRenderManifest(options) {
|
||||
const chunk = options.chunk;
|
||||
const hash = options.hash;
|
||||
const fullHash = options.fullHash;
|
||||
const outputOptions = options.outputOptions;
|
||||
const moduleTemplates = options.moduleTemplates;
|
||||
const dependencyTemplates = options.dependencyTemplates;
|
||||
|
||||
const result = [];
|
||||
|
||||
let filenameTemplate;
|
||||
if(chunk.filenameTemplate)
|
||||
filenameTemplate = chunk.filenameTemplate;
|
||||
else if(chunk.isInitial())
|
||||
filenameTemplate = outputOptions.filename;
|
||||
else {
|
||||
filenameTemplate = outputOptions.chunkFilename;
|
||||
}
|
||||
|
||||
const useChunkHash = this.useChunkHash(chunk);
|
||||
|
||||
result.push({
|
||||
render: () => this.render(hash, chunk, moduleTemplates.javascript, dependencyTemplates),
|
||||
filenameTemplate,
|
||||
pathOptions: {
|
||||
noChunkHash: !useChunkHash,
|
||||
chunk
|
||||
},
|
||||
identifier: `chunk${chunk.id}`,
|
||||
hash: useChunkHash ? chunk.hash : fullHash
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
render(hash, chunk, moduleTemplate, dependencyTemplates) {
|
||||
const buf = [];
|
||||
buf.push(this.applyPluginsWaterfall("bootstrap", "", chunk, hash, moduleTemplate, dependencyTemplates));
|
||||
|
|
|
@ -42,8 +42,9 @@ const getDebugIdent = set => {
|
|||
|
||||
class Module extends DependenciesBlock {
|
||||
|
||||
constructor() {
|
||||
constructor(type) {
|
||||
super();
|
||||
this.type = type;
|
||||
|
||||
// Unique Id
|
||||
this.debugId = debugId++;
|
||||
|
|
|
@ -10,12 +10,15 @@ module.exports = class ModuleTemplate extends Template {
|
|||
constructor(outputOptions) {
|
||||
super(outputOptions);
|
||||
}
|
||||
|
||||
// TODO move chunk into extra options object, it's not available i. e. in wasm modules
|
||||
render(module, dependencyTemplates, chunk) {
|
||||
const moduleSource = module.source(dependencyTemplates, this.outputOptions, this.requestShortener);
|
||||
const moduleSourcePostModule = this.applyPluginsWaterfall("module", moduleSource, module, chunk, dependencyTemplates);
|
||||
const moduleSourcePostRender = this.applyPluginsWaterfall("render", moduleSourcePostModule, module, chunk, dependencyTemplates);
|
||||
return this.applyPluginsWaterfall("package", moduleSourcePostRender, module, chunk, dependencyTemplates);
|
||||
}
|
||||
|
||||
updateHash(hash) {
|
||||
hash.update("1");
|
||||
this.applyPlugins("hash", hash);
|
||||
|
|
|
@ -11,7 +11,7 @@ const RawSource = require("webpack-sources").RawSource;
|
|||
class MultiModule extends Module {
|
||||
|
||||
constructor(context, dependencies, name) {
|
||||
super();
|
||||
super("javascript/dynamic");
|
||||
|
||||
// Info from Factory
|
||||
this.context = context;
|
||||
|
|
|
@ -32,6 +32,13 @@ const asString = (buf) => {
|
|||
return buf;
|
||||
};
|
||||
|
||||
const asBuffer = str => {
|
||||
if(!Buffer.isBuffer(str)) {
|
||||
return new Buffer(str, "utf-8"); // eslint-disable-line
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
const contextify = (context, request) => {
|
||||
return request.split("!").map(r => {
|
||||
const splitPath = r.split("?");
|
||||
|
@ -59,13 +66,14 @@ const dependencyTemplatesHashMap = new WeakMap();
|
|||
|
||||
class NormalModule extends Module {
|
||||
|
||||
constructor(request, userRequest, rawRequest, loaders, resource, parser) {
|
||||
super();
|
||||
constructor(type, request, userRequest, rawRequest, loaders, resource, parser) {
|
||||
super(type);
|
||||
|
||||
// Info from Factory
|
||||
this.request = request;
|
||||
this.userRequest = userRequest;
|
||||
this.rawRequest = rawRequest;
|
||||
this.binary = type.startsWith("webassembly");
|
||||
this.parser = parser;
|
||||
this.resource = resource;
|
||||
this.context = getContext(resource);
|
||||
|
@ -177,6 +185,10 @@ class NormalModule extends Module {
|
|||
return new SourceMapSource(source, identifier, sourceMap);
|
||||
}
|
||||
|
||||
if(Buffer.isBuffer(source)) {
|
||||
return new RawSource(source);
|
||||
}
|
||||
|
||||
return new OriginalSource(source, identifier);
|
||||
}
|
||||
|
||||
|
@ -211,7 +223,7 @@ class NormalModule extends Module {
|
|||
return callback(error);
|
||||
}
|
||||
|
||||
this._source = this.createSource(asString(source), resourceBuffer, sourceMap);
|
||||
this._source = this.createSource(this.binary ? asBuffer(source) : asString(source), resourceBuffer, sourceMap);
|
||||
this._ast = typeof extraInfo === "object" && extraInfo !== null && extraInfo.webpackAST !== undefined ? extraInfo.webpackAST : null;
|
||||
return callback();
|
||||
});
|
||||
|
@ -473,6 +485,7 @@ class NormalModule extends Module {
|
|||
}
|
||||
|
||||
source(dependencyTemplates, outputOptions, requestShortener) {
|
||||
if(this.type.startsWith("javascript")) {
|
||||
const hashDigest = this.getHashDigest(dependencyTemplates);
|
||||
if(this._cachedSource && this._cachedSource.hash === hashDigest) {
|
||||
return this._cachedSource.source;
|
||||
|
@ -491,6 +504,8 @@ class NormalModule extends Module {
|
|||
this.sourceBlock(this, [], dependencyTemplates, source, outputOptions, requestShortener);
|
||||
return new CachedSource(source);
|
||||
}
|
||||
return this._source;
|
||||
}
|
||||
|
||||
originalSource() {
|
||||
return this._source;
|
||||
|
|
|
@ -9,6 +9,7 @@ const Tapable = require("tapable");
|
|||
const NormalModule = require("./NormalModule");
|
||||
const RawModule = require("./RawModule");
|
||||
const Parser = require("./Parser");
|
||||
const WebAssemblyParser = require("./WebAssemblyParser");
|
||||
const RuleSet = require("./RuleSet");
|
||||
|
||||
const loaderToIdent = data => {
|
||||
|
@ -80,6 +81,7 @@ class NormalModuleFactory extends Tapable {
|
|||
}
|
||||
|
||||
createdModule = new NormalModule(
|
||||
result.type,
|
||||
result.request,
|
||||
result.userRequest,
|
||||
result.rawRequest,
|
||||
|
@ -191,6 +193,7 @@ class NormalModuleFactory extends Tapable {
|
|||
if(err) return callback(err);
|
||||
loaders = results[0].concat(loaders, results[1], results[2]);
|
||||
process.nextTick(() => {
|
||||
const type = settings.type || "javascript/auto";
|
||||
callback(null, {
|
||||
context: context,
|
||||
request: loaders.map(loaderToIdent).concat([resource]).join("!"),
|
||||
|
@ -200,7 +203,8 @@ class NormalModuleFactory extends Tapable {
|
|||
loaders,
|
||||
resource,
|
||||
resourceResolveData,
|
||||
parser: this.getParser(settings.parser)
|
||||
type,
|
||||
parser: this.getParser(type, settings.parser)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -268,23 +272,39 @@ class NormalModuleFactory extends Tapable {
|
|||
}, callback);
|
||||
}
|
||||
|
||||
getParser(parserOptions) {
|
||||
let ident = "null";
|
||||
getParser(type, parserOptions) {
|
||||
let ident = type;
|
||||
if(parserOptions) {
|
||||
if(parserOptions.ident)
|
||||
ident = parserOptions.ident;
|
||||
ident = `${type}|${parserOptions.ident}`;
|
||||
else
|
||||
ident = JSON.stringify(parserOptions);
|
||||
ident = JSON.stringify([type, parserOptions]);
|
||||
}
|
||||
const parser = this.parserCache[ident];
|
||||
if(parser)
|
||||
return parser;
|
||||
return this.parserCache[ident] = this.createParser(parserOptions);
|
||||
return this.parserCache[ident] = this.createParser(type, parserOptions);
|
||||
}
|
||||
|
||||
createParser(parserOptions) {
|
||||
const parser = new Parser();
|
||||
createParser(type, parserOptions) {
|
||||
// TODO move to plugin
|
||||
// const parser = this.applyPluginsBailResult1(`create-parser ${type}`);
|
||||
let parser;
|
||||
switch(type) {
|
||||
case "javascript/auto":
|
||||
parser = new Parser();
|
||||
break;
|
||||
case "webassembly/experimental":
|
||||
parser = new WebAssemblyParser();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown module type");
|
||||
}
|
||||
if(type === "javascript/auto") {
|
||||
// TODO: remove backward compat in webpack 5
|
||||
this.applyPlugins2("parser", parser, parserOptions || {});
|
||||
}
|
||||
this.applyPlugins2(`parser ${type}`, parser, parserOptions || {});
|
||||
return parser;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ const RawSource = require("webpack-sources").RawSource;
|
|||
module.exports = class RawModule extends Module {
|
||||
|
||||
constructor(source, identifier, readableIdentifier) {
|
||||
super();
|
||||
super("javascript/dynamic");
|
||||
this.sourceStr = source;
|
||||
this.identifierStr = identifier || this.sourceStr;
|
||||
this.readableIdentifierStr = readableIdentifier || this.identifierStr;
|
||||
|
|
|
@ -126,15 +126,16 @@ module.exports = class Template extends Tapable {
|
|||
return arrayOverhead < objectOverhead ? [minId, maxId] : false;
|
||||
}
|
||||
|
||||
renderChunkModules(chunk, moduleTemplate, dependencyTemplates, prefix) {
|
||||
renderChunkModules(chunk, filterFn, moduleTemplate, dependencyTemplates, prefix) {
|
||||
if(!prefix) prefix = "";
|
||||
var source = new ConcatSource();
|
||||
if(chunk.getNumberOfModules() === 0) {
|
||||
const modules = chunk.getModules().filter(filterFn);
|
||||
var removedModules = chunk.removedModules;
|
||||
if(modules.length === 0 && removedModules.length === 0) {
|
||||
source.add("[]");
|
||||
return source;
|
||||
}
|
||||
var removedModules = chunk.removedModules;
|
||||
var allModules = chunk.mapModules(module => {
|
||||
var allModules = modules.map(module => {
|
||||
return {
|
||||
id: module.id,
|
||||
source: moduleTemplate.render(module, dependencyTemplates, chunk)
|
||||
|
@ -156,7 +157,7 @@ module.exports = class Template extends Tapable {
|
|||
var maxId = bounds[1];
|
||||
if(minId !== 0) source.add("Array(" + minId + ").concat(");
|
||||
source.add("[\n");
|
||||
var modules = {};
|
||||
const modules = {};
|
||||
allModules.forEach(module => {
|
||||
modules[module.id] = module;
|
||||
});
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
const REGEXP_HASH = /\[hash(?::(\d+))?\]/gi,
|
||||
REGEXP_CHUNKHASH = /\[chunkhash(?::(\d+))?\]/gi,
|
||||
REGEXP_MODULEHASH = /\[modulehash(?::(\d+))?\]/gi,
|
||||
REGEXP_NAME = /\[name\]/gi,
|
||||
REGEXP_ID = /\[id\]/gi,
|
||||
REGEXP_MODULEID = /\[moduleid\]/gi,
|
||||
REGEXP_FILE = /\[file\]/gi,
|
||||
REGEXP_QUERY = /\[query\]/gi,
|
||||
REGEXP_FILEBASE = /\[filebase\]/gi;
|
||||
|
@ -50,6 +52,10 @@ const replacePathVariables = (path, data) => {
|
|||
const chunkName = chunk && (chunk.name || chunk.id);
|
||||
const chunkHash = chunk && (chunk.renderedHash || chunk.hash);
|
||||
const chunkHashWithLength = chunk && chunk.hashWithLength;
|
||||
const module = data.module;
|
||||
const moduleId = module && module.id;
|
||||
const moduleHash = module && (module.renderedHash || module.hash);
|
||||
const moduleHashWithLength = module && module.hashWithLength;
|
||||
|
||||
if(data.noChunkHash && REGEXP_CHUNKHASH_FOR_TEST.test(path)) {
|
||||
throw new Error(`Cannot use [chunkhash] for chunk in '${path}' (use [hash] instead)`);
|
||||
|
@ -58,7 +64,9 @@ const replacePathVariables = (path, data) => {
|
|||
return path
|
||||
.replace(REGEXP_HASH, withHashLength(getReplacer(data.hash), data.hashWithLength))
|
||||
.replace(REGEXP_CHUNKHASH, withHashLength(getReplacer(chunkHash), chunkHashWithLength))
|
||||
.replace(REGEXP_MODULEHASH, withHashLength(getReplacer(moduleHash), moduleHashWithLength))
|
||||
.replace(REGEXP_ID, getReplacer(chunkId))
|
||||
.replace(REGEXP_MODULEID, getReplacer(moduleId))
|
||||
.replace(REGEXP_NAME, getReplacer(chunkName))
|
||||
.replace(REGEXP_FILE, getReplacer(data.filename))
|
||||
.replace(REGEXP_FILEBASE, getReplacer(data.basename))
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
|
||||
|
||||
const Tapable = require("tapable");
|
||||
|
||||
class WebAssemblyParser extends Tapable {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
parse(source, initialState) {
|
||||
// Does nothing current
|
||||
// TODO parse WASM AST and walk it
|
||||
// extract exports, imports
|
||||
return initialState;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebAssemblyParser;
|
|
@ -62,6 +62,7 @@ class WebpackOptionsApply extends OptionsApply {
|
|||
compiler.dependencies = options.dependencies;
|
||||
if(typeof options.target === "string") {
|
||||
let JsonpTemplatePlugin;
|
||||
let FetchCompileWasmTemplatePlugin;
|
||||
let NodeSourcePlugin;
|
||||
let NodeTargetPlugin;
|
||||
let NodeTemplatePlugin;
|
||||
|
@ -69,9 +70,11 @@ class WebpackOptionsApply extends OptionsApply {
|
|||
switch(options.target) {
|
||||
case "web":
|
||||
JsonpTemplatePlugin = require("./JsonpTemplatePlugin");
|
||||
FetchCompileWasmTemplatePlugin = require("./FetchCompileWasmTemplatePlugin");
|
||||
NodeSourcePlugin = require("./node/NodeSourcePlugin");
|
||||
compiler.apply(
|
||||
new JsonpTemplatePlugin(options.output),
|
||||
new FetchCompileWasmTemplatePlugin(options.output),
|
||||
new FunctionModulePlugin(options.output),
|
||||
new NodeSourcePlugin(options.node),
|
||||
new LoaderTargetPlugin(options.target)
|
||||
|
|
|
@ -48,6 +48,7 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
|
|||
const filename = options.output.filename;
|
||||
return filename.indexOf("[name]") >= 0 ? filename.replace("[name]", "[id]") : "[id]." + filename;
|
||||
});
|
||||
this.set("output.webassemblyModuleFilename", "[modulehash].module.wasm");
|
||||
this.set("output.library", "");
|
||||
this.set("output.hotUpdateFunction", "make", (options) => {
|
||||
return Template.toIdentifier("webpackHotUpdate" + options.output.library);
|
||||
|
|
|
@ -159,7 +159,7 @@ const getPathInAst = (ast, node) => {
|
|||
|
||||
class ConcatenatedModule extends Module {
|
||||
constructor(rootModule, modules) {
|
||||
super();
|
||||
super("javascript/esm");
|
||||
super.setChunks(rootModule._chunks);
|
||||
|
||||
// Info from Factory
|
||||
|
|
|
@ -260,6 +260,11 @@
|
|||
"type": "string",
|
||||
"absolutePath": false
|
||||
},
|
||||
"webassemblyModuleFilename": {
|
||||
"description": "The filename of WebAssembly modules as relative path inside the `output.path` directory.",
|
||||
"type": "string",
|
||||
"absolutePath": false
|
||||
},
|
||||
"crossOriginLoading": {
|
||||
"description": "This option enables cross-origin loading of chunks.",
|
||||
"enum": [
|
||||
|
@ -653,6 +658,14 @@
|
|||
"query": {
|
||||
"$ref": "#/definitions/ruleSet-query"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"javascript/auto",
|
||||
"javascript/dynamic",
|
||||
"javascript/esm",
|
||||
"webassembly/experimental"
|
||||
]
|
||||
},
|
||||
"resource": {
|
||||
"allOf": [
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue