refactor: decompose CSS-Modules parser and move escape handling into the tokenizer#21196
Conversation
🦋 Changeset detectedLatest commit: 7e4f606 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 |
…the tokenizer Decompose the CSS-Modules logic in lib/css/CssParser.js into focused helpers and a node-type switch (walkSelectorList, emitComposesWithAnchor, the Declaration visitor), and move CSS escape resolution into the tokenizer as lazy accessors (Token#unescaped, Node#unescapedName) — superseding backslash-only stripping with full unescaping, which also fixes hex escapes (e.g. \75 rl -> url). Add CssIcssExportDependency codegen perf (resolveReferences context-hoist and a transient composes-name index) plus assorted parser allocation cuts. Rebased on #21181: keeps its orthogonal optimizations and supersedes its stripBackslashes / property-name caching with the equivalents here.
a0055a0 to
7e4f606
Compare
|
This PR is packaged and the instant preview is available (7552543). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@7552543
yarn add -D webpack@https://pkg.pr.new/webpack@7552543
pnpm add -D webpack@https://pkg.pr.new/webpack@7552543 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #21196 +/- ##
==========================================
+ Coverage 92.69% 92.71% +0.02%
==========================================
Files 588 588
Lines 64122 64146 +24
Branches 17799 17793 -6
==========================================
+ Hits 59435 59473 +38
+ Misses 4687 4673 -14
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:
|
Merging this PR will not alter performance
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | benchmark "asset-modules-bytes", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
321.2 KB | 859.3 KB | -62.63% |
| ❌ | Memory | benchmark "many-modules-esm", scenario '{"name":"mode-production","mode":"production"}' |
7.8 MB | 10.1 MB | -23.07% |
| ❌ | Memory | benchmark "many-chunks-esm", scenario '{"name":"mode-production","mode":"production"}' |
7.7 MB | 9.9 MB | -22.48% |
| ⚡ | Memory | benchmark "asset-modules-inline", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
1,293.8 KB | 324.4 KB | ×4 |
| ⚡ | Memory | benchmark "css-modules", scenario '{"name":"mode-production","mode":"production"}' |
10 MB | 7.2 MB | +38.54% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing refactor/css-parser-decomposition (7e4f606) with main (0db8bbb)
Summary
Decomposes the CSS-Modules logic in
lib/css/CssParser.jsinto focused helpers and a node-typeswitch(walkSelectorList,emitComposesWithAnchor, theDeclarationvisitor), and moves CSS escape resolution into the tokenizer as lazy accessors (Token#unescaped,Node#unescapedName) so the parser reads clean names instead of scattered escape-strip /unescapeIdentifier(source.slice(...))calls.Also adds profiling-guided perf that does not overlap #21181: in
CssIcssExportDependencycodegen, a context-allocation hoist inresolveReferencesplus an O(entries²)→O(entries) composes-resolution index (transient, so retained heap is unchanged); and assorted parser allocation cuts (dashed-ident scope stack, known-property export tuples, lazy per-rulecomposesFiles).Behavior is unchanged (the CSS suites are byte-identical to baseline) except one correctness fix: function/pseudo names are now fully unescaped, so e.g.
\75 rl()resolves tourl()(previously only backslashes were stripped).Rebased onto
mainafter #21181 merged. #21181 independently added some of the same micro-optimizations (astripBackslasheshelper, property-name caching); this PR keeps #21181's orthogonal wins (normalizeUrl/getComments/getReexport fast-paths, lazywebpackIgnoredloc, non-moduleslocalIdentifierssharing) and supersedes its backslash-only stripping with the fuller tokenizer-level unescaping here. Note: the local benchmark (~5–6% on a CSS-Modules fixture) was measured against the pre-#21181 base, so part of that gap is now already upstream — the decomposition, theCssIcsscodegen perf, and the correctness fix are the parts unique to this PR.What kind of change does this PR introduce?
refactor (with
perfwork inCssIcssExportDependencyand a smallfixfor hex-escaped names).Did you add tests for your changes?
No new test cases — behavior is covered by the existing CSS suites (
ConfigTestCases/ConfigCacheTestCasescss,cssParsing-webpack.spectest,walkCssTokens*/cssIdentifierunit tests,cases/css,configCases/css/postcss-modules-plugins), which stay green and byte-identical to baseline. Happy to add a focused case for hex-escaped function/selector names to lock in the correctness fix if preferred.Does this PR introduce a breaking change?
No.
If relevant, what needs to be documented once your changes are merged or what have you already documented?
n/a — internal refactor of CSS-Modules parsing/codegen; no public API or configuration change. A changeset (
patch) is included for the escape-resolution fix.Use of AI
Yes. Developed with AI assistance (Claude Code): used to profile hot paths, propose and apply the decomposition and performance changes, reconcile the rebase onto #21181, and validate each step against the existing CSS test suites and a compile-time/heap benchmark. Every change was reviewed and verified before pushing.