Skip to content

feat: detect ES modules from top-level await and import.meta#21218

Merged
alexander-akait merged 10 commits into
mainfrom
claude/top-level-async-errors-iw22wt
Jun 19, 2026
Merged

feat: detect ES modules from top-level await and import.meta#21218
alexander-akait merged 10 commits into
mainfrom
claude/top-level-async-errors-iw22wt

Conversation

@alexander-akait

Copy link
Copy Markdown
Member

Summary

Aligns webpack with Node.js module syntax detection: a .js (javascript/auto) module that uses top-level await or import.meta is now treated as an ES module, with no need for type: "module", .mjs, or import/export syntax. Previously top-level await in such a file threw Top-level-await is only supported in EcmaScript Modules, and import.meta left 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 stay javascript/auto (strictHarmonyModule: false): they are strict and namespaced, but keep CommonJS interop (module/require) — they are not promoted to strict .mjs semantics, which keeps them consistent with import/export .js modules 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-syntax and test/configCases/parsing/import-meta-{auto-esm,url-auto-esm,unknown-auto-esm,keeps-commonjs-interop} (cover bare import.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.meta case is a behavior change: a .js file using import.meta without import/export is now a strict ES module (strict mode, top-level this is undefined, 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 await and import.meta no longer require type: "module"/.mjs and 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

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-bot

changeset-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 163bbb3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack Minor

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

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

This PR is packaged and the instant preview is available (0be3021).

Install it locally:

  • npm
npm i -D webpack@https://pkg.pr.new/webpack@0be3021
  • yarn
yarn add -D webpack@https://pkg.pr.new/webpack@0be3021
  • pnpm
pnpm add -D webpack@https://pkg.pr.new/webpack@0be3021

@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.76%. Comparing base (4b00f6e) to head (163bbb3).
⚠️ Report is 2 commits behind head on main.

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     
Flag Coverage Δ
css-parsing 28.72% <27.65%> (+0.02%) ⬆️
html5lib 31.16% <27.65%> (+0.01%) ⬆️
integration 88.74% <97.87%> (+0.02%) ⬆️
test262 45.53% <87.23%> (+0.03%) ⬆️
unit 41.11% <68.08%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.
@codspeed-hq

codspeed-hq Bot commented Jun 18, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 67.76%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 3 improved benchmarks
❌ 1 regressed benchmark
✅ 140 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

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)

Open in CodSpeed

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.
@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging claude/top-level-async-errors-iw22wt into main will be
99.35%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
bin
   webpack.js98.77%100%100%98.77%91
examples
   build-common.js100%100%100%100%
   buildAll.js100%100%100%100%
   examples.js100%100%100%100%
   template-common.js98.21%100%100%98.21%72
examples/custom-javascript-parser
   test.filter.js100%100%100%100%
examples/custom-javascript-parser/internals
   acorn-parse.js100%100%100%100%
   meriyah-parse.js100%100%100%100%
   oxc-parse.js91.30%100%100%91.30%140, 142–143, 145, 147, 153–154, 161, 168, 90
examples/markdown
   webpack.config.mjs100%100%100%100%
examples/typescript
   test.filter.js100%100%100%100%
examples/typescript-non-erasable
   test.filter.js50%100%100%50%5
examples/virtual-modules
   test.filter.js100%100%100%100%
examples/wasm-bindgen-esm
   test.filter.js100%100%100%100%
examples/wasm-complex
   test.filter.js100%100%100%100%
examples/wasm-simple
   test.filter.js100%100%100%100%
examples/wasm-simple-source-phase
   test.filter.js100%100%100%100%
lib
   APIPlugin.js100%100%100%100%
   AsyncDependenciesBlock.js100%100%100%100%
   AutomaticPrefetchPlugin.js100%100%100%100%
   BannerPlugin.js100%100%100%100%
   Cache.js98.21%100%100%98.21%101
   CacheFacade.js100%100%100%100%
   Chunk.js99.72%100%100%99.72%39
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CleanPlugin.js99.15%100%100%99.15%206, 226
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.43%100%100%98.43%1618, 1937, 1944, 1952, 1974, 1977, 2917, 3396–3397, 3429, 4095, 4125, 4178–4179, 4183, 4188, 4204–4205, 4219–4220, 4225–4226, 4703, 4729, 514, 519, 5537, 5569, 5586, 5602, 5618, 5633, 5658–5659, 5661, 5989, 5994, 6000, 6003, 6015, 6017, 6021, 6037, 6052, 6084, 6138, 6162, 6276, 764–765
   Compiler.js99.56%100%100%99.56%1135–1136, 1144
   ConcatenationScope.js98.59%100%100%98.59%189
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js100%100%100%100%
   ContextModuleFactory.js97.40%100%100%97.40%258, 395, 418, 420, 424, 433–434
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js99%100%100%99%171–172, 188, 207, 281
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.35%100%100%98.35%435, 481
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DotenvPlugin.js98.41%100%100%98.41%378, 391–392
   DynamicEntryPlugin.js100%100%100%100%
   EntryOptionPlugin.js100%100%100%100%
   EntryPlugin.js100%100%100%100%
   Entrypoint.js100%100%100%100%
   EnvironmentPlugin.js97.14%100%100%97.14%49
   ErrorHelpers.js100%100%100%100%
   EvalDevToolModulePlugin.js100%100%100%100%
   EvalSourceMapDevToolPlugin.js100%100%100%100%
   ExportsInfo.js100%100%100%100%
   ExportsInfoApiPlugin.js100%100%100%100%
   ExternalModule.js98.50%100%100%98.50%1057, 1060, 445–449, 451, 597
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.50%100%100%99.50%182, 2252–2253, 2256, 2267, 2278, 2289, 278, 3693, 3708, 3732
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.46%100%100%98.46%425, 434, 436, 440
   FlagDependencyUsagePlugin.js100%100%100%100%
   FlagEntryExportAsUsedPlugin.js100%100%100%100%
   Generator.js100%100%100%100%
   HotModuleReplacementPlugin.js100%100%100%100%
   HotUpdateChunk.js100%100%100%100%
   IgnorePlugin.js100%100%100%100%
   IgnoreWarningsPlugin.js100%100%100%100%
   InitFragment.js100%100%100%100%
   JavascriptMetaInfoPlugin.js100%100%100%100%
   LazyBarrel.js100%100%100%100%
   LibraryTemplatePlugin.js100%100%100%100%
   LoaderOptionsPlugin.js100%100%100%100%
   LoaderTargetPlugin.js100%100%100%100%
   MainTemplate.js100%100%100%100%
   ManifestPlugin.js100%100%100%100%
   Module.js98.50%100%100%98.50%1285, 1290, 1350, 1364, 1426, 1435
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1005
   ModuleGraphConnection.js100%100%100%100%
   ModuleInfoHeaderPlugin.js100%100%100%100%
   ModuleNotFoundError.js100%100%100%100%
   ModuleProfile.js100%100%100%100%
   ModuleSourceTypeConstants.js100%100%100%100%
   ModuleTemplate.js100%100%100%100%
   ModuleTypeConstants.js100%100%100%100%
   MultiCompiler.js99.69%100%100%99.69%659
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.90%100%100%97.90%1240, 1243, 1260, 1277, 1524, 1558, 1574, 1661, 2017, 2316, 2321–2331, 419, 423, 577
   NormalModuleFactory.js99.47%100%100%99.47%1083, 1392, 486, 498
   NormalModuleReplacementPlugin.js100%100%100%100%
   NullFactory.js100%100%100%100%
   OptimizationStages.js100%100%100%100%
   OptionsApply.js100%100%100%100%
   Parser.js100%100%100%100%
   PlatformPlugin.js100%100%100%100%
   PrefetchPlugin.js100%100%100%100%
   ProgressPlugin.js98.85%100%100%98.85%519–520, 525, 527, 591
   ProvidePlugin.js100%100%100%100%
   RawModule.js100%100%100%100%
   RecordIdsPlugin.js100%100%100%100%
   RequestShortener.js100%100%100%100%
   ResolverFactory.js100%100%100%100%
   RuntimeGlobals.js100%100%100%100%
   RuntimeModule.js100%100%100%100%
   RuntimePlugin.js100%100%100%100%
   RuntimeTemplate.js100%100%100%100%
   SelfModuleFactory.js100%100%100%100%
   SingleEntryPlugin.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js98.62%100%100%98.62%220, 224, 226, 419, 430, 891
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.17%100%100%99.17%176–177
   UseStrictPlugin.js100%100%100%100%
   WarnCaseSensitiveModulesPlugin.js100%100%100%100%
   WarnDeprecatedOptionPlugin.js100%100%100%100%
   WarnNoModeSetPlugin.js100%100%100%100%
   WatchIgnorePlugin.js100%100%100%100%
   Watching.js100%100%100%100%
   WebpackError.js100%100%100%100%
   WebpackIsIncludedPlugin.js100%100%100%100%
   WebpackOptionsApply.js100%100%100%100%
   WebpackOptionsDefaulter.js100%100%100%100%
   buildChunkGraph.js99.87%100%100%99.87%371
   cli.js98.62%100%100%98.62%10, 119, 545, 577, 627, 897
   index.js99.72%100%100%99.72%184
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js96.33%100%100%96.33%10, 198, 220, 222
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModule.js100%100%100%100%
   AssetModulesPlugin.js97.32%100%100%97.32%281, 305, 308, 36, 360, 41
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%71, 83, 91
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%106, 113–114, 122, 89
   PackFileCacheStrategy.js96.40%100%100%96.40%1250, 1350, 1354, 1416, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 722, 728, 762, 768, 774, 779, 790, 799, 804–805, 807, 824, 830–831, 833
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%
   defaults.js99.31%100%100%99.31%1444–1446, 1454, 274, 277, 282, 286
   defineConfig.js100%100%100%100%
   normalization.js99.02%100%100%99.02%191–192, 258, 273
   target.js100%100%100%100%
lib/container
   

@alexander-akait alexander-akait merged commit 0be3021 into main Jun 19, 2026
66 checks passed
@alexander-akait alexander-akait deleted the claude/top-level-async-errors-iw22wt branch June 19, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant