diff --git a/lib/WebpackOptionsApply.js b/lib/WebpackOptionsApply.js index 75aaf1ce0..f21ed0d94 100644 --- a/lib/WebpackOptionsApply.js +++ b/lib/WebpackOptionsApply.js @@ -308,10 +308,18 @@ class WebpackOptionsApply extends OptionsApply { new RuntimePlugin().apply(compiler); - if (options.experiments.importAwait || options.experiments.importAsync) { + if ( + options.experiments.importAwait || + options.experiments.importAsync || + options.experiments.topLevelAwait + ) { const InferAsyncModulesPlugin = require("./async-modules/InferAsyncModulesPlugin"); new InferAsyncModulesPlugin({ - errorOnMissingAwait: !options.experiments.importAsync + errorOnImport: options.experiments.importAsync + ? false + : options.experiments.importAwait + ? "await" + : true }).apply(compiler); } diff --git a/lib/async-modules/InferAsyncModulesPlugin.js b/lib/async-modules/InferAsyncModulesPlugin.js index 4bc199cf0..e92cb2152 100644 --- a/lib/async-modules/InferAsyncModulesPlugin.js +++ b/lib/async-modules/InferAsyncModulesPlugin.js @@ -13,9 +13,12 @@ const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImport /** @typedef {import("../Module")} Module */ class InferAsyncModulesPlugin { - constructor(options) { - const { errorOnMissingAwait = false } = options || {}; - this.errorOnMissingAwait = errorOnMissingAwait; + /** + * @param {Object} options options object + * @param {boolean | "await"=} options.errorOnImport false: no error, true: error when importing async module, "await": error when import async module without import await + */ + constructor({ errorOnImport = false } = {}) { + this.errorOnImport = errorOnImport; } /** * @param {Compiler} compiler webpack compiler @@ -41,12 +44,14 @@ class InferAsyncModulesPlugin { const dep = connection.dependency; if (dep instanceof HarmonyImportDependency && connection.active) { if ( - this.errorOnMissingAwait && + this.errorOnImport && dep instanceof HarmonyImportSideEffectDependency && - !dep.await + (this.errorOnImport === true || !dep.await) ) { const error = new WebpackError( - "Tried to import async module with normal import/export (must use 'import await'/'export await' instead)" + this.errorOnImport === true + ? "Tried to import async module with import/export (must enable experiments.importAsync to allow this)" + : "Tried to import async module with normal import/export (must use 'import await'/'export await' instead)" ); error.module = module; error.loc = dep.loc; diff --git a/lib/dependencies/HarmonyCompatibilityDependency.js b/lib/dependencies/HarmonyCompatibilityDependency.js index 24b46e345..577f3e8e2 100644 --- a/lib/dependencies/HarmonyCompatibilityDependency.js +++ b/lib/dependencies/HarmonyCompatibilityDependency.js @@ -56,13 +56,17 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate } if (moduleGraph.isAsync(module)) { runtimeRequirements.add(RuntimeGlobals.module); + if (usedExports !== false) + runtimeRequirements.add(RuntimeGlobals.exports); initFragments.push( new InitFragment( `${module.moduleArgument}.exports = (async () => {\n`, InitFragment.STAGE_ASYNC_BOUNDARY, 0, undefined, - `\nreturn ${module.exportsArgument};\n})();` + usedExports !== false + ? `\nreturn ${module.exportsArgument};\n})();` + : "\n})();" ) ); } diff --git a/test/cases/async-modules/top-level-await-without-export/index.js b/test/cases/async-modules/top-level-await-without-export/index.js new file mode 100644 index 000000000..9c2c98375 --- /dev/null +++ b/test/cases/async-modules/top-level-await-without-export/index.js @@ -0,0 +1,14 @@ +let value = 0; + +it("should not crash when top level await is used without export", () => { + // wait for itself + return require.cache[module.id].exports.then(() => { + expect(value).toBe(42); + }); +}); + +await new Promise(r => setTimeout(r, 100)); + +value = 42; + +export {};