add helpful error when importing wasm in initial chunk
This commit is contained in:
parent
e8dc36196f
commit
1ad71e01f9
|
@ -298,7 +298,7 @@ class Chunk {
|
|||
}
|
||||
|
||||
/**
|
||||
* @returns {SortableSet} the chunkGroups that said chunk is referenced in
|
||||
* @returns {SortableSet<ChunkGroup>} the chunkGroups that said chunk is referenced in
|
||||
*/
|
||||
get groupsIterable() {
|
||||
return this._groups;
|
||||
|
|
|
@ -26,6 +26,7 @@ module.exports = class ChunkTemplate extends Tapable {
|
|||
super();
|
||||
this.outputOptions = outputOptions || {};
|
||||
this.hooks = {
|
||||
/** @type {SyncWaterfallHook<TODO[], RenderManifestOptions>} */
|
||||
renderManifest: new SyncWaterfallHook(["result", "options"]),
|
||||
modules: new SyncWaterfallHook([
|
||||
"source",
|
||||
|
|
|
@ -232,6 +232,11 @@ class Compilation extends Tapable {
|
|||
/** @type {SyncHook} */
|
||||
seal: new SyncHook([]),
|
||||
|
||||
/** @type {SyncHook} */
|
||||
beforeChunks: new SyncHook([]),
|
||||
/** @type {SyncHook<Chunk[]>} */
|
||||
afterChunks: new SyncHook(["chunks"]),
|
||||
|
||||
/** @type {SyncBailHook<Module[]>} */
|
||||
optimizeDependenciesBasic: new SyncBailHook(["modules"]),
|
||||
/** @type {SyncBailHook<Module[]>} */
|
||||
|
@ -1150,6 +1155,7 @@ class Compilation extends Tapable {
|
|||
}
|
||||
this.hooks.afterOptimizeDependencies.call(this.modules);
|
||||
|
||||
this.hooks.beforeChunks.call();
|
||||
for (const preparedEntrypoint of this._preparedEntrypoints) {
|
||||
const module = preparedEntrypoint.module;
|
||||
const name = preparedEntrypoint.name;
|
||||
|
@ -1171,6 +1177,8 @@ class Compilation extends Tapable {
|
|||
}
|
||||
this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
|
||||
this.sortModules(this.modules);
|
||||
this.hooks.afterChunks.call(this.chunks);
|
||||
|
||||
this.hooks.optimize.call();
|
||||
|
||||
while (
|
||||
|
|
|
@ -19,6 +19,7 @@ const {
|
|||
const Template = require("./Template");
|
||||
|
||||
/** @typedef {import("webpack-sources").ConcatSource} ConcatSource */
|
||||
/** @typedef {import("webpack-sources").Source} Source */
|
||||
/** @typedef {import("./ModuleTemplate")} ModuleTemplate */
|
||||
/** @typedef {import("./Chunk")} Chunk */
|
||||
/** @typedef {import("./Module")} Module} */
|
||||
|
@ -93,7 +94,9 @@ module.exports = class MainTemplate extends Tapable {
|
|||
localVars: new SyncWaterfallHook(["source", "chunk", "hash"]),
|
||||
require: new SyncWaterfallHook(["source", "chunk", "hash"]),
|
||||
requireExtensions: new SyncWaterfallHook(["source", "chunk", "hash"]),
|
||||
/** @type {SyncWaterfallHook<string, Chunk, string>} */
|
||||
beforeStartup: new SyncWaterfallHook(["source", "chunk", "hash"]),
|
||||
/** @type {SyncWaterfallHook<string, Chunk, string>} */
|
||||
startup: new SyncWaterfallHook(["source", "chunk", "hash"]),
|
||||
render: new SyncWaterfallHook([
|
||||
"source",
|
||||
|
@ -448,7 +451,7 @@ module.exports = class MainTemplate extends Tapable {
|
|||
/**
|
||||
*
|
||||
* @param {string} hash string hash
|
||||
* @param {number} length length
|
||||
* @param {number=} length length
|
||||
* @returns {string} call hook return
|
||||
*/
|
||||
renderCurrentHashCode(hash, length) {
|
||||
|
|
|
@ -182,6 +182,9 @@ class Module extends DependenciesBlock {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Chunk[]} all chunks which contain the module
|
||||
*/
|
||||
getChunks() {
|
||||
return Array.from(this._chunks);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
*/
|
||||
"use strict";
|
||||
|
||||
/** @typedef {import("./Module")} Module */
|
||||
/** @typedef {import("./Dependency")} Dependency */
|
||||
|
||||
class ModuleReason {
|
||||
/**
|
||||
* @param {Module} module the referencing module
|
||||
* @param {Dependency} dependency the referencing dependency
|
||||
* @param {string=} explanation some extra detail
|
||||
*/
|
||||
constructor(module, dependency, explanation) {
|
||||
this.module = module;
|
||||
this.dependency = dependency;
|
||||
|
|
|
@ -8,6 +8,7 @@ const Template = require("../Template");
|
|||
const WebAssemblyUtils = require("./WebAssemblyUtils");
|
||||
|
||||
/** @typedef {import("../Module")} Module */
|
||||
/** @typedef {import("../MainTemplate")} MainTemplate */
|
||||
|
||||
// Get all wasm modules
|
||||
const getAllWasmModules = chunk => {
|
||||
|
@ -159,6 +160,11 @@ class WasmMainTemplatePlugin {
|
|||
this.supportsStreaming = supportsStreaming;
|
||||
this.mangleImports = mangleImports;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MainTemplate} mainTemplate main template
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(mainTemplate) {
|
||||
mainTemplate.hooks.localVars.tap(
|
||||
"WasmMainTemplatePlugin",
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const WebpackError = require("../WebpackError");
|
||||
|
||||
/** @typedef {import("../Module")} Module */
|
||||
/** @typedef {import("../RequestShortener")} RequestShortener */
|
||||
|
||||
/**
|
||||
* @param {Module} module module to get chains from
|
||||
* @param {RequestShortener} requestShortener to make readable identifiers
|
||||
* @returns {string[]} all chains to the module
|
||||
*/
|
||||
const getInitialModuleChains = (module, requestShortener) => {
|
||||
const queue = [
|
||||
{ head: module, message: module.readableIdentifier(requestShortener) }
|
||||
];
|
||||
/** @type {Set<string>} */
|
||||
const results = new Set();
|
||||
/** @type {Set<string>} */
|
||||
const incompleteResults = new Set();
|
||||
/** @type {Set<Module>} */
|
||||
const visitedModules = new Set();
|
||||
|
||||
for (const chain of queue) {
|
||||
const { head, message } = chain;
|
||||
let final = true;
|
||||
/** @type {Set<Module>} */
|
||||
const alreadyReferencedModules = new Set();
|
||||
for (const reason of head.reasons) {
|
||||
const newHead = reason.module;
|
||||
if (newHead) {
|
||||
if (!newHead.getChunks().some(c => c.canBeInitial())) continue;
|
||||
final = false;
|
||||
if (alreadyReferencedModules.has(newHead)) continue;
|
||||
alreadyReferencedModules.add(newHead);
|
||||
const moduleName = newHead.readableIdentifier(requestShortener);
|
||||
const detail = reason.explanation ? ` (${reason.explanation})` : "";
|
||||
const newMessage = `${moduleName}${detail} --> ${message}`;
|
||||
if (visitedModules.has(newHead)) {
|
||||
incompleteResults.add(`... --> ${newMessage}`);
|
||||
continue;
|
||||
}
|
||||
visitedModules.add(newHead);
|
||||
queue.push({
|
||||
head: newHead,
|
||||
message: newMessage
|
||||
});
|
||||
} else {
|
||||
final = false;
|
||||
const newMessage = reason.explanation
|
||||
? `(${reason.explanation}) --> ${message}`
|
||||
: message;
|
||||
results.add(newMessage);
|
||||
}
|
||||
}
|
||||
if (final) {
|
||||
results.add(message);
|
||||
}
|
||||
}
|
||||
for (const result of incompleteResults) {
|
||||
results.add(result);
|
||||
}
|
||||
return Array.from(results);
|
||||
};
|
||||
|
||||
module.exports = class WebAssemblyInInitialChunkError extends WebpackError {
|
||||
/**
|
||||
* @param {Module} module WASM module
|
||||
* @param {RequestShortener} requestShortener request shortener
|
||||
*/
|
||||
constructor(module, requestShortener) {
|
||||
const moduleChains = getInitialModuleChains(module, requestShortener);
|
||||
const message = `WebAssembly module is included in initial chunk.
|
||||
This is not allowed, because WebAssembly download and compilation must happen asynchronous.
|
||||
Add an async splitpoint (i. e. import()) somewhere between your entrypoint and the WebAssembly module:
|
||||
${moduleChains.map(s => `* ${s}`).join("\n")}`;
|
||||
|
||||
super(message);
|
||||
this.name = "WebAssemblyInInitialChunkError";
|
||||
this.hideStack = true;
|
||||
this.module = module;
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
};
|
|
@ -10,12 +10,19 @@ const WebAssemblyGenerator = require("./WebAssemblyGenerator");
|
|||
const WebAssemblyJavascriptGenerator = require("./WebAssemblyJavascriptGenerator");
|
||||
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
|
||||
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
|
||||
const WebAssemblyInInitialChunkError = require("./WebAssemblyInInitialChunkError");
|
||||
|
||||
/** @typedef {import("../Compiler")} Compiler */
|
||||
|
||||
class WebAssemblyModulesPlugin {
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Compiler} compiler compiler
|
||||
* @returns {void}
|
||||
*/
|
||||
apply(compiler) {
|
||||
compiler.hooks.compilation.tap(
|
||||
"WebAssemblyModulesPlugin",
|
||||
|
@ -78,6 +85,27 @@ class WebAssemblyModulesPlugin {
|
|||
return result;
|
||||
}
|
||||
);
|
||||
|
||||
compilation.hooks.afterChunks.tap("WebAssemblyModulesPlugin", () => {
|
||||
const initialWasmModules = new Set();
|
||||
for (const chunk of compilation.chunks) {
|
||||
if (chunk.canBeInitial()) {
|
||||
for (const module of chunk.modulesIterable) {
|
||||
if (module.type.startsWith("webassembly")) {
|
||||
initialWasmModules.add(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const module of initialWasmModules) {
|
||||
compilation.errors.push(
|
||||
new WebAssemblyInInitialChunkError(
|
||||
module,
|
||||
compilation.requestShortener
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2854,3 +2854,39 @@ WARNING in UglifyJs Plugin: Dropping unused function someUnRemoteUsedFunction4 [
|
|||
|
||||
WARNING in UglifyJs Plugin: Dropping unused function someUnRemoteUsedFunction5 [./a.js:7,0] in bundle.js"
|
||||
`;
|
||||
|
||||
exports[`StatsTestCases should print correct stats for wasm-in-initial-chunk-error 1`] = `
|
||||
"Hash: 9f32353d97d5973caae9
|
||||
Time: Xms
|
||||
Built at: Thu Jan 01 1970 00:00:00 GMT
|
||||
Asset Size Chunks Chunk Names
|
||||
0.js 130 bytes 0
|
||||
main.js 9.54 KiB 1 main
|
||||
Entrypoint main = main.js
|
||||
[0] ./wasm.wat 42 bytes {1} [built]
|
||||
[1] ./module2.js 45 bytes {1} [built]
|
||||
[2] ./module3.js 47 bytes {1} [built]
|
||||
[3] ./wasm2.wat 42 bytes {1} [built]
|
||||
[4] ./index.js + 1 modules 124 bytes {1} [built]
|
||||
| ./index.js 19 bytes [built]
|
||||
| ./module.js 100 bytes [built]
|
||||
[5] ./async.js 0 bytes {0} [built]
|
||||
|
||||
WARNING in configuration
|
||||
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
|
||||
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
|
||||
|
||||
ERROR in ./wasm2.wat
|
||||
WebAssembly module is included in initial chunk.
|
||||
This is not allowed, because WebAssembly download and compilation must happen asynchronous.
|
||||
Add an async splitpoint (i. e. import()) somewhere between your entrypoint and the WebAssembly module:
|
||||
* ./index.js --> ./module.js --> ./module2.js --> ./module3.js --> ./wasm2.wat
|
||||
|
||||
ERROR in ./wasm.wat
|
||||
WebAssembly module is included in initial chunk.
|
||||
This is not allowed, because WebAssembly download and compilation must happen asynchronous.
|
||||
Add an async splitpoint (i. e. import()) somewhere between your entrypoint and the WebAssembly module:
|
||||
* ./index.js --> ./module.js --> ./wasm.wat
|
||||
* ... --> ./module.js --> ./module2.js --> ./wasm.wat
|
||||
* ... --> ./module2.js --> ./module3.js --> ./wasm.wat"
|
||||
`;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
import "./module";
|
|
@ -0,0 +1,7 @@
|
|||
import { getNumber } from "./wasm.wat";
|
||||
|
||||
import("./async.js");
|
||||
|
||||
require("./module2");
|
||||
|
||||
getNumber();
|
|
@ -0,0 +1,2 @@
|
|||
require("./wasm.wat");
|
||||
require("./module3");
|
|
@ -0,0 +1,2 @@
|
|||
require("./wasm.wat");
|
||||
require("./wasm2.wat");
|
|
@ -0,0 +1,4 @@
|
|||
(module
|
||||
(func $getNumber (export "getNumber") (result i32)
|
||||
(i32.const 42)))
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
(module
|
||||
(func $getNumber (export "getNumber") (result i32)
|
||||
(i32.const 42)))
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
entry: "./index",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.wat$/,
|
||||
loader: "wast-loader",
|
||||
type: "webassembly/experimental"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue