feat: detect ES modules from top-level await and import.meta#21218
Conversation
Treat top-level await and import.meta as ES module markers, matching Node.js syntax detection. A module using either is now promoted to an ES module without requiring an explicit module type or import/export syntax. Top-level await in such a module no longer throws.
ImportMetaPlugin bails on known members (import.meta.url, .webpack, .main, .env) before the member-chain hooks fire, so cover them explicitly along with the member-chain catch-alls. Add a test for the import.meta.url form.
Replace the per-member hook taps with a single AST scan in the program hook, gated by a cheap source check. Any import.meta usage — including unknown properties — now marks the module as ESM, decoupled from the set of members ImportMetaPlugin supports. Add a test for unknown properties.
A .js module auto-detected as ESM via import.meta stays javascript/auto (strictHarmonyModule: false): strict and namespaced, but module/require interop remains available unlike a strict .mjs/type:module module.
🦋 Changeset detectedLatest commit: 163bbb3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is packaged and the instant preview is available (0be3021). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@0be3021
yarn add -D webpack@https://pkg.pr.new/webpack@0be3021
pnpm add -D webpack@https://pkg.pr.new/webpack@0be3021 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21218 +/- ##
==========================================
+ Coverage 92.75% 92.76% +0.01%
==========================================
Files 591 591
Lines 64366 64466 +100
Branches 17884 17914 +30
==========================================
+ Hits 59703 59805 +102
+ Misses 4663 4661 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
track-returned/index.js uses import.meta.hot, so it is now detected as an ES module, adding the ESM namespace runtime module (246 -> 247 modules). Update the stats snapshot and count. Also add a test asserting a string or comment 'import.meta' does not falsely promote a module to ESM.
Merging this PR will improve performance by 67.76%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
247.6 KB | 360.8 KB | -31.37% |
| ⚡ | Memory | benchmark "lodash", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
859.1 KB | 124.7 KB | ×6.9 |
| ⚡ | Memory | benchmark "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
327.4 KB | 246.8 KB | +32.65% |
| ⚡ | Memory | benchmark "context-esm", scenario '{"name":"mode-production","mode":"production"}' |
9.2 MB | 7.3 MB | +26.31% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/top-level-async-errors-iw22wt (163bbb3) with main (868209f)
Detect import.meta via parser hooks instead of a raw AST scan so webpack pragmas keep working in CommonJS modules: import.meta.webpackHot, import.meta.webpackContext and import.meta.url inside new URL()/new Worker() are consumed by their own plugins first and no longer promote the module to an ES module. Genuine import.meta usage (bare, .url value, .env, unknown members) still marks the module as ESM. Fixes HMR and lazy-compilation.
import.meta.dirname and import.meta.filename are ESM-only Node.js APIs (CommonJS uses __dirname/__filename), so using them marks the module as an ES module, matching Node.js. NodeStuffPlugin still resolves them afterwards.
Export IMPORT_META_NAMES from ImportMetaPlugin as the single source of truth and consume it in HarmonyDetectionParserPlugin, so adding a new import.meta member updates ESM detection automatically.
Summary
Aligns webpack with Node.js module syntax detection: a
.js(javascript/auto) module that uses top-levelawaitorimport.metais now treated as an ES module, with no need fortype: "module",.mjs, orimport/exportsyntax. Previously top-levelawaitin such a file threwTop-level-await is only supported in EcmaScript Modules, andimport.metaleft the module non-ESM.Detection is a single, decoupled AST scan in
HarmonyDetectionParserPlugin(gated by a cheap source check) plus the existing top-level-await hook. Detected modules stayjavascript/auto(strictHarmonyModule: false): they are strict and namespaced, but keep CommonJS interop (module/require) — they are not promoted to strict.mjssemantics, which keeps them consistent withimport/export.jsmodules and avoids removing CommonJS access.What kind of change does this PR introduce?
feat
Did you add tests for your changes?
Yes —
test/cases/async-modules/top-level-await-no-esm-syntaxandtest/configCases/parsing/import-meta-{auto-esm,url-auto-esm,unknown-auto-esm,keeps-commonjs-interop}(cover bareimport.meta, member, unknown property, top-level await, and the strict-ESM-but-keeps-CommonJS-interop contract).Does this PR introduce a breaking change?
Top-level await support is additive (it previously errored). The
import.metacase is a behavior change: a.jsfile usingimport.metawithoutimport/exportis now a strict ES module (strict mode, top-levelthisisundefined, namespace exports), matching Node.js. Code that relied on such a file being sloppy CommonJS could be affected.If relevant, what needs to be documented once your changes are merged or what have you already documented?
Docs could note that top-level
awaitandimport.metano longer requiretype: "module"/.mjsand are auto-detected as ES modules.Use of AI
Yes. Implemented with Claude Code as an assistant — investigation, code, and tests were generated with AI and then verified by the author by running the affected test suites and reviewing the diff.
🤖 Generated with Claude Code
Generated by Claude Code