Skip to content

Commit

Permalink
Merge pull request #15266 from webpack/bugfix/throwing-in-async-modules
Browse files Browse the repository at this point in the history
fix handling of errors thrown in async modules
  • Loading branch information
sokra committed Jan 28, 2022
2 parents 4abe329 + 804f49c commit 1e73ca7
Show file tree
Hide file tree
Showing 15 changed files with 120 additions and 33 deletions.
8 changes: 4 additions & 4 deletions lib/async-modules/AwaitDependenciesInitFragment.js
Expand Up @@ -30,8 +30,8 @@ class AwaitDependenciesInitFragment extends InitFragment {
}

merge(other) {
const promises = new Set(this.promises);
for (const p of other.promises) {
const promises = new Set(other.promises);
for (const p of this.promises) {
promises.add(p);
}
return new AwaitDependenciesInitFragment(promises);
Expand All @@ -51,7 +51,7 @@ class AwaitDependenciesInitFragment extends InitFragment {
for (const p of promises) {
return Template.asString([
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${p}]);`,
`${p} = (__webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__)[0];`,
`${p} = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0];`,
""
]);
}
Expand All @@ -60,7 +60,7 @@ class AwaitDependenciesInitFragment extends InitFragment {
// TODO check if destructuring is supported
return Template.asString([
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${sepPromises}]);`,
`([${sepPromises}] = __webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__);`,
`([${sepPromises}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);`,
""
]);
}
Expand Down
10 changes: 5 additions & 5 deletions lib/dependencies/HarmonyCompatibilityDependency.js
Expand Up @@ -74,14 +74,14 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate
initFragments.push(
new InitFragment(
runtimeTemplate.supportsArrowFunction()
? `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async (__webpack_handle_async_dependencies__) => {\n`
: `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async function (__webpack_handle_async_dependencies__) {\n`,
? `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n`
: `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async function (__webpack_handle_async_dependencies__, __webpack_async_result__) { try {\n`,
InitFragment.STAGE_ASYNC_BOUNDARY,
0,
undefined,
module.buildMeta.async
? `\n__webpack_handle_async_dependencies__();\n}, 1);`
: "\n});"
`\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } }${
module.buildMeta.async ? ", 1" : ""
});`
)
);
}
Expand Down
40 changes: 25 additions & 15 deletions lib/runtime/AsyncModuleRuntimeModule.js
Expand Up @@ -22,6 +22,7 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
return Template.asString([
'var webpackThen = typeof Symbol === "function" ? Symbol("webpack then") : "__webpack_then__";',
'var webpackExports = typeof Symbol === "function" ? Symbol("webpack exports") : "__webpack_exports__";',
'var webpackError = typeof Symbol === "function" ? Symbol("webpack error") : "__webpack_error__";',
`var completeQueue = ${runtimeTemplate.basicFunction("queue", [
"if(queue) {",
Template.indent([
Expand Down Expand Up @@ -56,9 +57,13 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
"obj[webpackExports] = r;",
"completeQueue(queue);",
"queue = 0;"
])}, ${runtimeTemplate.basicFunction("e", [
"obj[webpackError] = e;",
"completeQueue(queue);",
"queue = 0;"
])});`,
`var obj = {};
obj[webpackThen] = ${runtimeTemplate.expressionFunction(
"var obj = {};",
`obj[webpackThen] = ${runtimeTemplate.expressionFunction(
"queueFunction(queue, fn), dep['catch'](reject)",
"fn, reject"
)};`,
Expand All @@ -67,13 +72,13 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
"}"
]),
"}",
`var ret = {};
ret[webpackThen] = ${runtimeTemplate.expressionFunction(
"var ret = {};",
`ret[webpackThen] = ${runtimeTemplate.expressionFunction(
"completeFunction(fn)",
"fn"
)};
ret[webpackExports] = dep;
return ret;`
)};`,
"ret[webpackExports] = dep;",
"return ret;"
])})`,
"deps"
)};`,
Expand Down Expand Up @@ -119,24 +124,29 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
)};`,
"module.exports = promise;",
`body(${runtimeTemplate.basicFunction("deps", [
"if(!deps) return outerResolve();",
"currentDeps = wrapDeps(deps);",
"var fn, result;",
"var fn;",
`var getResult = ${runtimeTemplate.returningFunction(
`currentDeps.map(${runtimeTemplate.basicFunction("d", [
"if(d[webpackError]) throw d[webpackError];",
"return d[webpackExports];"
])})`
)}`,
`var promise = new Promise(${runtimeTemplate.basicFunction(
"resolve, reject",
[
`fn = ${runtimeTemplate.expressionFunction(
`resolve(result = currentDeps.map(${runtimeTemplate.returningFunction(
"d[webpackExports]",
"d"
)}))`
"resolve(getResult)"
)};`,
"fn.r = 0;",
"whenAll(currentDeps, fn, reject);"
]
)});`,
"return fn.r ? promise : result;"
])}).then(outerResolve, reject);`,
"return fn.r ? promise : getResult();"
])}, ${runtimeTemplate.expressionFunction(
"err && reject(promise[webpackError] = err), outerResolve()",
"err"
)});`,
"isEvaluating = false;"
])};`
]);
Expand Down
12 changes: 9 additions & 3 deletions lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js
Expand Up @@ -167,14 +167,20 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
)}`,
`${RuntimeGlobals.asyncModule}(${
module.moduleArgument
}, ${runtimeTemplate.basicFunction(
"__webpack_handle_async_dependencies__",
}, async ${runtimeTemplate.basicFunction(
"__webpack_handle_async_dependencies__, __webpack_async_result__",
[
"try {",
importsCode,
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${promises.join(
", "
)}]);`,
"return __webpack_async_dependencies__.then ? __webpack_async_dependencies__.then(__webpack_instantiate__) : __webpack_instantiate__(__webpack_async_dependencies__);"
`var [${promises.join(
", "
)}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__;`,
`${importsCompatCode}await ${instantiateCall};`,
"__webpack_async_result__();",
"} catch(e) { __webpack_async_result__(e); }"
]
)}, 1);`
])
Expand Down
12 changes: 6 additions & 6 deletions test/__snapshots__/StatsTestCases.basictest.js.snap
Expand Up @@ -4554,9 +4554,9 @@ webpack x.x.x compiled with 1 warning in X ms"
`;
exports[`StatsTestCases should print correct stats for wasm-explorer-examples-sync 1`] = `
"assets by path *.js 21.8 KiB
asset bundle.js 16.4 KiB [emitted] (name: main)
asset 325.bundle.js 3.79 KiB [emitted]
"assets by path *.js 22.2 KiB
asset bundle.js 16.7 KiB [emitted] (name: main)
asset 325.bundle.js 3.9 KiB [emitted]
asset 795.bundle.js 557 bytes [emitted]
asset 526.bundle.js 366 bytes [emitted] (id hint: vendors)
asset 189.bundle.js 243 bytes [emitted]
Expand All @@ -4571,8 +4571,8 @@ assets by path *.wasm 1.37 KiB
asset 0301cb3f9f4151b567f5.module.wasm 120 bytes [emitted] [immutable]
chunk (runtime: main) 20.bundle.js 50 bytes (javascript) 531 bytes (webassembly) [rendered]
./duff.wasm 50 bytes (javascript) 531 bytes (webassembly) [built] [code generated]
chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.25 KiB (runtime) [entry] [rendered]
runtime modules 9.25 KiB 11 modules
chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.49 KiB (runtime) [entry] [rendered]
runtime modules 9.49 KiB 11 modules
./index.js 586 bytes [built] [code generated]
chunk (runtime: main) 189.bundle.js 50 bytes (javascript) 156 bytes (webassembly) [rendered]
./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated]
Expand All @@ -4586,7 +4586,7 @@ chunk (runtime: main) 526.bundle.js (id hint: vendors) 34 bytes [rendered] split
chunk (runtime: main) 795.bundle.js 110 bytes (javascript) 444 bytes (webassembly) [rendered]
./fact.wasm 50 bytes (javascript) 154 bytes (webassembly) [built] [code generated]
./fast-math.wasm 60 bytes (javascript) 290 bytes (webassembly) [built] [code generated]
runtime modules 9.25 KiB 11 modules
runtime modules 9.49 KiB 11 modules
cacheable modules 2.31 KiB (javascript) 1.37 KiB (webassembly)
webassembly modules 310 bytes (javascript) 1.37 KiB (webassembly)
./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated]
Expand Down
3 changes: 3 additions & 0 deletions test/cases/async-modules/double-import/a.js
@@ -0,0 +1,3 @@
import x from "./shared";

