vitest: vite-node --watch doesn't close process before restarting

Describe the bug

I created a simple Typescript Express app that I wanted to try vite-node with. It runs fine but when I edit the src code, I get this error:

[vite-node] Failed to execute file:                       
Error: listen EADDRINUSE: address already in use :::4000

Which shows that vite-node isn’t closing the previous process when in watch mode.

Reproduction

https://stackblitz.com/edit/vitest-dev-vitest-7yrhdv?file=package.json,server.ts&initialPath=__vitest__

changing what is logged out starts to accumulate multiple log entries.

System Info

n/a

Used Package Manager

npm

Validations

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 15
  • Comments: 21 (10 by maintainers)

Most upvoted comments

So, Vite considers editing the main entry here as a full reload type of HMR, so it won’t call dispose, and it calls accept too late. But I found a way to close server before ViteNode reevaluates file:

if (import.meta.hot) {
  import.meta.hot.on("vite:beforeFullReload", () => {
    server.close();
  });
}

Maybe it’s a good idea to also add dispose just in case:

import.meta.hot.dispose(() => {
  server.close()
});

It will be called only if HMR is triggered not as a full reload. According to Vite docs it’s meant to clear all side effects.

We should probably add this information to docs.

@jgoux That seems to have fixed the issue, but it seems there is another one - if you quickly double save file, I can still get port in use error which crashes server. It seems that if callback is async, vite-node doesn’t wait for it to finish 😞

Above solution doesn’t work for me, can someone let me know what I’m doing wrong?

/src/index.ts

import config from './config';
import ApiRouter from '~/api';
import app from '~/app';

const PORT = config.PORT;

export const server = app(ApiRouter);

const _server = server.listen(PORT, () => {
  console.log(`Server listening port ${PORT}`);
});

if (import.meta.hot) {
  console.log('hot reload')
  async function killServer() {
    await _server.close((err) => {
      console.log('server closed')
      process.exit(err ? 1 : 0)
    });
  }

  import.meta.hot.on("vite:beforeFullReload", () => {
    console.log("full reload")
    killServer()
  });

  import.meta.hot.dispose(() => {
    console.log('dispose')
    killServer()
  });
}

And my run command is vite-node --watch src/

output:

pn dev
> vite-node --watch src/

hot reload
Server listening port 9000
      <<<<< made some changes here
hot reload
[vite-node] Failed to execute file:
 Error: listen EADDRINUSE: address already in use :::9000

Looks like neither of the callbacks are getting hit, so the server doesn’t get killed.

EDIT: looks like run command is incorrect, changing it to this works:

vite-node --watch src/*.ts

I have another issue now, where the server reloads, but the files don’t actually get updated. Anyone hit this before?

EDIT 2: ok so some deeper files are getting updated but it takes a long time (maybe ~10s), anyone face this before?

Alright, i have to revert what i´ve said. The error still appears, i just didn´t fully tested it.

Using vite-node for running servers in development doesnt seems to work at the moment. What could eventually solve it would be an option to completely get rid of the current node process that runs the code and spawn a new process with the latest code (basically rebuild the whole app and run it like nodemon). I´ve tried disabling hmr but that just disables all reload capabilities.

I´d love to use vite for this due to the speed and plugin ecosystem but i wonder if this kind of usage is in the scope of vite-node or if we should rather use something else to run our server (something like nodemon/ts-node-dev).

Can someone tell me what I’m doing wrong here then?

const http = require('http');

const requestListener = function (req, res) {
  res.writeHead(200);
  res.end('Hello, World!');
}

const server = http.createServer(requestListener);
server.listen(8080, () => {
  console.log(`listening2`)
});

if (import.meta.hot) {
  import.meta.hot.accept(() => {
    server.close()
  })
}

I started the server with npx vite-node --watch index.ts and it outputted listening. I then changed that to output listening2 and there was the immediate “address already in use” error.

Screen Shot 2022-11-19 at 2 00 40 PM