feat: use all native node deps for the server
This commit is contained in:
parent
12754bd0aa
commit
5c949e2da5
@ -4,7 +4,7 @@
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "remix vite:build",
|
||||
"build": "remix vite:build && vite build",
|
||||
"dev": "remix vite:dev",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
@ -13,7 +13,6 @@
|
||||
"@dnd-kit/modifiers": "^7.0.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@hono/node-server": "^1.13.5",
|
||||
"@kubernetes/client-node": "^0.22.2",
|
||||
"@primer/octicons-react": "^19.12.0",
|
||||
"@react-aria/toast": "3.0.0-beta.12",
|
||||
@ -25,15 +24,14 @@
|
||||
"@uiw/react-codemirror": "^4.23.6",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"hono": "^4.6.9",
|
||||
"isbot": "^5.1.17",
|
||||
"mime": "^4.0.4",
|
||||
"oauth4webapi": "^2.17.0",
|
||||
"react": "19.0.0-beta-26f2496093-20240514",
|
||||
"react-aria-components": "^1.2.1",
|
||||
"react-codemirror-merge": "^4.23.6",
|
||||
"react-dom": "19.0.0-beta-26f2496093-20240514",
|
||||
"react-error-boundary": "^4.1.2",
|
||||
"remix-hono": "^0.0.16",
|
||||
"remix-utils": "^7.7.0",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwindcss-react-aria-components": "^1.1.6",
|
||||
|
||||
@ -28,9 +28,6 @@ importers:
|
||||
'@dnd-kit/utilities':
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2(react@19.0.0-beta-26f2496093-20240514)
|
||||
'@hono/node-server':
|
||||
specifier: ^1.13.5
|
||||
version: 1.13.5(hono@4.6.9)
|
||||
'@kubernetes/client-node':
|
||||
specifier: ^0.22.2
|
||||
version: 0.22.2
|
||||
@ -64,12 +61,12 @@ importers:
|
||||
dotenv:
|
||||
specifier: ^16.4.5
|
||||
version: 16.4.5
|
||||
hono:
|
||||
specifier: ^4.6.9
|
||||
version: 4.6.9
|
||||
isbot:
|
||||
specifier: ^5.1.17
|
||||
version: 5.1.17
|
||||
mime:
|
||||
specifier: ^4.0.4
|
||||
version: 4.0.4
|
||||
oauth4webapi:
|
||||
specifier: ^2.17.0
|
||||
version: 2.17.0
|
||||
@ -88,9 +85,6 @@ importers:
|
||||
react-error-boundary:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2(react@19.0.0-beta-26f2496093-20240514)
|
||||
remix-hono:
|
||||
specifier: ^0.0.16
|
||||
version: 0.0.16(typescript@5.6.3)(zod@3.23.8)
|
||||
remix-utils:
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0(@remix-run/node@2.13.1(typescript@5.6.3))(@remix-run/react@2.13.1(react-dom@19.0.0-beta-26f2496093-20240514(react@19.0.0-beta-26f2496093-20240514))(react@19.0.0-beta-26f2496093-20240514)(typescript@5.6.3))(@remix-run/router@1.20.0)(react@19.0.0-beta-26f2496093-20240514)(zod@3.23.8)
|
||||
@ -666,12 +660,6 @@ packages:
|
||||
'@formatjs/intl-localematcher@0.5.4':
|
||||
resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==}
|
||||
|
||||
'@hono/node-server@1.13.5':
|
||||
resolution: {integrity: sha512-lSo+CFlLqAFB4fX7ePqI9nauEn64wOfJHAfc9duYFTvAG3o416pC0nTGeNjuLHchLedH+XyWda5v79CVx1PIjg==}
|
||||
engines: {node: '>=18.14.1'}
|
||||
peerDependencies:
|
||||
hono: ^4
|
||||
|
||||
'@internationalized/date@3.5.6':
|
||||
resolution: {integrity: sha512-jLxQjefH9VI5P9UQuqB6qNKnvFt1Ky1TPIzHGsIlCi7sZZoMR8SdYbBGRvM0y+Jtb+ez4ieBzmiAUcpmPYpyOw==}
|
||||
|
||||
@ -2279,10 +2267,6 @@ packages:
|
||||
hast-util-whitespace@2.0.1:
|
||||
resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==}
|
||||
|
||||
hono@4.6.9:
|
||||
resolution: {integrity: sha512-p/pN5yZLuZaHzyAOT2nw2/Ud6HhJHYmDNGH6Ck1OWBhPMVeM1r74jbCRwNi0gyFRjjbsGgoHbOyj7mT1PDNbTw==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
|
||||
hosted-git-info@6.1.1:
|
||||
resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
@ -2737,6 +2721,11 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
mime@4.0.4:
|
||||
resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
mimic-fn@2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
@ -3084,10 +3073,6 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
pretty-cache-header@1.0.0:
|
||||
resolution: {integrity: sha512-xtXazslu25CdnGnUkByU1RoOjK55TqwatJkjjJLg5ZAdz2Lngko/mmaUgeET36P2GMlNwh3fdM7FWBO717pNcw==}
|
||||
engines: {node: '>=12.13'}
|
||||
|
||||
pretty-format@24.9.0:
|
||||
resolution: {integrity: sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==}
|
||||
engines: {node: '>= 6'}
|
||||
@ -3252,23 +3237,6 @@ packages:
|
||||
remark-rehype@10.1.0:
|
||||
resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==}
|
||||
|
||||
remix-hono@0.0.16:
|
||||
resolution: {integrity: sha512-IPooI2E0eSFRV9wAgTzpsklSoOyij6Nsk6ANIugGhD9vYlovbYb2BT+siz++sLzWseZSPEtIzx/LBel203jBfQ==}
|
||||
peerDependencies:
|
||||
'@remix-run/cloudflare': ^2.0.0
|
||||
i18next: ^23.0.0
|
||||
remix-i18next: ^6.0.0
|
||||
zod: ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
'@remix-run/cloudflare':
|
||||
optional: true
|
||||
i18next:
|
||||
optional: true
|
||||
remix-i18next:
|
||||
optional: true
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
remix-utils@7.7.0:
|
||||
resolution: {integrity: sha512-J8NhP044nrNIam/xOT1L9a4RQ9FSaA2wyrUwmN8ZT+c/+CdAAf70yfaLnvMyKcV5U+8BcURQ/aVbth77sT6jGA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@ -3562,10 +3530,6 @@ packages:
|
||||
through2@2.0.5:
|
||||
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
|
||||
|
||||
timestring@6.0.0:
|
||||
resolution: {integrity: sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
to-fast-properties@2.0.0:
|
||||
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
||||
engines: {node: '>=4'}
|
||||
@ -4390,10 +4354,6 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
|
||||
'@hono/node-server@1.13.5(hono@4.6.9)':
|
||||
dependencies:
|
||||
hono: 4.6.9
|
||||
|
||||
'@internationalized/date@3.5.6':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.11
|
||||
@ -6645,8 +6605,6 @@ snapshots:
|
||||
|
||||
hast-util-whitespace@2.0.1: {}
|
||||
|
||||
hono@4.6.9: {}
|
||||
|
||||
hosted-git-info@6.1.1:
|
||||
dependencies:
|
||||
lru-cache: 7.18.3
|
||||
@ -7230,6 +7188,8 @@ snapshots:
|
||||
|
||||
mime@1.6.0: {}
|
||||
|
||||
mime@4.0.4: {}
|
||||
|
||||
mimic-fn@2.1.0: {}
|
||||
|
||||
minimatch@9.0.5:
|
||||
@ -7559,10 +7519,6 @@ snapshots:
|
||||
|
||||
prettier@2.8.8: {}
|
||||
|
||||
pretty-cache-header@1.0.0:
|
||||
dependencies:
|
||||
timestring: 6.0.0
|
||||
|
||||
pretty-format@24.9.0:
|
||||
dependencies:
|
||||
'@jest/types': 24.9.0
|
||||
@ -7833,16 +7789,6 @@ snapshots:
|
||||
mdast-util-to-hast: 12.3.0
|
||||
unified: 10.1.2
|
||||
|
||||
remix-hono@0.0.16(typescript@5.6.3)(zod@3.23.8):
|
||||
dependencies:
|
||||
'@remix-run/server-runtime': 2.13.1(typescript@5.6.3)
|
||||
hono: 4.6.9
|
||||
pretty-cache-header: 1.0.0
|
||||
optionalDependencies:
|
||||
zod: 3.23.8
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
remix-utils@7.7.0(@remix-run/node@2.13.1(typescript@5.6.3))(@remix-run/react@2.13.1(react-dom@19.0.0-beta-26f2496093-20240514(react@19.0.0-beta-26f2496093-20240514))(react@19.0.0-beta-26f2496093-20240514)(typescript@5.6.3))(@remix-run/router@1.20.0)(react@19.0.0-beta-26f2496093-20240514)(zod@3.23.8):
|
||||
dependencies:
|
||||
type-fest: 4.26.1
|
||||
@ -8199,8 +8145,6 @@ snapshots:
|
||||
readable-stream: 2.3.8
|
||||
xtend: 4.0.2
|
||||
|
||||
timestring@6.0.0: {}
|
||||
|
||||
to-fast-properties@2.0.0: {}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
|
||||
119
server.mjs
119
server.mjs
@ -1,6 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
// This is a polyglot entrypoint for Headplane when running in production
|
||||
// It doesn't use any dependencies aside from @remix-run/node and mime
|
||||
// During build we bundle the used dependencies into the file so that
|
||||
// we can only need this file and a Node.js installation to run the server.
|
||||
// PREFIX is defined globally, see vite.config.ts
|
||||
|
||||
import { access, constants } from 'node:fs/promises'
|
||||
import { createReadStream, existsSync, statSync } from 'node:fs'
|
||||
import { createServer } from 'node:http'
|
||||
import { join, resolve } from 'node:path'
|
||||
|
||||
function log(level, message) {
|
||||
const date = new Date().toISOString()
|
||||
@ -28,25 +35,105 @@ try {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const { installGlobals } = await import('@remix-run/node')
|
||||
const { remix } = await import('remix-hono/handler')
|
||||
const { serve } = await import('@hono/node-server')
|
||||
const { Hono } = await import('hono')
|
||||
const {
|
||||
createRequestHandler: remixRequestHandler,
|
||||
createReadableStreamFromReadable,
|
||||
writeReadableStreamToWritable
|
||||
} = await import('@remix-run/node')
|
||||
const { default: mime } = await import('mime')
|
||||
|
||||
installGlobals()
|
||||
const app = new Hono()
|
||||
const port = process.env.PORT || 3000
|
||||
const host = process.env.HOST || '0.0.0.0'
|
||||
const buildPath = process.env.BUILD_PATH || './build'
|
||||
|
||||
app.use('*', remix({
|
||||
build: await import('./build/server/index.js'),
|
||||
mode: 'production'
|
||||
}))
|
||||
// Because this is a dynamic import without an easily discernable path
|
||||
// we gain the "deoptimization" we want so that Vite doesn't bundle this
|
||||
const build = await import(resolve(join(buildPath, 'server', 'index.js')))
|
||||
const baseDir = resolve(join(buildPath, 'client'))
|
||||
|
||||
serve({
|
||||
fetch: app.fetch,
|
||||
hostname: host,
|
||||
port
|
||||
const handler = remixRequestHandler(build, 'production')
|
||||
const http = createServer(async (req, res) => {
|
||||
const url = new URL(`http://${req.headers.host}${req.url}`)
|
||||
|
||||
// Before we pass any requests to our Remix handler we need to check
|
||||
// if we can handle a raw file request. This is important for the
|
||||
// Remix loader to work correctly.
|
||||
//
|
||||
// To optimize this, we send them as readable streams in the node
|
||||
// response and we also set headers for aggressive caching.
|
||||
if (url.pathname.startsWith(`${PREFIX}assets/`)) {
|
||||
const filePath = join(baseDir, url.pathname.replace(PREFIX, ''))
|
||||
const exists = existsSync(filePath)
|
||||
const stats = statSync(filePath)
|
||||
|
||||
if (exists && stats.isFile()) {
|
||||
// Build assets are cache-bust friendly so we can cache them heavily
|
||||
if (req.url.startsWith('/build')) {
|
||||
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
||||
}
|
||||
|
||||
// Send the file as a readable stream
|
||||
const fileStream = createReadStream(filePath)
|
||||
const type = mime.getType(filePath)
|
||||
|
||||
res.setHeader('Content-Length', stats.size)
|
||||
res.setHeader('Content-Type', type)
|
||||
fileStream.pipe(res)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Handling the request
|
||||
const controller = new AbortController()
|
||||
res.on('close', () => controller.abort())
|
||||
|
||||
const headers = new Headers()
|
||||
for (const [key, value] of Object.entries(req.headers)) {
|
||||
if (!value) continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (const v of value) {
|
||||
headers.append(key, v)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
headers.append(key, value)
|
||||
}
|
||||
|
||||
const remixReq = new Request(url.href, {
|
||||
headers,
|
||||
method: req.method,
|
||||
signal: controller.signal,
|
||||
|
||||
// If we have a body we set a duplex and we load the body
|
||||
...(req.method !== 'GET' && req.method !== 'HEAD' ? {
|
||||
body: createReadableStreamFromReadable(req),
|
||||
duplex: 'half'
|
||||
} : {}
|
||||
)
|
||||
})
|
||||
|
||||
// Pass our request to the Remix handler and get a response
|
||||
const response = await handler(remixReq, {}) // No context
|
||||
|
||||
// Handle our response and reply
|
||||
res.statusCode = response.status
|
||||
res.statusMessage = response.statusText
|
||||
|
||||
for (const [key, value] of response.headers.entries()) {
|
||||
res.appendHeader(key, value)
|
||||
}
|
||||
|
||||
if (response.body) {
|
||||
await writeReadableStreamToWritable(response.body, res)
|
||||
return
|
||||
}
|
||||
|
||||
res.end()
|
||||
})
|
||||
|
||||
log('INFO', `Running on ${host}:${port}`)
|
||||
http.listen(port, host, () => {
|
||||
log('INFO', `Running on ${host}:${port}`)
|
||||
})
|
||||
|
||||
@ -6,12 +6,48 @@ import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
|
||||
installGlobals()
|
||||
|
||||
export default defineConfig(({ isSsrBuild }) => ({
|
||||
base: '/admin/',
|
||||
const prefix = process.env.__INTERNAL_PREFIX || '/admin/'
|
||||
if (!prefix.endsWith('/')) {
|
||||
throw new Error('Prefix must end with a slash')
|
||||
}
|
||||
|
||||
export default defineConfig(({ isSsrBuild }) => {
|
||||
// If we have the Headplane entry we build it as a single
|
||||
// server.mjs file that is built for production server bundle
|
||||
// We know the remix invoked command is vite:build
|
||||
if (!process.argv.includes('vite:build')) {
|
||||
return {
|
||||
build: {
|
||||
minify: false,
|
||||
target: 'esnext',
|
||||
rollupOptions: {
|
||||
input: './server.mjs',
|
||||
output: {
|
||||
entryFileNames: 'server.js',
|
||||
dir: 'build/headplane',
|
||||
banner: '#!/usr/bin/env node\n',
|
||||
},
|
||||
external: (id) => id.startsWith('node:'),
|
||||
}
|
||||
},
|
||||
define: {
|
||||
PREFIX: JSON.stringify(prefix),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
stream: 'node:stream',
|
||||
crypto: 'node:crypto',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ({
|
||||
base: prefix,
|
||||
build: isSsrBuild ? { target: 'ES2022' } : {},
|
||||
plugins: [
|
||||
remix({
|
||||
basename: '/admin/',
|
||||
basename: prefix,
|
||||
}),
|
||||
tsconfigPaths(),
|
||||
babel({
|
||||
@ -24,4 +60,5 @@ export default defineConfig(({ isSsrBuild }) => ({
|
||||
},
|
||||
}),
|
||||
],
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user