Skip to content

output.library.type: 'module' defaults output.module to false, emitting invalid JavaScript (export inside IIFE) #22061

Closed

Description

@Pcmhacker-piro

Have you used AI?

Yes

Which part of webpack is affected?

config (options, defaults, validation)

Bug Description

Following #22011 / commit c4cd6ca4 which removed the experiments.outputModule experiment flag, ESM output options (output.module, library types "module" / "modern-module", etc.) became first-class configurations.

However, in lib/config/defaults.js:1645, output.module defaults to true only for universal, deno, or bun targets:

F(output, "module", () =>
	Boolean(
		tp &&
		((tp.node === null && tp.web === null && tp.module !== false) ||
			tp.deno === true ||
			tp.bun === true)
	)
);

When building an ESM library (e.g. for web or node) by configuring:

module.exports = {
  output: {
    library: {
      type: "module"
    }
  }
};

(or on an entry via entry[name].library.type: "module"):

  1. output.module remains false by default.
  2. output.iife remains true by default for non-module output.
  3. ModuleLibraryPlugin emits an export default ... or export { ... } statement.
  4. Because output.module is false, the bundle is wrapped in an IIFE:
(() => {
  // ...
  let __webpack_exports__ = __webpack_require__("./index.js");
  export default __webpack_exports__;
})();
  1. In production mode, minification fails with:
    "Export" statement may only appear at the top level [bundle.js:45,undefined] (Terser syntax error).
  2. In development mode (or without minifier), runtime execution fails with:
    SyntaxError: Unexpected token 'export'.

Additionally, if a user explicitly sets output: { module: false, library: { type: "module" } }, webpack emits this invalid JavaScript syntax without any validation or error, whereas previously it threw:
library type "module" is only allowed when 'experiments.outputModule' is enabled.

Link to Minimal Reproduction and steps to reproduce

Minimal reproduction script:

const webpack = require("webpack");
const vm = require("vm");
const fs = require("fs");
const path = require("path");

const outDir = path.resolve("./dist-repro");
webpack({
  mode: "development",
  entry: "./test/fixtures/a.js",
  output: {
    path: outDir,
    filename: "bundle.js",
    library: {
      type: "module"
    }
  }
}, (err, stats) => {
  if (err) throw err;
  const code = fs.readFileSync(path.join(outDir, "bundle.js"), "utf8");
  try {
    new vm.Script(code);
    console.log("Syntax is valid!");
  } catch (e) {
    console.error("Syntax Error in emitted bundle:", e.message);
  }
  fs.rmSync(outDir, { recursive: true, force: true });
});

Steps:

  1. Run the script above on webpack main.
  2. Output displays Syntax Error in emitted bundle: Unexpected token 'export'.
  3. Switch mode to "production" and see Terser throw: "Export" statement may only appear at the top level.

Expected Behavior

  1. When output.library.type or any entry's library.type is "module" or "modern-module", output.module should default to true (if not explicitly specified).
  2. If output.module: false is explicitly specified with library.type: "module" or "modern-module", webpack should throw a descriptive error (e.g. 'library type "module" is only allowed when \'output.module\' is enabled') instead of emitting invalid JavaScript syntax.

Actual Behavior

output.module defaults to false, and webpack wraps the export statement inside an IIFE function, creating invalid JavaScript syntax that fails parsing in JS engines and minifiers.

Environment

System:
OS: macOS 26.6.2
CPU: (8) arm64 Apple M3
Binaries:
Node: 22.23.1
npm: 10.9.8
yarn: 1.22.22
Packages:
webpack: 5.110.3 (main)

Is this a regression?

Yes (please specify version below)

Last Working Version

Introduced in #22011 / commit c4cd6ca4 (when experiments.outputModule was removed)

Activity

  1. added
    area: configOptions, defaults, validation (lib/config, schemas/)
    area: runtimeEmitted runtime and chunk loading across targets (lib/runtime, lib/web, lib/node, lib/esm)
    regressionWorked in a previous release; pair with the Affected version field
    on Sep 11, 2026
  2. Pcmhacker-piro commented on Sep 11, 2026

    @Pcmhacker-piro
    Author

    I have opened a PR with the fix, unit tests, and integration test at #22063.

  3. Pcmhacker-piro commented on Sep 11, 2026

    @Pcmhacker-piro
    Author

    Re-opened clean PR with all checks passing at #22064.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: configOptions, defaults, validation (lib/config, schemas/)area: runtimeEmitted runtime and chunk loading across targets (lib/runtime, lib/web, lib/node, lib/esm)regressionWorked in a previous release; pair with the Affected version field

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions