Skip to content

feat: add context option support for VirtualUrlPlugin#20449

Merged
alexander-akait merged 4 commits intomainfrom
perf/virtual
Mar 12, 2026
Merged

feat: add context option support for VirtualUrlPlugin#20449
alexander-akait merged 4 commits intomainfrom
perf/virtual

Conversation

@xiaoxiaojx
Copy link
Copy Markdown
Member

@xiaoxiaojx xiaoxiaojx commented Feb 13, 2026

Summary

Add context option support for VirtualUrlPlugin

  • The context for the virtual module. A string path. Defaults to 'auto', which will try to resolve the context from the module id.
  • Support custom context path for resolving relative imports in virtual modules
  • Add examples demonstrating context usage and filename customization

What kind of change does this PR introduce?
feat

Did you add tests for your changes?
Yes

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?
Nothing

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 13, 2026

🦋 Changeset detected

Latest commit: ad7b477

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 Feb 13, 2026

This PR is packaged and the instant preview is available (214f361).

Install it locally:

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

resourceData.context = virtualConfig.context;
} else {
resourceData.context = compiler.context;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

let's allow only string context, by default - compiler.context, otherwise virtualConfig.context

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I’m curious why this isn’t true by default 🤔, like auto, where we automatically set the context for virtual modules. Since the virtual module ID and the context value are almost the same, it feels like setting the same value twice. I’d prefer to minimize users’ exposure to the context option unless there’s a very specific use case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree, that is why I think we don't need support function and true type, only string, also I don't think we need to check on undefined we by default use compiler.context

Copy link
Copy Markdown
Member Author

@xiaoxiaojx xiaoxiaojx Feb 13, 2026

Choose a reason for hiding this comment

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

Makes sense. For now, I’ve kept only the default value "auto". This avoids requiring users to configure the context option and also prevents configs from being cluttered with repeated "auto" values, like { mod1: { context: "auto", source() {} }, mod2: { context: "auto", source() {} }, mod3: { context: "auto", source() {} } }.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am afraid that it is a breaking change, auto is the new behavior, old virtual modules can be broken, right?

Do I understand correctly this feature, if I have /path/to/src/components/button.js, context will be path/to/src/components/?

Copy link
Copy Markdown
Member Author

@xiaoxiaojx xiaoxiaojx Feb 15, 2026

Choose a reason for hiding this comment

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

This won’t affect existing virtual modules. Previously, virtual module paths like "virtual:routes" were only used as unique IDs, and their context was always fixed to compiler.context. With context: "auto", for "virtual:routes" the computed context is still compiler.context, so nothing changes.

Now we’d like to extend virtual module paths so they’re not just IDs, but virtual paths with context information — for example, virtual:src/components/button.js. In that case, the context would be resolved to something like /path/to/src/components/button.js.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@xiaoxiaojx Got it, I see the default value should be auto, but I don't see this in our code

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@xiaoxiaojx If I understand correctly, I agree with @alexander-akait that this is a breaking change, because if anyone was already using slashes in their virtual module ID, the context will now be different.

I think that's a reasonable breaking change, though :)

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Feb 13, 2026

Merging this PR will degrade performance by 22.78%

❌ 2 regressed benchmarks
✅ 142 untouched benchmarks

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

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "lodash", scenario '{"name":"mode-development","mode":"development"}' 4.1 MB 5.3 MB -22.78%
Memory benchmark "many-chunks-esm", scenario '{"name":"mode-production","mode":"production"}' 7.1 MB 9 MB -20.99%

Comparing perf/virtual (ad7b477) with main (d5fc9c6)

Open in CodSpeed

Comment thread lib/schemes/VirtualUrlPlugin.js Outdated
modules,
...(scheme !== undefined && { scheme })
};
validate(options);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's move validate under the new hook for such things

Comment thread lib/schemes/VirtualUrlPlugin.js Outdated
const context =
typeof virtualConfig.context === "string"
? virtualConfig.context
: "auto";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There is an interesting question, change this potentially can break existing code for developers which already use it (right?). So we should decide it is a fix (and our current behavior is wrong and it should be auto by default) or do not change it by default on auto, what do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, the default "auto" might not be the best choice. It was originally set to avoid having to configure the context option for every module. I recently did a small refactor here and added a fallback options to provide default settings.

