1

Is it possible to create a Vite SSR app with Node.js native HTTP server? The guide from Vite documentation is using Express. But I prefer using Node.js native HTTP server as follows:

const port = 3000
const host = 'localhost'
const requestListener = async function (req, res) {

  const vite = await createViteServer({
    server: { middlewareMode: true },
    appType: 'custom'
  })

  // Use vite's connect instance as middleware
  vite.middlewares

  // Serve index.html as follows:
  const url = req.url

  let template
  template = fs.readFileSync(
    path.resolve(__dirname, 'index.html'),
    'utf-8'
  )
  template = await vite.transformIndexHtml(url, template)

  const render = (await vite.ssrLoadModule('/src/entry-server.js')).render

  const { 
    appHtml
  } = await render(url)

  const html = template
    .replace(`{{ appHtml }}`, appHtml)

  res.setHeader("Content-Type", "text/html")
  res.writeHead(200)
  res.end(html)
}

const server = createServer(requestListener)
server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`)
})

My attemp has the following error on the terminal:

$ npm run dev

> dev
> node server

Server is running on http://localhost:3000
WebSocket server error: Port is already in use

Browser errors:

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.
entry-client.js:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

Any ideas?

Run
  • 54,938
  • 169
  • 450
  • 748
  • 2
    Vite relies on connect middlewares, you'll have to reinvent their support from scratch when using vanilla server. Consider explaining your case. You can run multiple apps at different ports – Estus Flask Apr 08 '23 at 15:15

1 Answers1

1

If you add the Vite middleware and provide static files, it should work with Vite SSR and Node.js native HTTP server:

const { createServer } = require('http');
const { createViteServer } = require('vite');
const fs = require('fs');
const path = require('path');
const url = require('url');

const port = 3000;
const host = 'localhost';

async function createRequestListener() {
  const vite = await createViteServer({
    server: { middlewareMode: 'ssr' },
  });

  return async function (req, res) {
    try {
      const parsedUrl = new url.URL(req.url, `http://${host}:${port}`);
      let serveUrl = parsedUrl.pathname;

      if (vite.middlewares) {
        await new Promise((resolve) =>
          vite.middlewares(req, res, resolve)
        );
      }

      if (req.method === 'GET' && serveUrl.endsWith('.js')) {
        res.setHeader('Content-Type', 'application/javascript');
      }

      if (serveUrl === '/') {
        serveUrl = '/index.html';
      }

      if (serveUrl.endsWith('.html')) {
        const template = fs.readFileSync(
          path.resolve(__dirname, './index.html'),
          'utf-8'
        );

        const transformedTemplate = await vite.transformIndexHtml(
          serveUrl,
          template
        );

        const render = (await vite.ssrLoadModule('/src/entry-server.js')).render;
        const { appHtml } = await render(parsedUrl);

        const html = transformedTemplate.replace('{{ appHtml }}', appHtml);

        res.setHeader('Content-Type', 'text/html');
        res.writeHead(200);
        res.end(html);
      } else {
        res.end();
      }
    } catch (err) {
      console.error(err);
      res.writeHead(500);
      res.end(err.message);
    }
  };
}

createRequestListener().then((requestListener) => {
  const server = createServer(requestListener);
  server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`);
  });
});
Fedor Chernin
  • 146
  • 1
  • 4