import path from 'path' import fs from 'fs/promises' import dotenv from 'dotenv' import express, { Handler, Router } from 'express' import morgan from 'morgan' import { createServer as createViteServer } from 'vite' import { createApiRouter } from './server/routes' import { RenderFunction } from './shared/ssr' // Load ".env" dotenv.config() // const HTML_ROUTES = ['/', '/login', '/problem/:id', '/u/:uid', '/admin', '/profile', '/scores'] const config = { isDevelopment: process.env.MODE === 'development', port: process.env.PORT || 3000, } if (config.isDevelopment) { console.log(`[Config] Running in development mode`) console.log(`[Config] PORT = ${config.port}`) } // function mountIndexHtmlRoutes(r: Router, serveIndexHtml: Handler) { // for (const route of HTML_ROUTES) { // console.log(`[Server] Mounted index html for "${route}"`) // r.get(route, serveIndexHtml) // } // } async function createDevRouter() { const r = express.Router() const vite = await createViteServer({ server: { middlewareMode: true }, appType: 'custom', }) r.use(vite.middlewares) r.use('*', async (req, res, next) => { try { const indexHtml = await fs.readFile(path.resolve('./index.html'), 'utf-8') const transformedTemplate = await vite.transformIndexHtml(req.originalUrl, indexHtml) // Load (to be bundled) entry point for server side rendering const render: RenderFunction = (await vite.ssrLoadModule('./client/entry-server.tsx')).default const { html, metadata } = render(req.originalUrl) const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}` const metaTagsHtml = '' + (metadata.title ? `\n` : '') + (metadata.description ? `\n` : '') + `\n` res.send(transformedTemplate.replace('', metaTagsHtml).replace('', html)) } catch (error: any) { vite.ssrFixStacktrace(error) next(error) } }) return r } async function createProductionRouter() { // Load bundled entry point for server side rendering // @ts-ignore const render: RenderFunction = (await import('./dist/entry-server/entry-server.js')).default const r = express.Router() r.use('/', express.static('dist/entry-client')) r.use('*', async (req, res) => { const transformedTemplate = await fs.readFile(path.resolve('./dist/entry-client/index.html'), 'utf-8') const { html, metadata } = render(req.originalUrl) const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}` const metaTagsHtml = '' + (metadata.title ? `\n` : '') + (metadata.description ? `\n` : '') + `\n` res.send(transformedTemplate.replace('', metaTagsHtml).replace('', html)) }) return r } async function main() { const app = express() app.use(morgan('[Request] :method :url :status :response-time ms - :res[content-length]')) app.use('/', await createApiRouter()) if (config.isDevelopment) { app.use('/', await createDevRouter()) } else { app.use('/', await createProductionRouter()) } app.listen(config.port, () => { console.log(`[Server] Listening on port ${config.port}...`) }) } main()