Skip to content

support CSS Modules @value identifiers as @import URLs and inside url()#20925

Merged
alexander-akait merged 3 commits into
mainfrom
claude/add-value-support-url-import-FR6AV
May 7, 2026
Merged

support CSS Modules @value identifiers as @import URLs and inside url()#20925
alexander-akait merged 3 commits into
mainfrom
claude/add-value-support-url-import-FR6AV

Conversation

@alexander-akait
Copy link
Copy Markdown
Member

Allow CSS Modules @value definitions to be used as the path argument
to @import and inside url() references. For example:

@value path: "./other.module.css";
@import path;

@value bg: "./image.png";
.a { background: url(bg); }

Both the quoted ("./x", './x') and bare (./x) forms of the value
are accepted; whichever the user wrote is unwrapped and resolved as a
module request, so the asset goes through the normal webpack resolver
and asset pipeline instead of being left as a literal bg/path
identifier.

… `url()`

Allow CSS Modules `@value` definitions to be used as the path argument
to `@import` and inside `url()` references. For example:

    @value path: "./other.module.css";
    @import path;

    @value bg: "./image.png";
    .a { background: url(bg); }

Both the quoted (`"./x"`, `'./x'`) and bare (`./x`) forms of the value
are accepted; whichever the user wrote is unwrapped and resolved as a
module request, so the asset goes through the normal webpack resolver
and asset pipeline instead of being left as a literal `bg`/`path`
identifier.
Add a `values.module.css` that defines values consumed via `@value … from`
in `style.module.css` and `imported-via-value.module.css`, exercising
three scenarios:

1. Imported `@value` used as a regular declaration value
   (`color: externalColor;`) — already worked, now asserted against
   the bundle output.
2. Imported `@value` used as the URL of `@import` — emits a clear
   warning explaining that only locally defined values are supported
   in this position. The whole at-rule is dropped from the output so
   the unresolved identifier doesn't leak into a malformed CSS
   `@import`.
3. Imported `@value` used inside `url()` — emits an analogous warning
   and leaves the original `url(identifier)` text in place.

Update the warning messages in `CssParser` to distinguish "unknown
identifier" from "imported from another module — not supported here",
and add a `warnings.js` listing the two expected warnings for the
imported-value scenarios.
Copilot AI review requested due to automatic review settings May 7, 2026 17:45
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

🦋 Changeset detected

Latest commit: 88fe1c9

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
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

This PR is packaged and the instant preview is available (10c4fcf).

Install it locally:

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

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 85.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.35%. Comparing base (84d7d62) to head (88fe1c9).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lib/css/CssParser.js 83.78% 6 Missing ⚠️

❌ Your changes status has failed because you have indirect coverage changes. Learn more about Unexpected Coverage Changes and reasons for indirect coverage changes.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #20925      +/-   ##
==========================================
- Coverage   91.37%   91.35%   -0.03%     
==========================================
  Files         566      566              
  Lines       56093    56130      +37     
  Branches    14886    14904      +18     
==========================================
+ Hits        51254    51276      +22     
- Misses       4839     4854      +15     
Flag Coverage Δ
integration 90.28% <85.00%> (-0.03%) ⬇️
test262 45.61% <0.00%> (-0.04%) ⬇️
unit 36.04% <0.00%> (-0.03%) ⬇️

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

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 extends webpack’s CSS Modules parsing so locally-defined @value identifiers can be used as module requests in @import and inside url(), allowing those references to be resolved via webpack’s normal resolver/asset pipeline.

Changes:

  • Teach the CSS tokenizer/parser to recognize @import <identifier>; and resolve it via CSS Modules @value (local-only), emitting warnings for unsupported imported @values.
  • Add CSS Modules url() handling to resolve url(<identifier>) via local @value definitions (with warnings for imported @values).
  • Add a new configCases test suite (including a fixture image) plus a changeset documenting the new feature.

Reviewed changes

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

