Skip to content

refactor: remove HmrUrl concept - #23172

Merged
sapphi-red merged 3 commits into
mainfrom
refactor/remove-hmr-url-concept
Aug 21, 2026
Merged

refactor: remove HmrUrl concept#23172
sapphi-red merged 3 commits into
mainfrom
refactor/remove-hmr-url-concept

Conversation

@sapphi-red

@sapphi-red sapphi-red commented Aug 5, 2026

Copy link
Copy Markdown
Member

This PR removes the "HmrUrl" concept.

There were three "Url" concept in Vite:

  • "ModuleUrl"
    • the URL used in the module graph.
    • does not replace \0
    • does not have base
    • Example: \0virtual:foo
  • "BrowserUrl"
    • the URL used by the browser
    • replaces \0
    • has base
    • Example: /base/@id/__x00__virtual:foo?t=123
  • "HmrUrl"
    • the URL used in the HMR client.
    • replaces \0
    • does not have base
    • Example: /@id/__x00__virtual:foo

It is confusing to have three kinds of representation. I think the HmrUrl is not needed as well.


Stack created with GitHub Stacks CLIGive Feedback 💬

Co-authored-by: c4605 <bolasblack@gmail.com>
@sapphi-red
sapphi-red force-pushed the fix/hmr-invalidate-in-virtual-module branch from 651aef4 to 4aeb91e Compare August 5, 2026 09:25
@sapphi-red
sapphi-red force-pushed the refactor/remove-hmr-url-concept branch from 3d6e4b0 to 5399f52 Compare August 5, 2026 09:32
isWithinCircularImport,
}) {
const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`)
const browserPath = wrapIdIfNeeded(acceptedPathWithoutQuery)

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.

acceptedPath is now "moduleUrl" so wrapId is needed to convert to "browserUrl".

Comment on lines -22 to -24
// runner always caches modules by their full path without /@id/ prefix
update.acceptedPath = unwrapId(update.acceptedPath)
update.path = unwrapId(update.path)

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.

HmrClient received "moduleUrl" (Url without /@id/). acceptedPath and path is now "moduleUrl" (was "HmrUrl") so unwrapId is no longer needed.

// make sure to unwrap and normalize away base
const hmrUrl = unwrapId(stripBase(url, base))
const isLocalImport = !isExternalUrl(hmrUrl) && !isDataUrl(hmrUrl)
const moduleUrl = unwrapId(stripBase(url, base))

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.

A simple rename here. This is/was a "moduleUrl" because unwrapId is called.

`import { createHotContext as __vite__createHotContext } from "${clientPublicPath}";` +
`import.meta.hot = __vite__createHotContext(${JSON.stringify(
normalizeHmrUrl(importerModule.url),
importerModule.url,

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.

HotContext now receives "moduleUrl" similarly to acceptedPath and path.

Comment on lines -830 to -831
const hmrAccept = normalizeHmrUrl(normalized)
str().overwrite(start, end, JSON.stringify(hmrAccept), {

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.

The acceptPath of import.meta.hot.accept(acceptPath) now receives "moduleUrl", similarly to path and acceptPath. This is not a breaking change because this acceptPath argument is always rewritten by this import-analysis plugin.

Comment on lines -722 to -723
({ acceptedVia }) =>
normalizeHmrUrl(acceptedVia.url) === firstInvalidatedBy,

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.

firstInvalidatedBy was "HmrUrl" (as it was sent from the client), but now it's "ModuleUrl".

path: normalizeHmrUrl(boundary.url),
acceptedPath: normalizeHmrUrl(acceptedVia.url),
path: boundary.url,
acceptedPath: acceptedVia.url,

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.

path and acceptedPath is now "moduleUrl" instead of "HmrUrl".

return urls.size > 0
}

export function normalizeHmrUrl(url: string): string {

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.

This conversion happens on the client side when importing a module (i.e. when "moduleUrl" is converted to "browserUrl").

Comment on lines +494 to +495
// moduleUrl must be derived the same way as importAnalysis
const moduleUrl = unwrapId(

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.

This is a simple rename. This is/was a "moduleUrl" because unwrapId is called.

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.

As noted in this file and hmrPayload.d.ts, technically these fields will have slightly different behavior. But I assume this won't be a huge problem.

@sapphi-red sapphi-red added the p1-chore Doesn't change code behavior (priority) label Aug 5, 2026
@sapphi-red

Copy link
Copy Markdown
Member Author

/ecosystem-ci run

@sapphi-red sapphi-red added this to the 8.3 milestone Aug 5, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vitejs/plugin-legacy

pnpm add https://pkg.pr.new/@vitejs/plugin-legacy@23172 -D
npm i https://pkg.pr.new/@vitejs/plugin-legacy@23172 -D
yarn add https://pkg.pr.new/@vitejs/plugin-legacy@23172.tgz -D

vite

pnpm add https://pkg.pr.new/vite@23172 -D
npm i https://pkg.pr.new/vite@23172 -D
yarn add https://pkg.pr.new/vite@23172.tgz -D

commit: 5399f52

@vite-ecosystem-ci

Copy link
Copy Markdown

📝 Ran ecosystem CI on 94cf41f: Open

suite result latest scheduled
react-router failure failure
sveltekit failure failure
qwik ⏹️ cancelled ⏹️ cancelled
storybook failure success
tanstack-start ⏹️ cancelled ⏹️ cancelled
vitest failure failure
astro failure failure

vite-plugin-react, module-federation, marko, analogjs, vite-plugin-pwa, vite-plugin-svelte, unocss, laravel, vite-setup-catalogue, vite-plugin-cloudflare, vitepress, vite-plugin-vue, vuepress, vite-plugin-rsc, vite-environment-examples, quasar, waku, vike, nuxt

@sapphi-red
sapphi-red marked this pull request as ready for review August 5, 2026 11:08
@sapphi-red

Copy link
Copy Markdown
Member Author

^ storybook failure seems unrelated

bluwy
bluwy previously approved these changes Aug 7, 2026

@bluwy bluwy left a comment

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 like this refactor. Let's try this in the next minor and hope it doesn't break many people.

Base automatically changed from fix/hmr-invalidate-in-virtual-module to main August 21, 2026 09:06
@sapphi-red
sapphi-red dismissed bluwy’s stale review August 21, 2026 09:06

The base branch was changed.

@sapphi-red
sapphi-red merged commit 67a6807 into main Aug 21, 2026
11 checks passed
@sapphi-red
sapphi-red deleted the refactor/remove-hmr-url-concept branch August 21, 2026 09:07
This was referenced Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p1-chore Doesn't change code behavior (priority) trigger: preview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants