feat: support multiple HMR clients on the server#15340
feat: support multiple HMR clients on the server#15340patak-cat merged 11 commits intovitejs:mainfrom
Conversation
|
|
|
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
bluwy
left a comment
There was a problem hiding this comment.
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))
}
}) |
patak-cat
left a comment
There was a problem hiding this comment.
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
hotproperty on theserverthat 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
wsusage (all plugins should still work as they did before). In the future, it is recommended to usehotproperty instead ofwswhen supporting SSR:configureServer(server) { - server.ws.on('connection', () => server.ws.send('custom')) + server.hot.on('connection', () => server.hot.send('custom')) }Type interface also requires passing down a channel to the
onhandler to send messages back (this is howws.onalready works).HMR channel is also required to send
connectionevent 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).