export default x + " world";
3 changes: 3 additions & 0 deletions test/cases/async-modules/double-import/b.js
@@ -0,0 +1,3 @@
import x from "./shared";

export default x + " world";
4 changes: 4 additions & 0 deletions test/cases/async-modules/double-import/index.js
@@ -0,0 +1,4 @@
it("should allow to import an async module twice", async () => {
const result = await require("./main");
expect(result.default).toBe("hello world, hello world");
});
4 changes: 4 additions & 0 deletions test/cases/async-modules/double-import/main.js
@@ -0,0 +1,4 @@
import a from "./a";
import b from "./b";

export default a + ", " + b;
3 changes: 3 additions & 0 deletions test/cases/async-modules/double-import/shared.js
@@ -0,0 +1,3 @@
await 1;
await 1;
export default "hello";
6 changes: 6 additions & 0 deletions test/cases/async-modules/top-level-error/counter.js
@@ -0,0 +1,6 @@
await 1;
let value = 0;
export const count = () => {
value++;
return value;
};
39 changes: 39 additions & 0 deletions test/cases/async-modules/top-level-error/index.js
@@ -0,0 +1,39 @@
it("should allow to import an rejected async module again", async () => {
await expect(require("./main")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 1")
})
);
await expect(require("./module")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 1")
})
);
await expect(require("./module?2")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 2")
})
);
await expect(require("./reexport?2")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 1")
})
);
await Promise.all([
expect(require("./module?3")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 3")
})
),
expect(require("./module?4")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 4")
})
),
expect(require("./module?5")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 5")
})
)
]);
});
2 changes: 2 additions & 0 deletions test/cases/async-modules/top-level-error/main.js
@@ -0,0 +1,2 @@
export { default as a } from "./reexport";
export { default as b } from "./module?2";
6 changes: 6 additions & 0 deletions test/cases/async-modules/top-level-error/module.js
@@ -0,0 +1,6 @@
import { count } from "./counter";

const c = count();
throw new Error("expected rejection " + c);

export default "ok";
1 change: 1 addition & 0 deletions test/cases/async-modules/top-level-error/reexport.js
@@ -0,0 +1 @@
export { default as default } from "./module";

0 comments on commit 1e73ca7

Please sign in to comment.