Visitar URL original
refactor: type rule effect values, the typeof hook's node and the statement-type hook maps by alexander-akait · Pull Request #23309 · webpack/webpack · GitHub
Skip to content

refactor: type rule effect values, the typeof hook's node and the statement-type hook maps - #23309

Open
alexander-akait wants to merge 2 commits into
mainfrom
refactor/typed-tapable-interceptors
Open

alexander-akait wants to merge 2 commits into
mainfrom
refactor/typed-tapable-interceptors

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Three type fixes, with no runtime change:

  • NormalModuleFactory reads each rule effect's value once, as unknown. The value is a loader for a use effect, or else the rule's value for the setting the effect is named for. It's only merged into the module's settings or handed on, so it no longer inherits the any of the rules' open option bags. This removes 12 untyped sites; the public EffectBasic/EffectUse types are unchanged.
  • The parser's preStatementByType and blockPreStatementByType are now TypedHookMap<Record<string, StatementHook>>. They're keyed by a statement's type name, so the parser's own interceptor that watches their keys gets a string instead of any. That removes 2 untyped sites. StatementHook names the hook type they already used.
  • The typeof hook is now handed a UnaryExpression. The hook takes [Expression], but the walk handed it a node typed only as Node. The interceptor's any[] arguments hid the mismatch. With typed interceptor arguments in tapable (fix: type the call and loop interceptor arguments as the hook's own tapable#288) it becomes a type error, so this fixes it ahead of that.

Untyped goes from 69 to 55. With webpack/tapable#288 it drops to 54 (the call interceptor in Compilation).

What kind of change does this PR introduce?

refactor

Did you add tests for your changes?

No, these are type-only changes. These pass:

Does this PR introduce a breaking change?

No runtime change. In types.d.ts, preStatementByType and blockPreStatementByType change from HookMap<StatementHook> to TypedHookMap<Record<string, StatementHook>>. Their get/for keys are now typed string (a statement type name) instead of any, and the hook each returns is the same. Nothing else changes.

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

n/a

Use of AI

Written with Claude Code (investigation, implementation, verification and this description), directed and reviewed by me.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HArJAXZagrHVVNLZZk9soh

Summary by CodeRabbit

  • Chores
    • Refined internal type handling across JavaScript parsing and module rule processing. Loader selection and other runtime behavior remain unchanged, and there are no changes to user-facing functionality or capabilities. No new features or bug fixes are included in this update.

…ok's node

A rule effect's value is only merged into the module's settings or handed on as
a loader, so `NormalModuleFactory` reads it once as `unknown` instead of
inheriting the `any` of the rule's open option bags. The `typeof` hook takes
an `Expression`; the node the walk hands it is now typed `UnaryExpression`,
which a typed interceptor `call` in tapable requires.
@changeset-bot

changeset-bot Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 970c9cf

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai coderabbitai Bot added the area: parser JavaScript parsing and dependency analysis (lib/javascript, lib/dependencies) label Nov 13, 2026
@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The parser defines and uses a shared statement-hook type and adds a UnaryExpression annotation. The module rule loop stores effect values in an unknown local and applies branch-specific casts. Loader selection and settings merge behavior remain unchanged.

Changes: Type annotations

Layer / File(s) Summary
Parser and rule effect types
lib/javascript/JavascriptParser.js, lib/module/NormalModuleFactory.js
The parser hook maps use the shared StatementHook type, and a local is annotated as UnaryExpression. Rule effect values use an unknown local with casts for loader inputs and settings values.

Suggested labels: area: parser

Suggested reviewers: bjohansebas

Change: Refactor

Merge Risk: Minimal · up to 970c9

This is a type-only refactor with no runtime behavior change, so no actionable merge risk remains.

Pre-merge checks: Passed 4

Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title uses the prefix "refactor:", which matches the required conventional-commit type and has no scope. It names the changes: typing rule effect values, the typeof hook's node, and the statement-…
Full details: Title check

Explanation

The title uses the prefix "refactor:", which matches the required conventional-commit type and has no scope. It names the changes: typing rule effect values, the typeof hook's node, and the statement-type hook maps. These match the PR objectives and the raw summary. The title does not name the branch prefix, which the requirement also asks to match, so the branch name could not be checked from the provided context.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 99128fd and 970c9cf.

Files ignored due to path filters: 1

  • types.d.ts is excluded by !types.d.ts
Files selected for processing: 1

  • lib/javascript/JavascriptParser.js

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Reports for 970c9cf, computed by this pull request's own code. Each section updates when its workflow finishes.


Preview package

This PR is packaged and the instant preview of its latest commit is available.

Install it locally:

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

From Preview package run #12849.


Generated code size

Comparing base (bf1b2dc) to head (970c9cf). Merging this pull request will not change the code webpack generates.

What moved Tests Assets Gzip Raw Test edit
Changed, test untouched 0 0 — — —
Changed, test edited 0 0 — — —
New 0 0 — — —
Deleted 0 0 — — —
Unchanged — 10392 — — —