Show a summary per file
File Description
lib/css/walkCssTokens.js Extends @import tokenization to record when the URL was provided as an identifier.
lib/css/CssParser.js Resolves @import and url() identifier forms via local CSS Modules @value definitions and emits warnings for unsupported cases.
test/configCases/css/css-modules-value-url-import/webpack.config.js Adds a config case enabling CSS experiments and CSS Modules for the new scenario.
test/configCases/css/css-modules-value-url-import/style.module.css Exercises local @value usage in @import and url() (quoted and bare forms).
test/configCases/css/css-modules-value-url-import/values.module.css Provides exported @value definitions used by the warning scenario.
test/configCases/css/css-modules-value-url-import/imported.module.css Simple imported stylesheet content used to assert @import worked.
test/configCases/css/css-modules-value-url-import/imported-via-value.module.css Exercises warning paths for imported @value used in @import and url().
test/configCases/css/css-modules-value-url-import/index.js Asserts behavior for resolved @import/url() and warning-only cases.
test/configCases/css/css-modules-value-url-import/warnings.js Asserts the expected warnings are emitted for unsupported imported @value positions.
test/configCases/css/css-modules-value-url-import/img/shape.png Fixture asset referenced by @value-backed url() cases.
.changeset/css-modules-value-url-import.md Declares a minor release note for the new CSS Modules capability.

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

Comment thread lib/css/CssParser.js Outdated
Comment on lines +826 to +837
if (!def.value) {
this._emitWarning(
state,
`'@value' identifier '${name}' was imported from another module and cannot be used as the URL of '@import' — only locally defined values are supported here`,
locConverter,
start,
semi
);
const dep = new ConstDependency("", [start, semi]);
module.addPresentationalDependency(dep);
return semi;
}
Comment on lines +31 to +33
const matches = [
...cssContent.matchAll(/background-image:\s*url\(([^)]+)\)/g)
];
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 7, 2026

Merging this PR will degrade performance by 66.92%

⚠️ 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
❌ 9 regressed benchmarks
✅ 132 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "future-defaults", scenario '{"name":"mode-development","mode":"development"}' 1.4 MB 1.8 MB -26.25%
Memory benchmark "asset-modules-source", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 161.2 KB 367.7 KB -56.17%
Memory benchmark "react", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 170.7 KB 237.4 KB -28.1%
Memory benchmark "concatenate-modules", scenario '{"name":"mode-development","mode":"development"}' 552.6 KB 756 KB -26.9%
Memory benchmark "json-modules", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 291.2 KB 218.1 KB +33.54%
Memory benchmark "asset-modules-inline", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 225.1 KB 680.3 KB -66.92%
Memory benchmark "many-chunks-commonjs", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 468.4 KB 977.7 KB -52.09%
Memory benchmark "many-modules-esm", scenario '{"name":"mode-production","mode":"production"}' 9 MB 7.3 MB +22.78%
Memory benchmark "devtool-eval", scenario '{"name":"mode-development","mode":"development"}' 1,272 KB 843.5 KB +50.81%
Memory benchmark "many-modules-commonjs", scenario '{"name":"mode-production","mode":"production"}' 7.3 MB 10 MB -27%
Memory benchmark "devtool-eval-source-map", scenario '{"name":"mode-development","mode":"development"}' 884.9 KB 1,398 KB -36.7%
Memory benchmark "devtool-source-map", scenario '{"name":"mode-production","mode":"production"}' 5.5 MB 7.2 MB -24.35%

Comparing claude/add-value-support-url-import-FR6AV (88fe1c9) with main (0d7468e)

Open in CodSpeed

- `processAtImport`: distinguish "imported @value" from "locally defined
  with empty string value" by checking `def.value === undefined`
  explicitly instead of `!def.value`. A local `@value p: ;` now falls
  through to the empty-URL handling (which drops the at-rule) instead
  of being misreported as an imported value.
- Replace `String.prototype.matchAll` in the new test with a
  `RegExp#exec` loop — webpack still supports Node 10.13+ and `matchAll`
  was added in Node 12.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Types Coverage

