Skip to content

Conversation

@sapphi-red
Copy link
Member

Description

Added vite:client-connect / vite:client-disconnect events so that it's possible to track the client life-cycle. This is needed for full bundle mode to properly trigger HMR (we have the same issue in unbundled mode as well, but it wasn't a big problem). This is probably needed to solve #19710 as well.
This API is also useful for vite-dev-rpc to migrate to environment.hot.

@sapphi-red sapphi-red added the p3-significant High priority enhancement (priority) label Oct 22, 2025
@sapphi-red sapphi-red added this to the 7.2 milestone Oct 22, 2025
@sapphi-red sapphi-red requested a review from hi-ogawa October 23, 2025 02:58
Copy link
Contributor

@hi-ogawa hi-ogawa left a comment

Choose a reason for hiding this comment

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

Looks good to me. Should we update docs to mention this? I see currently there is an example like server.on('connection', ...). The difference from current hot.on("connection" ...) should be probably mentioned too or how it can be entirely replaced? (and that's something I'm actually confused about)

Comment on lines 19 to 21
// server events
'vite:client-connect': undefined
'vite:client-disconnect': undefined
Copy link
Member

@bluwy bluwy Oct 23, 2025

Choose a reason for hiding this comment

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

Should we rename it as vite:client:connect so it's similar to vite:ws:connect?


Btw I found it initially weird that we're mixing client/server events here, but it's not too terrible at the end if it makes configuring events easier. It's unlikely that client <-> server with the same event name will need different payload shape.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 23, 2025

Open in StackBlitz

npm i https://pkg.pr.new/vite@20978

commit: 541e945

@sapphi-red
Copy link
Member Author

Should we update docs to mention this? I see currently there is an example like server.on('connection', ...). The difference from current hot.on("connection" ...) should be probably mentioned too or how it can be entirely replaced? (and that's something I'm actually confused about)

The difference is only the callback parameter. connection event has (this: Server<T>, socket: T, request: IncomingMessage) => void as a callback for env that uses the builtin WebSocket channel, and () => void for other channels.
I'll update the docs.

@hi-ogawa
Copy link
Contributor

For custom hmr channel/transport, I think the mechanism of emitting vite:... needs to be explicitly implemented on their side.

@sapphi-red
Copy link
Member Author

Added the docs and renamed the event 👍

hi-ogawa
hi-ogawa previously approved these changes Oct 23, 2025
Copy link
Contributor

@hi-ogawa hi-ogawa left a comment

Choose a reason for hiding this comment

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

I don't think this is an issue but let me note one observation.

Currently, the default ssr environment server.environments.ssr.hot.on("vite:client:connect", ...) is triggered when server.environment.ssr.runner getter is accessed (or manually createServerModuleRunner is called) and the timing might look arbitrary. For example, one plugin can do ....ssr.hot.on("vite:client:connect" ...) during configureServer (like in the test) and other plugins might also grab ....ssr.runner during its configureServer.

For node worker example (which currently ignores vite:client:connect), I think the same can be said and the implementation might be able to allow somehow lazily spawning workers and also allow multiple workers with different entrypoints, If such implementation is possible, it probably makes sense to emit vite:client:connect for each module runner instantiation or entrypoint import on worker side. (I'm vaguely imagining how ?nodeWorker could be implemented #3932).

Comment on lines 328 to 329
// client is already connected
if (event === 'connection' || event === 'vite:client:connect') return
Copy link
Contributor

Choose a reason for hiding this comment

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

Related to the last comment, I'm wondering if it's more natural to emit vite:client:connect from "client" side. For example, what does "server" side see if we add a following in worker.js above?

runner.hmrClient.send({ type: "custom", event: "vite:client:connected" })

Also what if we do the same in hmrClient.send(...) in @vite/client?

Copy link
Member Author

Choose a reason for hiding this comment

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

It would work for vite:client:connect but it won't work for vite:client:disconnect. I think it's better to handle them on the same side.

@sapphi-red
Copy link
Member Author

the timing might look arbitrary

Indeed. I think I need to figure out when it should be emitted and probably how to handle the missed events (the clients connected before listening to the event). Solving #19710 should give me a better idea of how to handle it.

@hi-ogawa
Copy link
Contributor

Somewhat related to missed events, would it make sense (or is it possible) to expose currently connected clients environment.hot.clients like current server.ws.clients?

@sapphi-red
Copy link
Member Author

Yeah, I thought about that. I think it makes sense, but I'd like to leave that for now. (if you have a concrete usecase, we can add it now)

@sapphi-red sapphi-red requested a review from bluwy October 24, 2025 03:00
@hi-ogawa
Copy link
Contributor

Leaving it out sounds good to me 👍 No use case in mind but was curious.

@bluwy
Copy link
Member

bluwy commented Oct 24, 2025

The difference is only the callback parameter. connection event has (this: Server<T>, socket: T, request: IncomingMessage) => void as a callback for env that uses the builtin WebSocket channel, and () => void for other channels.

I'm reading the new docs and I'm still a bit confused between connection and vite:client:connect. It's not clear when connection gets emitted. So are they the same and should trigger at the same time? (with the different params) Though I'm also seeing that we're restricting the params via types:

on(event: 'connection', listener: () => void): void

If they are the same, should we deprecate the connection event? Or at least hide its exposure to the devs and prefer vite:client:connect instead? For APIs interacting with server.hot, this.environment.hot, etc I mean. For server.ws, I guess we have to keep it.

@sapphi-red
Copy link
Member Author

I'm reading the new docs and I'm still a bit confused between connection and vite:client:connect. It's not clear when connection gets emitted. So are they the same and should trigger at the same time? (with the different params)

The connection event was vague about when it is emitted. Assuming that it follows the behavior of the connection event in server.ws, it should be emitted when the connection is established. The new vite:client:connect event has the same behavior (additionally this PR also clarified when it should be emitted), but with a different params. It's a new event because we cannot change the params of the connection event for server.ws. So, to sum up, connection event should be deprecated (updated).

Copy link
Member

@bluwy bluwy left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for updating!

@sapphi-red sapphi-red merged commit 543d87c into vitejs:main Oct 27, 2025
17 checks passed
@sapphi-red sapphi-red deleted the feat/add-vite-client-connect-events branch October 28, 2025 02:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p3-significant High priority enhancement (priority) trigger: preview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants