feat: switch to server in dev & prod
This commit is contained in:
parent
6cf343d623
commit
1c2c374ada
@ -5,7 +5,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "remix vite:build && vite build",
|
"build": "remix vite:build && vite build",
|
||||||
"dev": "remix vite:dev",
|
"dev": "node server/dev.mjs",
|
||||||
|
"start": "node build/headplane/server.js",
|
||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -43,6 +44,7 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/preset-typescript": "^7.26.0",
|
||||||
"@remix-run/dev": "^2.15.0",
|
"@remix-run/dev": "^2.15.0",
|
||||||
"@remix-run/route-config": "^2.15.0",
|
"@remix-run/route-config": "^2.15.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
|
|||||||
@ -110,6 +110,9 @@ importers:
|
|||||||
specifier: ^3.23.8
|
specifier: ^3.23.8
|
||||||
version: 3.23.8
|
version: 3.23.8
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@babel/preset-typescript':
|
||||||
|
specifier: ^7.26.0
|
||||||
|
version: 7.26.0(@babel/core@7.26.0)
|
||||||
'@remix-run/dev':
|
'@remix-run/dev':
|
||||||
specifier: ^2.15.0
|
specifier: ^2.15.0
|
||||||
version: 2.15.0(@remix-run/react@2.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.2))(@types/node@22.10.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.1)(jiti@1.21.6)(yaml@2.6.1))
|
version: 2.15.0(@remix-run/react@2.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.7.2))(@types/node@22.10.1)(typescript@5.7.2)(vite@6.0.3(@types/node@22.10.1)(jiti@1.21.6)(yaml@2.6.1))
|
||||||
|
|||||||
33
server/dev.mjs
Normal file
33
server/dev.mjs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// This is a polyglot entrypoint for Headplane when running in development
|
||||||
|
// It does some silly magic to load the vite config, set some globals that
|
||||||
|
// are required to function, and create the vite development server.
|
||||||
|
import { createServer } from 'vite'
|
||||||
|
import { env, exit } from 'node:process'
|
||||||
|
import { log } from './utils.mjs'
|
||||||
|
|
||||||
|
log('DEVX', 'INFO', 'This script is only intended for development')
|
||||||
|
env.NODE_ENV = 'development'
|
||||||
|
|
||||||
|
// The production entrypoint uses a global called "PREFIX" to determine
|
||||||
|
// what route the application is being served at and a global called "BUILD"
|
||||||
|
// to determine the Remix handler. We need to set these globals here so that
|
||||||
|
// the development server can function correctly and override the production
|
||||||
|
// values.
|
||||||
|
|
||||||
|
log('DEVX', 'INFO', 'Creating Vite Development Server')
|
||||||
|
const server = await createServer({
|
||||||
|
server: {
|
||||||
|
middlewareMode: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// This entrypoint is defined in the documentation to load the server
|
||||||
|
const build = await server.ssrLoadModule('virtual:remix/server-build')
|
||||||
|
|
||||||
|
// We already handle this logic in the Vite configuration
|
||||||
|
global.PREFIX = server.config.base.slice(0, -1)
|
||||||
|
global.BUILD = build
|
||||||
|
global.MODE = 'development'
|
||||||
|
global.MIDDLEWARE = server.middlewares
|
||||||
|
|
||||||
|
await import('./prod.mjs')
|
||||||
50
server.mjs → server/prod.mjs
Executable file → Normal file
50
server.mjs → server/prod.mjs
Executable file → Normal file
@ -9,30 +9,16 @@ import { createReadStream, existsSync, statSync } from 'node:fs'
|
|||||||
import { createServer } from 'node:http'
|
import { createServer } from 'node:http'
|
||||||
import { join, resolve } from 'node:path'
|
import { join, resolve } from 'node:path'
|
||||||
import { env } from 'node:process'
|
import { env } from 'node:process'
|
||||||
|
import { log } from './utils.mjs'
|
||||||
|
|
||||||
function log(level, message) {
|
log('SRVX', 'INFO', `Running with Node.js ${process.versions.node}`)
|
||||||
const date = new Date().toISOString()
|
|
||||||
console.log(`${date} (${level}) [SRVX] ${message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
log('INFO', `Running with Node.js ${process.versions.node}`)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await access('./node_modules/@remix-run', constants.F_OK | constants.R_OK)
|
await access('./node_modules/@remix-run', constants.F_OK | constants.R_OK)
|
||||||
log('INFO', 'Found node_modules dependencies')
|
log('SRVX', 'INFO', 'Found node_modules dependencies')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('ERROR', 'No node_modules found. Please run `pnpm install` first')
|
log('SRVX', 'ERROR', 'No node_modules found. Please run `pnpm install`')
|
||||||
log('ERROR', error)
|
log('SRVX', 'ERROR', error)
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await access('./build/server', constants.F_OK | constants.R_OK)
|
|
||||||
log('INFO', 'Found build directory')
|
|
||||||
} catch (error) {
|
|
||||||
const date = new Date().toISOString()
|
|
||||||
log('ERROR', 'No build directory found. Please run `pnpm build` first')
|
|
||||||
log('ERROR', error)
|
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,16 +32,36 @@ const { default: mime } = await import('mime')
|
|||||||
const port = env.PORT || 3000
|
const port = env.PORT || 3000
|
||||||
const host = env.HOST || '0.0.0.0'
|
const host = env.HOST || '0.0.0.0'
|
||||||
const buildPath = env.BUILD_PATH || './build'
|
const buildPath = env.BUILD_PATH || './build'
|
||||||
|
const baseDir = resolve(join(buildPath, 'client'))
|
||||||
|
|
||||||
|
if (!global.BUILD) {
|
||||||
|
try {
|
||||||
|
await access(join(buildPath, 'server'), constants.F_OK | constants.R_OK)
|
||||||
|
log('SRVX', 'INFO', 'Found build directory')
|
||||||
|
} catch (error) {
|
||||||
|
const date = new Date().toISOString()
|
||||||
|
log('SRVX', 'ERROR', 'No build found. Please run `pnpm build`')
|
||||||
|
log('SRVX', 'ERROR', error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// Because this is a dynamic import without an easily discernable path
|
// Because this is a dynamic import without an easily discernable path
|
||||||
// we gain the "deoptimization" we want so that Vite doesn't bundle this
|
// we gain the "deoptimization" we want so that Vite doesn't bundle this
|
||||||
const build = await import(resolve(join(buildPath, 'server', 'index.js')))
|
const build = await import(resolve(join(buildPath, 'server', 'index.js')))
|
||||||
const baseDir = resolve(join(buildPath, 'client'))
|
global.BUILD = build
|
||||||
|
global.MODE = 'production'
|
||||||
|
}
|
||||||
|
|
||||||
const handler = remixRequestHandler(build, 'production')
|
const handler = remixRequestHandler(global.BUILD, global.MODE)
|
||||||
const http = createServer(async (req, res) => {
|
const http = createServer(async (req, res) => {
|
||||||
const url = new URL(`http://${req.headers.host}${req.url}`)
|
const url = new URL(`http://${req.headers.host}${req.url}`)
|
||||||
|
|
||||||
|
if (global.MIDDLEWARE) {
|
||||||
|
await new Promise(resolve => {
|
||||||
|
global.MIDDLEWARE(req, res, resolve)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!url.pathname.startsWith(PREFIX)) {
|
if (!url.pathname.startsWith(PREFIX)) {
|
||||||
res.writeHead(404)
|
res.writeHead(404)
|
||||||
res.end()
|
res.end()
|
||||||
@ -155,5 +161,5 @@ const http = createServer(async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
http.listen(port, host, () => {
|
http.listen(port, host, () => {
|
||||||
log('INFO', `Running on ${host}:${port}`)
|
log('SRVX', 'INFO', `Running on ${host}:${port}`)
|
||||||
})
|
})
|
||||||
4
server/utils.mjs
Normal file
4
server/utils.mjs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export function log(topic, level, message) {
|
||||||
|
const date = new Date().toISOString()
|
||||||
|
console.log(`${date} (${level}) [${topic}] ${message}`)
|
||||||
|
}
|
||||||
@ -17,15 +17,18 @@ if (!version) {
|
|||||||
|
|
||||||
export default defineConfig(({ isSsrBuild }) => {
|
export default defineConfig(({ isSsrBuild }) => {
|
||||||
// If we have the Headplane entry we build it as a single
|
// If we have the Headplane entry we build it as a single
|
||||||
// server.mjs file that is built for production server bundle
|
// server/prod.mjs file that is built for production server bundle
|
||||||
// We know the remix invoked command is vite:build
|
// We know the remix invoked command is vite:build
|
||||||
if (!process.argv.includes('vite:build') && !process.argv.includes('vite:dev')) {
|
if (
|
||||||
|
process.env.NODE_ENV !== 'development'
|
||||||
|
&& !process.argv.includes('vite:build')
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
build: {
|
build: {
|
||||||
minify: false,
|
minify: false,
|
||||||
target: 'esnext',
|
target: 'esnext',
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: './server.mjs',
|
input: './server/prod.mjs',
|
||||||
output: {
|
output: {
|
||||||
entryFileNames: 'server.js',
|
entryFileNames: 'server.js',
|
||||||
dir: 'build/headplane',
|
dir: 'build/headplane',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user