server.mjs 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // @ts-check
  2. import fs from 'node:fs';
  3. import path from 'node:path';
  4. import { fileURLToPath } from 'node:url';
  5. import express from 'express';
  6. const isTest = process.env.VITEST;
  7. export async function createServer(root = process.cwd(), isProd = process.env.NODE_ENV === 'production', hmrPort) {
  8. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  9. const resolve = (p) => path.resolve(__dirname, p);
  10. const indexProd = isProd ? fs.readFileSync(resolve('dist/client/index.html'), 'utf-8') : '';
  11. const manifest = isProd ? JSON.parse(fs.readFileSync(resolve('dist/client/ssr-manifest.json'), 'utf-8')) : {};
  12. const app = express();
  13. /**
  14. * @type {import('vite').ViteDevServer}
  15. */
  16. let vite;
  17. if (!isProd) {
  18. vite = await (
  19. await import('vite')
  20. ).createServer({
  21. base: '/',
  22. root,
  23. mode: 'aktivisda',
  24. logLevel: isTest ? 'error' : 'info',
  25. server: {
  26. middlewareMode: true,
  27. watch: {
  28. // During tests we edit the files too fast and sometimes chokidar
  29. // misses change events, so enforce polling for consistency
  30. usePolling: true,
  31. interval: 100,
  32. },
  33. hmr: {
  34. port: hmrPort,
  35. },
  36. },
  37. appType: 'custom',
  38. });
  39. // use vite's connect instance as middleware
  40. app.use(vite.middlewares);
  41. } else {
  42. app.use((await import('compression')).default());
  43. app.use(
  44. '/',
  45. (await import('serve-static')).default(resolve('dist/client'), {
  46. index: false,
  47. })
  48. );
  49. }
  50. app.use('*', async (req, res) => {
  51. try {
  52. const url = req.originalUrl.replace('/', '/');
  53. let template, render;
  54. if (!isProd) {
  55. // always read fresh template in dev
  56. template = fs.readFileSync(resolve('index.html'), 'utf-8');
  57. template = await vite.transformIndexHtml(url, template);
  58. render = (await vite.ssrLoadModule('/src/entry-server.mjs')).render;
  59. } else {
  60. template = indexProd;
  61. // @ts-ignore
  62. render = (await import('./dist/server/entry-server.mjs')).render;
  63. }
  64. const [appHtml, preloadLinks, state, metaTags] = await render(url, manifest);
  65. let html = template.replace(`<!--preload-links-->`, preloadLinks).replace(`'<pinia-store>'`, state).replace(`<!--app-html-->`, appHtml);
  66. for (const metaTag in metaTags) {
  67. html = html.replace(metaTag, metaTags[metaTag]);
  68. }
  69. res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
  70. } catch (e) {
  71. vite && vite.ssrFixStacktrace(e);
  72. console.log(e.stack);
  73. res.status(500).end(e.stack);
  74. }
  75. });
  76. return { app, vite };
  77. }
  78. if (!isTest) {
  79. createServer().then(({ app }) =>
  80. app.listen(6173, () => {
  81. console.log('http://localhost:6173');
  82. })
  83. );
  84. }