|
1 |
| -import fs from 'node:fs/promises' |
2 |
| -import express from 'express' |
3 |
| -import { |
4 |
| - discoverProjectStyles, |
5 |
| - loadStyleDefinitions, |
6 |
| - createCriticalStyleStream, |
7 |
| -} from 'used-styles' |
| 1 | +import fs from 'node:fs/promises'; |
| 2 | + |
| 3 | +import express from 'express'; |
| 4 | +import { loadStyleDefinitions, createCriticalStyleStream } from 'used-styles'; |
| 5 | +import { discoverProjectStyles } from 'used-styles/node'; |
8 | 6 |
|
9 | 7 | // Constants
|
10 |
| -const isProduction = process.env.NODE_ENV === 'production' |
11 |
| -const port = process.env.PORT || 5173 |
12 |
| -const base = process.env.BASE || '/' |
13 |
| -const ABORT_DELAY = 10000 |
| 8 | +const isProduction = process.env.NODE_ENV === 'production'; |
| 9 | +const port = process.env.PORT || 5173; |
| 10 | +const base = process.env.BASE || '/'; |
| 11 | +const ABORT_DELAY = 10000; |
14 | 12 |
|
15 | 13 | // generate lookup table on server start
|
16 | 14 | const stylesLookup = isProduction
|
17 | 15 | ? discoverProjectStyles('./dist/client')
|
18 |
| - // in dev mode vite injects all styles to <head/> element |
19 |
| - : loadStyleDefinitions(async () => []) |
| 16 | + : // in dev mode vite injects all styles to <head/> element |
| 17 | + loadStyleDefinitions(async () => []); |
20 | 18 |
|
21 | 19 | // Cached production assets
|
22 |
| -const templateHtml = isProduction |
23 |
| - ? await fs.readFile('./dist/client/index.html', 'utf-8') |
24 |
| - : '' |
25 |
| -const ssrManifest = isProduction |
26 |
| - ? await fs.readFile('./dist/client/.vite/ssr-manifest.json', 'utf-8') |
27 |
| - : undefined |
| 20 | +const templateHtml = isProduction ? await fs.readFile('./dist/client/index.html', 'utf-8') : ''; |
| 21 | +const ssrManifest = isProduction ? await fs.readFile('./dist/client/.vite/ssr-manifest.json', 'utf-8') : undefined; |
28 | 22 |
|
29 | 23 | // Create http server
|
30 |
| -const app = express() |
| 24 | +const app = express(); |
31 | 25 |
|
32 | 26 | // Add Vite or respective production middlewares
|
33 |
| -let vite |
| 27 | +let vite; |
| 28 | + |
34 | 29 | if (!isProduction) {
|
35 |
| - const { createServer } = await import('vite') |
| 30 | + const { createServer } = await import('vite'); |
| 31 | + |
36 | 32 | vite = await createServer({
|
37 | 33 | server: { middlewareMode: true },
|
38 | 34 | appType: 'custom',
|
39 |
| - base |
40 |
| - }) |
41 |
| - app.use(vite.middlewares) |
| 35 | + base, |
| 36 | + }); |
| 37 | + |
| 38 | + app.use(vite.middlewares); |
42 | 39 | } else {
|
43 |
| - const compression = (await import('compression')).default |
44 |
| - const sirv = (await import('sirv')).default |
45 |
| - app.use(compression()) |
46 |
| - app.use(base, sirv('./dist/client', { extensions: [] })) |
| 40 | + const compression = (await import('compression')).default; |
| 41 | + const sirv = (await import('sirv')).default; |
| 42 | + app.use(compression()); |
| 43 | + app.use(base, sirv('./dist/client', { extensions: [] })); |
47 | 44 | }
|
48 | 45 |
|
49 | 46 | // Serve HTML
|
50 | 47 | app.use('*', async (req, res) => {
|
51 | 48 | try {
|
52 |
| - await stylesLookup |
| 49 | + await stylesLookup; |
53 | 50 |
|
54 |
| - const url = req.originalUrl.replace(base, '') |
| 51 | + const url = req.originalUrl.replace(base, ''); |
| 52 | + |
| 53 | + let template; |
| 54 | + let render; |
55 | 55 |
|
56 |
| - let template |
57 |
| - let render |
58 | 56 | if (!isProduction) {
|
59 | 57 | // Always read fresh template in development
|
60 |
| - template = await fs.readFile('./index.html', 'utf-8') |
61 |
| - template = await vite.transformIndexHtml(url, template) |
62 |
| - render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render |
| 58 | + template = await fs.readFile('./index.html', 'utf-8'); |
| 59 | + template = await vite.transformIndexHtml(url, template); |
| 60 | + render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render; |
63 | 61 | } else {
|
64 |
| - template = templateHtml |
65 |
| - render = (await import('./dist/server/entry-server.js')).render |
| 62 | + template = templateHtml; |
| 63 | + render = (await import('./dist/server/entry-server.js')).render; |
66 | 64 | }
|
67 | 65 |
|
68 |
| - const styledStream = createCriticalStyleStream(stylesLookup) |
| 66 | + const styledStream = createCriticalStyleStream(stylesLookup); |
69 | 67 |
|
70 |
| - let didError = false |
| 68 | + let didError = false; |
71 | 69 |
|
72 | 70 | const { pipe, abort } = render(url, ssrManifest, {
|
73 | 71 | onShellError() {
|
74 |
| - res.status(500) |
75 |
| - res.set({ 'Content-Type': 'text/html' }) |
76 |
| - res.send('<h1>Something went wrong</h1>') |
| 72 | + res.status(500); |
| 73 | + res.set({ 'Content-Type': 'text/html' }); |
| 74 | + res.send('<h1>Something went wrong</h1>'); |
77 | 75 | },
|
78 | 76 | // Can use also `onAllReady` callback
|
79 | 77 | onShellReady() {
|
80 |
| - res.status(didError ? 500 : 200) |
81 |
| - res.set({ 'Content-Type': 'text/html' }) |
| 78 | + res.status(didError ? 500 : 200); |
| 79 | + res.set({ 'Content-Type': 'text/html' }); |
82 | 80 |
|
83 |
| - let [htmlStart, htmlEnd] = template.split(`<!--app-html-->`) |
| 81 | + let [htmlStart, htmlEnd] = template.split(`<!--app-html-->`); |
84 | 82 |
|
85 |
| - // React 19 supports document metadata out of box, |
| 83 | + // React 19 supports document metadata out of box, |
86 | 84 | // but for react 18 we can use `react-helmet-async` here:
|
87 | 85 | // htmlStart = htmlStart.replace(`<!--app-head-->`, helmet.title.toString())
|
88 |
| - |
89 |
| - res.write(htmlStart) |
90 | 86 |
|
91 |
| - styledStream.pipe(res, { end: false }) |
| 87 | + res.write(htmlStart); |
| 88 | + |
| 89 | + styledStream.pipe(res, { end: false }); |
92 | 90 |
|
93 |
| - pipe(styledStream) |
| 91 | + pipe(styledStream); |
94 | 92 |
|
95 | 93 | styledStream.on('end', () => {
|
96 |
| - res.end(htmlEnd) |
97 |
| - }) |
| 94 | + res.end(htmlEnd); |
| 95 | + }); |
98 | 96 | },
|
99 | 97 | onError(error) {
|
100 |
| - didError = true |
101 |
| - console.error(error) |
| 98 | + didError = true; |
| 99 | + console.error(error); |
102 | 100 | // You can log crash reports here:
|
103 | 101 | // logServerCrashReport(error)
|
104 |
| - } |
105 |
| - }) |
| 102 | + }, |
| 103 | + }); |
106 | 104 |
|
107 | 105 | setTimeout(() => {
|
108 |
| - abort() |
109 |
| - }, ABORT_DELAY) |
| 106 | + abort(); |
| 107 | + }, ABORT_DELAY); |
110 | 108 | } catch (e) {
|
111 |
| - vite?.ssrFixStacktrace(e) |
112 |
| - console.log(e.stack) |
113 |
| - res.status(500).end(e.stack) |
| 109 | + vite?.ssrFixStacktrace(e); |
| 110 | + console.log(e.stack); |
| 111 | + res.status(500).end(e.stack); |
114 | 112 | }
|
115 |
| -}) |
| 113 | +}); |
116 | 114 |
|
117 | 115 | // Start http server
|
118 | 116 | app.listen(port, () => {
|
119 |
| - console.log(`Server started at http://localhost:${port}`) |
120 |
| -}) |
| 117 | + console.log(`Server started at http://localhost:${port}`); |
| 118 | +}); |
0 commit comments