@github-actions
Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging perf/virtual into main will be
98.94%
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%
lib
   APIPlugin.js100%100%100%100%
   AbstractMethodError.js100%100%100%100%
   AsyncDependenciesBlock.js100%100%100%100%
   AsyncDependencyToInitialChunkError.js100%100%100%100%
   AutomaticPrefetchPlugin.js100%100%100%100%
   BannerPlugin.js100%100%100%100%
   Cache.js98.21%100%100%98.21%87
   CacheFacade.js100%100%100%100%
   CaseSensitiveModulesWarning.js100%100%100%100%
   Chunk.js99.72%100%100%99.72%37
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkRenderError.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CleanPlugin.js98.72%100%100%98.72%196, 216, 371
   CodeGenerationError.js100%100%100%100%
   CodeGenerationResults.js100%100%100%100%
   CommentCompilationWarning.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.54%100%100%98.54%1510, 1799, 1806, 1814, 1836, 2712, 3130, 3778, 3807, 3859–3860, 3864, 3869, 3885–3886, 3900–3901, 3906–3907, 4369, 4395, 469, 474, 5078, 5158, 5173, 5198–5199, 5201, 5517, 5522, 5528, 5531, 5543, 5545, 5549, 5565, 5580, 5611, 5665, 5689, 5799, 680–681
   Compiler.js99.56%100%100%99.56%1091–1092, 1100
   ConcatenationScope.js98.59%100%100%98.59%166
   ConcurrentCompilationError.js100%100%100%100%
   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%253, 385, 410, 435, 439, 450
   ContextReplacementPlugin.js100%100%100%100%
   CssModule.js81.32%100%100%81.32%152, 157–172
   DefinePlugin.js98.92%100%100%98.92%153–154, 170, 189, 263
   DelegatedModule.js95.24%100%100%95.24%240–244
   DelegatedModuleFactoryPlugin.js98.15%100%100%98.15%103
   DelegatedPlugin.js100%100%100%100%
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.13%100%100%98.13%351, 381
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DllEntryPlugin.js100%100%100%100%
   DllModule.js100%100%100%100%
   DllModuleFactory.js100%100%100%100%
   DllPlugin.js100%100%100%100%
   DllReferencePlugin.js100%100%100%100%
   DotenvPlugin.js97.88%100%100%97.88%235, 375, 388–389
   DynamicEntryPlugin.js100%100%100%100%
   EntryOptionPlugin.js100%100%100%100%
   EntryPlugin.js100%100%100%100%
   Entrypoint.js100%100%100%100%
   EnvironmentNotSupportAsyncWarning.js100%100%100%100%
   EnvironmentPlugin.js97.14%100%100%97.14%48
   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.86%100%100%98.86%384–388, 525
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FalseIIFEUmdWarning.js100%100%100%100%
   FileSystemInfo.js99.49%100%100%99.49%168, 2140–2141, 2144, 2155, 2166, 2177, 261, 3495, 3510, 3534
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.74%100%100%98.74%396, 398, 402
   FlagDependencyUsagePlugin.js100%100%100%100%
   FlagEntryExportAsUsedPlugin.js100%100%100%100%
   Generator.js100%100%100%100%
   GraphHelpers.js100%100%100%100%
   HarmonyLinkingError.js100%100%100%100%
   HookWebpackError.js100%100%100%100%
   HotModuleReplacementPlugin.js100%100%100%100%
   HotUpdateChunk.js100%100%100%100%
   IgnoreErrorModuleFactory.js100%100%100%100%
   IgnorePlugin.js100%100%100%100%
   IgnoreWarningsPlugin.js100%100%100%100%
   InitFragment.js100%100%100%100%
   InvalidDependenciesModuleWarning.js100%100%100%100%
   JavascriptMetaInfoPlugin.js100%100%100%100%
   LibManifestPlugin.js97.14%100%100%97.14%114, 117
   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%1186, 1191, 1249, 1262, 1319, 1327
   ModuleBuildError.js100%100%100%100%
   ModuleDependencyError.js100%100%100%100%
   ModuleDependencyWarning.js100%100%100%100%
   ModuleError.js100%100%100%100%
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%105, 107
   ModuleGraph.js99.73%100%100%99.73%942
   ModuleGraphConnection.js100%100%100%100%
   ModuleHashingError.js100%100%100%100%
   ModuleInfoHeaderPlugin.js100%100%100%100%
   ModuleNotFoundError.js100%100%100%100%
   ModuleParseError.js100%100%100%100%
   ModuleProfile.js100%100%100%100%
   ModuleRestoreError.js100%100%100%100%
   ModuleSourceTypeConstants.js100%100%100%100%
   ModuleStoreError.js100%100%100%100%
   ModuleTemplate.js100%100%100%100%
   ModuleTypeConstants.js100%100%100%100%
   ModuleWarning.js100%100%100%100%
   MultiCompiler.js99.69%100%100%99.69%619
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NoModeWarning.js100%100%100%100%
   NodeStuffInWebError.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.78%100%100%97.78%1032, 1048, 1135, 1765, 1770–1780, 214, 722, 740, 757, 998
   NormalModuleFactory.js99.46%100%100%99.46%1032, 1337, 447, 459
   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.75%100%100%98.75%431–432, 437, 439, 503
   ProvidePlugin.js100%100%100%100%
   RawModule.js100%100%100%100%
   RecordIdsPlugin.js100%100%100%100%
   RequestShortener.js100%100%100%100%
   RequireJsStuffPlugin.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%
   SizeFormatHelpers.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js99.16%100%100%99.16%265–266, 608
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js98.84%100%100%98.84%128–129
   UnhandledSchemeError.js100%100%100%100%
   UnsupportedFeatureWarning.js100%100%100%100%
   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.js96.97%100%100%96.97%43
   WebpackIsIncludedPlugin.js100%100%100%100%
   WebpackOptionsApply.js100%100%100%100%
   WebpackOptionsDefaulter.js100%100%100%100%
   buildChunkGraph.js99.87%100%100%99.87%317
   cli.js98.71%100%100%98.71%109, 453, 485, 527, 785
   formatLocation.js100%100%100%100%
   index.js100%100%100%100%
   validateSchema.js94.67%100%100%94.67%86, 88, 97, 99
   webpack.js97.22%100%100%97.22%186, 208, 210
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModulesPlugin.js97.77%100%100%97.77%282, 306, 309, 361, 39
   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%70, 82, 90
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%104, 111–112, 120, 87
   PackFileCacheStrategy.js96.40%100%100%96.40%1225,

@alexander-akait alexander-akait merged commit 214f361 into main Mar 12, 2026
54 of 55 checks passed
@alexander-akait alexander-akait deleted the perf/virtual branch March 12, 2026 18:47
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.

3 participants