Skip to content

Commit d86e3e1

Browse files
committed
fix: preserve rsc output for local production
1 parent c5e814a commit d86e3e1

File tree

7 files changed

+84
-1
lines changed

7 files changed

+84
-1
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
"dev": "pnpm run with-env vite dev",
1010
"with-env": "dotenv -e .env -e .env.local",
1111
"dev:frontend": "pnpm run with-env vite dev",
12-
"build": "vite build && cp src/instrument.server.mjs dist/server",
12+
"build": "vite build && node scripts/finalize-build.mjs",
1313
"start": "vite start",
14+
"start:prod": "pnpm run with-env node scripts/run-built-server.mjs",
1415
"lint": "oxlint --type-aware",
1516
"format": "oxfmt --write",
1617
"db:generate": "drizzle-kit generate",

scripts/finalize-build.mjs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
import url from 'node:url'
4+
5+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
6+
const rootDir = path.resolve(__dirname, '..')
7+
const distServerDir = path.join(rootDir, 'dist/server')
8+
const rscTempDir = path.join(rootDir, 'node_modules/.vite-rsc-temp/rsc')
9+
const rscDistDir = path.join(distServerDir, 'rsc')
10+
const instrumentSource = path.join(rootDir, 'src/instrument.server.mjs')
11+
const instrumentDest = path.join(distServerDir, 'instrument.server.mjs')
12+
13+
if (!fs.existsSync(rscTempDir)) {
14+
throw new Error(`Missing RSC temp output: ${rscTempDir}`)
15+
}
16+
17+
fs.rmSync(rscDistDir, { force: true, recursive: true })
18+
fs.cpSync(rscTempDir, rscDistDir, { recursive: true })
19+
fs.copyFileSync(instrumentSource, instrumentDest)

scripts/run-built-server.mjs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import http from 'node:http'
2+
import { Buffer } from 'node:buffer'
3+
import path from 'node:path'
4+
import url from 'node:url'
5+
6+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
7+
const rootDir = path.resolve(__dirname, '..')
8+
const serverModuleUrl = url.pathToFileURL(
9+
path.join(rootDir, 'dist/server/server.js'),
10+
).href
11+
12+
const mod = await import(serverModuleUrl)
13+
const app = mod.default || mod.app || mod
14+
const port = Number(process.env.PORT || 3000)
15+
16+
const server = http.createServer(async (req, res) => {
17+
try {
18+
const requestUrl = new URL(req.url || '/', `http://localhost:${port}`)
19+
const chunks = []
20+
21+
for await (const chunk of req) {
22+
chunks.push(chunk)
23+
}
24+
25+
const request = new Request(requestUrl, {
26+
method: req.method,
27+
headers: req.headers,
28+
body:
29+
req.method === 'GET' || req.method === 'HEAD'
30+
? undefined
31+
: Buffer.concat(chunks),
32+
})
33+
const response = await app.fetch(request)
34+
35+
res.statusCode = response.status
36+
response.headers.forEach((value, key) => {
37+
res.setHeader(key, value)
38+
})
39+
40+
if (!response.body || req.method === 'HEAD') {
41+
res.end()
42+
return
43+
}
44+
45+
res.end(Buffer.from(await response.arrayBuffer()))
46+
} catch (error) {
47+
console.error(error)
48+
res.statusCode = 500
49+
res.end(String(error?.stack || error))
50+
}
51+
})
52+
53+
server.listen(port, () => {
54+
console.log(`Production server listening on http://localhost:${port}`)
55+
})

src/components/ThemeProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client'
2+
13
import { createClientOnlyFn, createIsomorphicFn } from '@tanstack/react-start'
24
import * as React from 'react'
35
import { createContext, ReactNode, useEffect, useState } from 'react'

src/components/ToastProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client'
2+
13
import * as React from 'react'
24
import * as Toast from '@radix-ui/react-toast'
35

src/contexts/LoginModalContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client'
2+
13
import * as React from 'react'
24
import { useQueryClient } from '@tanstack/react-query'
35
import { LoginModal } from '~/components/LoginModal'

src/contexts/SearchContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client'
2+
13
import * as React from 'react'
24

35
interface SearchContextType {

0 commit comments

Comments
 (0)