Skip to content

Default output.globalObject to globalThis for universal targets#21314

Merged
alexander-akait merged 10 commits into
mainfrom
fix/universal-global-object
Jul 1, 2026
Merged

Default output.globalObject to globalThis for universal targets#21314
alexander-akait merged 10 commits into
mainfrom
fix/universal-global-object

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #6522. A UMD (or global) library built for both web and node — the universal target (["web", "node"] / "universal") — resolved output.globalObject to self, which is undefined in Node, so the emitted })(self, …) wrapper threw ReferenceError: self is not defined when the bundle was required there. output.globalObject now defaults to globalThis for the universal target when no merged target reports globalThis support (tp.globalThis === undefined); a target that reports it as absent (false, or null for mixed support such as node10 + node12) still falls back to self.

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes. test/configCases/target/universal-global-object/ builds the universal non-ESM library as both umd and global (the two library types that embed the global object) and asserts it loads in Node without a self ReferenceError (gated to Node 12+, where the emitted globalThis exists, via test.filter.js). test/Defaults.unittest.js adds cases for a universal target whose version lacks globalThis (false) and for mixed globalThis support (null), both keeping output.globalObject: "self".

Does this PR introduce a breaking change?

No. It only changes the default for the universal target, which was previously broken (it emitted self, which crashes in Node).

If relevant, what needs to be documented once your changes are merged or what have you already documented?

n/a

Use of AI

AI (Claude Code) was used to investigate the defaults resolution, map affected target/library combinations, write the tests, and draft this PR. All changes were reviewed and verified against the full ConfigTestCases, StatsTestCases, and Defaults.unittest suites.

🤖 Generated with Claude Code

A UMD library targeting both web and node (the universal signature,
tp.web === null && tp.node === null) only resolved output.globalObject to
`globalThis` when output was ESM. Classic non-module UMD builds fell back to
`self`, which is undefined in Node — the #6522 symptom. Key off target
support for `globalThis` instead, unless a merged version explicitly lacks it.
Replace the issue-6522 case with a minimal multi-compiler test under
target/ that builds the universal (web + node) non-ESM library as both
`umd` and `global` — the two library types that embed output.globalObject.
Drop the unnecessary node/externalsPresets options; the bundle simply
running in node proves `self` (undefined there) was not used. Tighten the
defaults.js comment.
Any ESM-capable runtime supports `globalThis` (ES2020), so module output
should never fall back to `self`. This extends the universal-target fix to
plain `web`/`webworker` ESM builds. Update the output-module defaults snapshot.
Set output.environment.globalThis via conditionallyOptimistic(tp, output.module),
matching the sibling `module`/`dynamicImport` flags: ESM output only runs where
`globalThis` (ES2020) exists. This renders shimmed `global` as the `globalThis`
literal and drops the global runtime module for module output.

Revert defaulting output.globalObject to `globalThis` for plain web ESM: `self`
already works in every web context, and switching the chunk-loading global to
`globalThis` broke web+ESM jsonp chunk loading. The universal (web + node) fix
is unaffected. Update the output-module defaults snapshots.
Covers the tp.globalThis !== false guard: output.globalObject stays self and
environment.globalThis stays false when a merged version (node<12) reports no
globalThis, even for the universal target.
Copilot AI review requested due to automatic review settings July 1, 2026 12:08
@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c8e0314

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

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

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 Jul 1, 2026

Copy link
Copy Markdown
Contributor

This PR is packaged and the instant preview is available (676f564).

Install it locally:

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes universal (web + node) library builds that previously defaulted output.globalObject to self (breaking in Node) by preferring globalThis when the merged target supports it, and it aligns output.environment.globalThis inference with existing ESM-output environment inference patterns.

Changes:

  • Default output.globalObject to "globalThis" for universal targets unless the merged target explicitly reports globalThis: false.
  • Infer output.environment.globalThis: true for ESM output via conditionallyOptimistic(tp.globalThis, output.module).
  • Add regression coverage via a new config case and updated Defaults snapshots (including a node10 universal-merge guard case).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
lib/config/defaults.js Adjusts output.globalObject defaulting for universal targets and infers environment.globalThis for ESM output.
test/Defaults.unittest.js Updates inline snapshots for ESM output and adds a universal + node10 case ensuring globalThis remains disabled when absent.
test/configCases/target/universal-global-object/webpack.config.js New integration config compiling universal UMD + global library outputs with classic (non-ESM) output.
test/configCases/target/universal-global-object/test.config.js Selects the correct bundle per sub-configuration for the new multi-compiler test case.
test/configCases/target/universal-global-object/index.js Runtime assertion that the bundle loads in Node (i.e., does not reference self).
.changeset/universal-umd-global-object.md Adds a patch changeset describing the default behavior changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/configCases/target/universal-global-object/webpack.config.js Outdated
Comment thread test/configCases/target/universal-global-object/index.js Outdated
Comment thread test/Defaults.unittest.js Outdated
Comment thread .changeset/universal-umd-global-object.md Outdated
Address Copilot review: compress the multi-line comments in the new config
case, index.js, and the Defaults guard case to single lines, and shorten the
changeset to one short sentence, per the repo's comment conventions.
@codspeed-hq

codspeed-hq Bot commented Jul 1, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 37.53%

⚠️ 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

⚡ 1 improved benchmark
❌ 5 regressed benchmarks
✅ 138 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "side-effects-reexport", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 126.9 KB 859.3 KB -85.23%
Memory benchmark "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 326.6 KB 855.8 KB -61.84%
Memory benchmark "many-modules-esm", scenario '{"name":"mode-development","mode":"development"}' 1.2 MB 1.9 MB -38.69%
Memory benchmark "wasm-modules-async", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 190 KB 245.9 KB -22.73%
Memory benchmark "many-chunks-esm", scenario '{"name":"mode-production","mode":"production"}' 7.4 MB 9.4 MB -20.61%
Memory benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 360.1 KB 128.4 KB ×2.8

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/universal-global-object (c8e0314) with main (1a97d13)

Open in CodSpeed

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.87%. Comparing base (3cb8578) to head (c8e0314).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #21314   +/-   ##
=======================================
  Coverage   92.86%   92.87%           
=======================================
  Files         594      594           
  Lines       65210    65210           
  Branches    18143    18143           
=======================================
+ Hits        60559    60562    +3     
+ Misses       4651     4648    -3     
Flag Coverage Δ
css-parsing 28.60% <100.00%> (ø)
html5lib 31.13% <100.00%> (ø)
integration 88.89% <100.00%> (+<0.01%) ⬆️
test262 45.58% <100.00%> (+0.02%) ⬆️
unit 43.09% <100.00%> (+0.01%) ⬆️

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.

The emitted UMD/global bundles reference the globalThis runtime global, which
does not exist before Node 12, so the config case crashed with 'globalThis is
not defined' on the legacy-node CI matrix. Gate it with supportsGlobalThis().
Copilot AI review requested due to automatic review settings July 1, 2026 12:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comment thread lib/config/defaults.js Outdated
Comment thread lib/config/defaults.js Outdated
Comment thread test/configCases/target/universal-global-object/index.js Outdated
Address Copilot review: mergeTargetProperties yields tp.globalThis === null
for a universal target with mixed globalThis support (e.g. node10 + node12), and
the previous !== false guard treated that as supported. Key off === undefined so
mixed (null) and unsupported (false) both fall back to self. Add a Defaults case
for the mixed target, and tighten the two inline/test comments.
It broke the Node 10 CI matrix: making output.environment.globalThis true for
universal module output renders the shimmed `global` as the `globalThis`
literal, which is undefined on Node <12, so runtime environment checks (e.g.
hot/lazy-compilation-universal.js picking the node vs web backend) misfire and
pick the web path. The optimism only applied to the version-agnostic universal
target (which spans old Node), so it had no safe benefit. The universal
output.globalObject fix is unaffected.
Copilot AI review requested due to automatic review settings July 1, 2026 13:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread lib/config/defaults.js Outdated
Comment on lines +1732 to +1734
// Universal target (web + node): default to `globalThis` (the only accessor
// available everywhere) when no merged target reports it; a reported value
// (`false`, or `null` for mixed support) falls through to `self`.
Comment on lines +1 to +4
it("should load in node without referencing `self`", () => {
// `self` (undefined in node) would throw at load; reaching here proves it isn't referenced.
expect(hello()).toBe("hello");
});
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging fix/universal-global-object into main will be
99.36%
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/module-federation
   test.filter.js100%100%100%100%
examples/reexport-components
   test.filter.js100%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-emscripten
   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%
   CircularModulesPlugin.js98.81%100%100%98.81%136
   CleanPlugin.js99.12%100%100%99.12%207, 227
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.43%100%100%98.43%1641, 1960, 1967, 1975, 1997, 2000, 2939, 3418–3419, 3451, 4117, 4147, 4200–4201, 4205, 4210, 4226–4227, 4241–4242, 4247–4248, 4725, 4751, 526, 531, 5559, 5591, 5608, 5624, 5640, 5655, 5680–5681, 5683, 6011, 6016, 6022, 6025, 6037, 6039, 6043, 6059, 6074, 6106, 6160, 6184, 6298, 777–778
   Compiler.js99.56%100%100%99.56%1147–1148, 1156
   ConcatenationScope.js98.65%100%100%98.65%195
   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.js98.99%100%100%98.99%172–173, 189, 208, 282
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.51%100%100%98.51%479, 525
   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.55%100%100%98.55%1100, 1103, 502–506, 508, 654
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.52%100%100%99.52%182, 2382–2383, 2386, 2397, 2408, 2419, 280, 3823, 3838, 3862
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.42%100%100%98.42%413, 422, 424, 428
   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%661
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.89%100%100%97.89%1227, 1230, 1247, 1264, 1511, 1545, 1561, 1648, 2004, 2303, 2308–2318, 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%527–528, 533, 535, 599
   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, 889
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.43%100%100%99.43%308–309
   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.63%100%100%98.63%10, 119, 549, 581, 631, 905
   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.33%100%100%97.33%282, 306, 309, 36, 362, 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/bun
   BunTargetPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%75, 87, 95
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%107, 114–115, 123, 90
   PackFileCacheStrategy.js96.40%100%100%96.40%1251, 1351, 1355, 1417, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 723, 729, 763, 769, 775, 780, 791, 800, 805–806, 808, 825, 831–832, 834
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%
   defaults.js99.33%100%100%99.33%1468–1470, 1478, 274,

@alexander-akait alexander-akait merged commit 676f564 into main Jul 1, 2026
63 checks passed
@alexander-akait alexander-akait deleted the fix/universal-global-object branch July 1, 2026 15:06
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.

Can't create UMD build which can be required by Node

2 participants