feat: reduce generated runtime code via method shorthand, spread, and Object.hasOwn#21188
Conversation
…n generated runtime code Adds a RuntimeTemplate.method() helper (object-literal method shorthand with a function/arrow-property fallback) and a new output.environment.spread capability with target detection, then applies them where they shrink the generated runtime: the deferred-namespace Proxy handler, the CommonJS harmony module decorator, and the CSS HMR apply handler. Also converts the remaining safe css removed-chunks guard to optional chaining.
…d runtime code Adds a new output.environment.hasOwn capability (with target detection) and a RuntimeTemplate.objectHasOwn() helper that emits Object.hasOwn(obj, prop) when the target environment supports it, falling back to Object.prototype.hasOwnProperty.call otherwise. Applies it to the __webpack_require__.o helper (emitted in nearly every bundle) and the deferred-namespace property copy.
… and HMR runtime Extends the environment-gated code reduction to the remaining sites: object spread for worker option merging (was Object.assign), Object.hasOwn in the HMR runtime, and optional chaining for the css-modules and lazy-compilation HMR self-acceptance guards. Each falls back to the previous output when the target environment does not support the feature.
🦋 Changeset detectedLatest commit: ff75786 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 (8596a28). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@8596a28
yarn add -D webpack@https://pkg.pr.new/webpack@8596a28
pnpm add -D webpack@https://pkg.pr.new/webpack@8596a28 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #21188 +/- ##
==========================================
- Coverage 92.70% 92.70% -0.01%
==========================================
Files 588 588
Lines 64091 64118 +27
Branches 17785 17797 +12
==========================================
+ Hits 59416 59438 +22
- Misses 4675 4680 +5
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:
|
…sOwn Regenerate the ecmaVersion browserslist inline environment snapshots to include the new `spread` and `hasOwn` capabilities, and the es2022 html ConfigTest / ConfigCacheTest snapshots where `__webpack_require__.o` now uses `Object.hasOwn`.
Merging this PR will improve performance by ×2.2
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Memory | benchmark "lodash", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
858.6 KB | 129.2 KB | ×6.6 |
| ⚡ | Memory | benchmark "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
1,048.3 KB | 325.4 KB | ×3.2 |
| ⚡ | Memory | benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
786.6 KB | 563.9 KB | +39.5% |
| ⚡ | Memory | benchmark "wasm-modules-async", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
488.8 KB | 389 KB | +25.65% |
| ⚡ | Memory | benchmark "future-defaults", scenario '{"name":"mode-production","mode":"production"}' |
9.1 MB | 7.4 MB | +23.7% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/env-code-reduction (ff75786) with main (877f3dc)
The generated runtime executes in the Jest process, so es2022/node16.9 fixtures emitting `Object.hasOwn` crash on Node < 16.9. Mirror the existing optionalChaining gate: force `environment.hasOwn = false` in the config/hot/ watch/cases harnesses on old Node (except `ecmaVersion`), and raise the html snapshot fixtures' filters to require `Object.hasOwn` since their es2022 snapshots now use it.
Adds a new output.environment.symbol capability (with target detection) and a RuntimeTemplate.supportsSymbol() check, then drops the `typeof Symbol !== 'undefined' &&` guard in __webpack_require__.r (makeNamespaceObject) when the target environment is known to have `Symbol`, falling back to the guarded form otherwise.
…snapshots The `supportsObjectHasOwn` helper referenced `Object.hasOwn` directly, which the test tsconfig's pre-es2022 lib rejects; cast through EXPECTED_ANY. Also regenerate the ecmaVersion browserslist inline environment snapshots to include the new `symbol` key.
The makeNamespaceObject runtime is ~28 bytes smaller when the Symbol global guard is dropped, shifting the entryB.js asset size in Stats.test.js.
Summary
Follow-up to #21186 (optional chaining in runtime code). This emits smaller runtime code on modern targets by using ECMAScript features webpack already models, and adds three new
output.environmentcapabilities for features it didn't:RuntimeTemplate.method()helper uses object method shorthand (get(){…}) instead ofget: function/arrowfor object-literal methods (deferred-namespace Proxy handler, CommonJS harmony decorator, CSS HMR apply handler).output.environment.spread(ES2018) —{ ...opts, type }instead ofObject.assign({}, opts, { type })for worker options, and[...a, x]instead ofa.concat([x])in the deferred-namespaceownKeys.output.environment.hasOwn(ES2022) —Object.hasOwn(obj, prop)instead ofObject.prototype.hasOwnProperty.call(obj, prop)in__webpack_require__.o(nearly every bundle) and the HMR runtime.output.environment.symbol(ES2015) — drops thetypeof Symbol !== 'undefined' &&guard in__webpack_require__.r(makeNamespaceObject).Each conversion falls back to the exact previous output when the target environment doesn't support the feature, so default/legacy builds are byte-identical. All new capability flags use target detection (
config/target.js, browserslist) and are non-optimistic where the feature is new. No related issue.What kind of change does this PR introduce?
feat
Did you add tests for your changes?
Yes — unit tests for
RuntimeTemplate.methodandRuntimeTemplate.objectHasOwnintest/RuntimeTemplate.unittest.js, plus updatedDefaults/target-browserslist/Clisnapshots covering the new flags and target detection. The generated runtime is exercised end-to-end by the existingConfigTestCases/HotTestCasessuites in both the modern and fallback forms. The test harnesses forceenvironment.hasOwn = falseon Node < 16.9 (mirroring the existingoptionalChaininggate) since the generated runtime runs in the Jest process.Does this PR introduce a breaking change?
No. New options default conservatively (
hasOwnis non-optimistic likeimportMetaDirnameAndFilename), so output is unchanged for targets that don't report support.If relevant, what needs to be documented once your changes are merged or what have you already documented?
The three new
output.environmentoptions —spread,hasOwn, andsymbol— should be added to theoutput.environmentdocumentation alongside the existing capability flags.Use of AI
AI was used: Claude (Claude Code) helped locate candidate sites, write the helpers and the
spread/hasOwn/symbolplumbing (schema, target detection, defaults), apply the edits, and run/compare the test suites. All changes were reviewed by me.