Coverage after merging claude/add-value-support-url-import-FR6AV into main will be
98.92%
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.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%37
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CleanPlugin.js98.72%100%100%98.72%206, 226, 382
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.55%100%100%98.55%1554, 1850, 1857, 1865, 1887, 2783, 3208, 3870, 3899, 3952–3953, 3957, 3962, 3978–3979, 3993–3994, 3999–4000, 4477, 4503, 493, 498, 5211, 5292, 5307, 5332–5333, 5335, 5659, 5664, 5670, 5673, 5685, 5687, 5691, 5707, 5722, 5754, 5808, 5832, 5946, 712–713
   Compiler.js99.55%100%100%99.55%1116–1117, 1125
   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.75%100%100%97.75%258, 393, 418, 443, 447, 458
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js98.92%100%100%98.92%158–159, 175, 194, 268
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.20%100%100%98.20%379, 425
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DotenvPlugin.js97.88%100%100%97.88%237, 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.89%100%100%98.89%399–403, 542
   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, 3694, 3709, 3733
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.74%100%100%98.74%399, 401, 405
   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%
   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%1304, 1309, 1370, 1384, 1446, 1455
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1004
   ModuleGraphConnection.js100%100%100%100%
   ModuleInfoHeaderPlugin.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%645
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.78%100%100%97.78%1020, 1036, 1123, 1774, 1779–1789, 708, 711, 728, 745, 986
   NormalModuleFactory.js99.47%100%100%99.47%1067, 1376, 466, 478
   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.js99.16%100%100%99.16%267–268, 610
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js98.86%100%100%98.86%134–135
   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%325
   cli.js98.71%100%100%98.71%117, 469, 501, 543, 813
   index.js100%100%100%100%
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js97.22%100%100%97.22%196, 218, 220
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModulesPlugin.js97.77%100%100%97.77%285, 309, 312, 364, 40
   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.14%100%100%99.14%1314–1316, 1324, 271, 274, 279, 283, 472
   normalization.js99%100%100%99%191–192, 258, 273
   target.js100%100%100%100%
lib/container
   ContainerEntryDependency.js100%100%100%100%
   ContainerEntryModule.js100%100%100%100%
   ContainerEntryModuleFactory.js100%100%100%100%
   ContainerExposedDependency.js100%100%100%100%
   ContainerPlugin.js100%100%100%100%
   ContainerReferencePlugin.js100%100%100%100%
   FallbackDependency.js100%100%100%100%
   FallbackItemDependency.js100%100%100%100%
   FallbackModule.js100%100%100%100%
   FallbackModuleFactory.js100%100%100%100%
   HoistContainerReferencesPlugin.js100%100%100%100%
   ModuleFederationPlugin.js100%100%100%100%
   RemoteModule.js100%100%100%100%
   RemoteRuntimeModule.js100%100%100%100%
   

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 10 out of 11 changed files in this pull request and generated no new comments.

@alexander-akait alexander-akait merged commit 10c4fcf into main May 7, 2026
61 of 64 checks passed
@alexander-akait alexander-akait deleted the claude/add-value-support-url-import-FR6AV branch May 7, 2026 19:25
aryanraj45 pushed a commit to aryanraj45/webpack that referenced this pull request May 8, 2026
alexander-akait pushed a commit to webpack/webpack.js.org that referenced this pull request May 18, 2026
…107) (#8244)

Webpack 5.107 brings two additions to the experiments.css feature set:

- Scope hoisting (module concatenation) now applies to CSS Modules with
  exportType "text", "css-style-sheet", "style", or "link", reducing
  runtime overhead in CSS-heavy bundles when
  optimization.concatenateModules is enabled.
- @value identifiers can be used as the path argument to @import and
  inside url() references, so shared paths/assets are defined once and
  reused. Both quoted and bare forms are accepted and resolved through
  webpack's normal asset pipeline.

Adds two bullets to the existing experimental features list in
experiments.css.

Refs:
- webpack/webpack#20851 (scope hoisting)
- webpack/webpack#20925 (@value in URLs)
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.

2 participants