-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support multiple HMR clients on the server #15340
Conversation
Run & review this pull request in StackBlitz Codeflow. |
The change looks good to me. I think we add
It seems that it is intended to be used to send a reply. |
72d6c4d
to
65ef618
Compare
@sapphi-red can you help me with failing tests? For some reason, I get |
The tests were failing because |
Thank you 🙏🏻 This is now ready for review and merge |
Currently to add export default {
name: 'plugin-example',
enforce: 'pre',
configureServer(server) {
server.hot.addChannel(...)
}
} I wonder if it would be a good idea to add import ssrChannel from './ssrChannel'
const server = await createServer({
server: {
hot: {
channels: [ssrChannel] // ws is always there for now
}
}
}) With this, we can also ask all channels to emit |
If there is no need to have the flexibility to add/remove channels after the server is created, then I think this is better. It would be good to think already how the API looks if ws is removed (I was thinking if this should be called |
751f009
to
3b07e33
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally reviewed and the idea sounds good to me 😄
I guess my question about channels is that, do we need to expose them for the initial implementation? I thought we wanted to add SSR HMR first class, so the concept of channels could be kept internally. And I'm not sure if it's common for someone to add another channel.
If there's a usecase I'm missing about exposing channels and we need to expose them, I'm leaning towards adding channels through server.hot.channels
instead to avoid the race condition.
We don't need to expose them, but we need to define them before the server is created or user-land If a new channel is added after that, then it's possible that a One of the possible solutions is to reapply all handlers to newly added channels, but we still risk that the connection event is not emitted yet: addChannel(channel) {
channels.push(channel)
appliedHandlers.forEach((event, listener) => channel.on(event, listener))
} const server = await createServer()
server.hot.addChannel(new ServerHMRChannel()) I do want to find a way to have it like in the example above because the alternative requires additional code to support HMR, but I don't have a good solution: const server = await createServer({
server: {
hmr: {
channels: [new ServerHMRChannel()]
}
}
// or
plugins: [
{
enforce: 'pre',
configureServer(server) {
server.hot.addChannel(new ServerHMRChannel())
}
}
]
}) Ideally, we want to have them ready by this point: vite/packages/vite/src/node/server/index.ts Line 406 in d2aa096
For built-in support, we will probably just add a channel right there, but libraries would have to use either |
Thanks 👍 Yeah I think either solutions work for me. If I have to pick one, I'd prefer the I guess I'm thinking that if we don't expose the concept of channels first (keep it internal only), we're free to choose either one you think is best and iterate from there. So that would unblock merging this. |
Another thing is that we still need to expose
// this part is done inside "createServer" function
class ServerHMRChannel {
name = 'ssr'
outerEmitter = new EventEmitter()
innerEmitter = new EventEmitter()
send(payload) {
this.outerEmitter.emit('send', payload)
}
on(event, listener) {
this.innerEmitter.on(event, listener)
}
}
// this is done right after websocket channel is created
const hmr = new ServerHMRChannel()
server.hot.addChannel(hmr)
// this part is a separate function and it requires an already created server
const hmr = server.hot.channels.find(c => c.name === 'ssr')!
const runtime = createViteRuntime({
send(payload) {
hmr.innerEmitter.emit('send', payload)
},
onUpdate() {
hmr.outerEmitter.on('send', (payload) => handleHMRUpdate(payload))
}
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's merge this PR so we let @sheremet-va keep iterating
Is there a plugin using |
@aleclarson I'm not sure if there's any. This is related to the environment API (#16358). |
Description
This PR introduces
hot
property on theserver
that allows extending HMR logic by providing your own HMR channel. This is required to support HMR on the server in the future.This is not a breaking change since it doesn't replace
ws
usage (all plugins should still work as they did before). In the future, it is recommended to usehot
property instead ofws
when supporting SSR:Type interface also requires passing down a channel to the
on
handler to send messages back (this is howws.on
already works).HMR channel is also required to send
connection
event before being able to send messages.Additional context
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).