Read Changed, test untouched first: the test is byte-identical on both sides, so webpack generated the difference. Changed, test edited moved partly because the test did — Test edit is how many bytes the test gained, and a bundle that grew by less is not a regression. New and Deleted are whole files, not deltas — a new asset was created, not grown, so it reads gray (⚪ added) — and adding tests cannot bury a real change. Gzip decides, because that is what users download; raw is the tiebreak, and brotli and zstd are per asset below.

Sample apps

No sample app changed size.

26 unchanged app(s)
App Gzip Gzip change Raw Raw change
library amd 1.38 KiB — 2.68 KiB —
library amd-require 1.38 KiB — 2.67 KiB —
library assign 1.38 KiB — 2.67 KiB —
library assign-properties 1.42 KiB — 2.80 KiB —
library commonjs 1.38 KiB — 2.66 KiB —
library commonjs-module 1.38 KiB — 2.67 KiB —
library commonjs-static 1.55 KiB — 3.54 KiB —
library commonjs2 1.38 KiB — 2.67 KiB —
library global 1.38 KiB — 2.66 KiB —
library jsonp 1.37 KiB — 2.66 KiB —
library module 1.21 KiB — 2.35 KiB —
library self 1.38 KiB — 2.66 KiB —
library system 1.41 KiB — 2.73 KiB —
library this 1.38 KiB — 2.66 KiB —
library umd 1.44 KiB — 2.86 KiB —
library umd2 1.44 KiB — 2.86 KiB —
library var 1.38 KiB — 2.67 KiB —
library window 1.38 KiB — 2.66 KiB —
node-ssr 96.36 KiB — 371.30 KiB —
node-ssr-esm 96.61 KiB — 372.00 KiB —
react-100 32.17 KiB — 150.55 KiB —
react-1k 73.34 KiB — 907.38 KiB —
react-1k-esm 72.54 KiB — 906.44 KiB —
react-5k 191.49 KiB — 4.08 MiB —
webworker 36.60 KiB — 95.02 KiB —
webworker-esm 35.67 KiB — 94.21 KiB —

No asset changed size.

No runtime gained or lost a runtime module.

Built test/configCases with the defaults a user gets, and the apps in test/sizeCoreReports (apps/): 2520 test(s), 10392 asset(s), 109 emitted nothing.

From Generated code size run #7484.


Types Coverage

Typed lines: 99.71% (184735 of 185274)

Kind Base Head Change
any 48 48 —
unknown 54 57 🔴 +3
object 18 18 —
Function 1 1 —
untyped 69 55 🟢 −14

any counts EXPECTED_ANY, a written any and the any TypeScript infers where no type is written, such as const map = new Map() or const list = []. unknown counts a written unknown; ThrownValue, the unknown a throw or a rejected promise hands on, is left out. object counts EXPECTED_OBJECT and Object, Function counts EXPECTED_FUNCTION. Untyped counts the identifiers whose type is or holds any, wherever it comes from; the any TypeScript's built-in lib and Node.js's @​types/node write in their own signatures, such as the replacer of String#replace or a stream's chunk, is left out, and so is the open fallback beside declared fields, such as KnownBuildInfo & Record<string, any>, though reading a key no field declares is counted.

Changed by this pull request

File Typed lines any unknown object Function untyped
lib/javascript/JavascriptParser.js 100.00% 1 0 (🟢 −2)
lib/module/NormalModuleFactory.js 100.00% 3 (🔴 +3) 3 (🟢 −12)
Where this pull request adds them, in every column it grows
  • lib/module/NormalModuleFactory.js:1057 unknown
  • lib/module/NormalModuleFactory.js:1073 unknown
  • lib/module/NormalModuleFactory.js:1075 unknown
What the files this pull request changes still have
  • lib/javascript/JavascriptParser.js:539 object (EXPECTED_OBJECT)
  • lib/module/NormalModuleFactory.js:1058 untyped value: string | boolean | ResolveOptions | { [key: string]: any; } | { [key: string]: any; } | { loader: st…
  • lib/module/NormalModuleFactory.js:1149 untyped parserOptions: { [key: string]: any; } | undefined
  • lib/module/NormalModuleFactory.js:1151 untyped generatorOptions: { [key: string]: any; } | undefined
55 file(s) in lib/, hot/, declarations/ and bin/ still have an escape hatch, `any` or untyped identifiers
File Typed lines any unknown object Function untyped
lib/Compilation.js 99.86% 5 2 9
declarations/WebpackOptions.ts 100.00% 12 2
lib/javascript/syntax-parser.js 100.00% 11
lib/asset/AssetModulesPlugin.js 99.40% 9
lib/Compiler.js 100.00% 2 4
lib/javascript/syntax-printer.js 100.00% 1 5
lib/loaders/LoaderPlugin.js 99.42% 6
lib/module/NormalModule.js 100.00% 4 1 1
lib/module/NormalModuleFactory.js 100.00% 3 (🔴 +3) 3 (🟢 −12)
lib/dependencies/amd/AMDPlugin.js 99.35% 5
lib/util/SourceProcessor.js 100.00% 5
lib/dependencies/amd/AMDRuntimeModules.js 95.45% 4
lib/javascript/JavascriptModulesPlugin.js 100.00% 4
lib/loaders/LoaderOptionsPlugin.js 100.00% 4
lib/module/Module.js 100.00% 4
lib/resolve/getResolveOptionsKey.js 100.00% 4
lib/serialization/ObjectMiddleware.js 100.00% 4
lib/util/SortableSet.js 100.00% 4
lib/config/normalization.js 99.57% 3
lib/css/syntax-parser.js 100.00% 3
lib/hmr/HotModuleReplacement.runtime.js 100.00% 2 1
lib/html/syntax-parser.js 100.00% 3
lib/stats/StatsFactory.js 100.00% 2 1
lib/config/defineConfig.js 100.00% 2
lib/css/syntax.js 100.00% 2
lib/define/DefinePlugin.js 100.00% 1 1
lib/hmr/HotModuleReplacementPlugin.js 100.00% 1 1
lib/hmr/JavascriptHotModuleReplacement.runtime.js 100.00% 2
lib/html/syntax.js 100.00% 2
lib/index.js 100.00% 2
lib/loaders/LoaderRunner.js 100.00% 2
lib/module/Parser.js 100.00% 2
lib/node/nodeConsole.js 100.00% 2
lib/rules/BasicEffectRulePlugin.js 93.75% 2
lib/rules/UseEffectRulePlugin.js 99.06% 2
lib/util/cleverMerge.js 100.00% 2
lib/util/implicitTypeLoaderFallback.js 100.00% 2
lib/config/WebpackOptionsApply.js 100.00% 1
lib/config/cli.js 100.00% 1
lib/dependencies/import/ImportParserPlugin.js 100.00% 1
lib/dll/DllReferencePlugin.js 100.00% 1
lib/errors/JSONParseError.js 100.00% 1
lib/graph/ModuleGraph.js 100.00% 1
lib/javascript/JavascriptParser.js 100.00% 1 0 (🟢 −2)
lib/javascript/syntax.js 100.00% 1
lib/logging/Logger.js 100.00% 1
lib/logging/createConsoleLogger.js 100.00% 1
lib/output/ManifestPlugin.js 100.00% 1
lib/serialization/types.js 100.00% 1
lib/stats/DefaultStatsFactoryPlugin.js 100.00% 1
lib/stats/StatsPrinter.js 100.00% 1
lib/template/Template.js 100.00% 1
lib/util/magicComment.js 100.00% 1
lib/util/makeSerializable.js 100.00% 1
lib/util/terminalColors.js 100.00% 1

From Types Coverage run #3699.


Module loading

How many files a fresh Node.js process loads (require or import) to do each thing below; a change from the pull request's base is marked 🔴 when it grew and 🟢 when it shrank. Every file is startup time, so fewer is better. webpack files are this package's own (lib/, hot/); node_modules files are its dependencies'.

What the process does webpack files node_modules files
require("webpack") 2 0
import "webpack" 2 0
JavaScript syntax 11 0
CSS syntax 12 0
HTML syntax 11 0
XML syntax 11 0
JavaScript minifier 12 0
CSS minifier 14 0
HTML minifier 19 0
XML minifier 14 0
JSON minifier 1 0
development build 440 103
production build 447 133
watch with HMR, one rebuild 450 110

From Module loading run #943.

`preStatementByType` and `blockPreStatementByType` are keyed by a statement's
type, so they are `TypedHookMap<Record<string, StatementHook>>`, and the
interceptor that watches their keys gets a `string` rather than `any`.
@github-actions github-actions Bot added the area: types types.d.ts, JSDoc annotations, hand-maintained declarations label Nov 13, 2026
@alexander-akait alexander-akait changed the title refactor: type rule effect values as they are read, and the typeof hook's node refactor: type rule effect values, the typeof hook's node and the statement-type hook maps Nov 13, 2026
@codspeed

codspeed Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 24.97%

2 warnings, 1 note

⚠️ 117 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ 1 benchmark with a significant change ran on different hardware

Its base and head results were measured on machines with a different CPU, core count or memory, so the change might not come from the code. Run base and head on the same runner type.

Learn more about Macro Runners

See affected benchmarks

ℹ️ 41 benchmarks have no flamegraph

The flamegraph wasn't generated: most of the time was spent in runtime internals, which would leave nothing useful to analyze.

See affected benchmarks

⚡ 1 improved benchmark
✅ 350 untouched benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
⚡ Memory benchmark "no-concatenation", scenario '{"name":"mode-production","mode":"production"}' 1,033 KB 826.6 KB +24.97%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing refactor/typed-tapable-interceptors (970c9cf) with main (bf1b2dc)

Open in CodSpeed

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

Labels

area: parser JavaScript parsing and dependency analysis (lib/javascript, lib/dependencies) area: types types.d.ts, JSDoc annotations, hand-maintained declarations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant