silphius/server.js
2024-11-04 12:39:06 -06:00

130 lines
3.6 KiB
JavaScript

import {createRequestHandler} from '@remix-run/express';
import express from 'express';
import morgan from 'morgan';
import sylvite from 'sylvite';
import environment from './environment.server.js';
const cacheDirectory = `${import.meta.dirname}/node_modules/.cache`;
const {hooks} = await sylvite({
entry: 'node',
...await import('./sylvite.config.js'),
});
const {
httpsCert,
httpsKey,
isInsecure,
isProduction,
port,
useMkcert,
} = environment();
const app = express();
hooks.call('@/sylvite/cha0s-remix:initializeExpress', app);
const httpServerOptions = hooks.call('@/sylvite/cha0s-remix:httpServerOptions', {});
let server;
if (isInsecure) {
// http://
const {createServer} = await import('node:http');
server = createServer(httpServerOptions, app);
}
else {
const {readFile} = await import('node:fs/promises');
// generate certificates
if (useMkcert) {
const {execSync} = await import('node:child_process');
const {mkdirSync, statSync} = await import('node:fs');
mkdirSync(cacheDirectory, {recursive: true});
try {
statSync(`${cacheDirectory}/localhost-key.pem`);
}
catch (error) { // eslint-disable-line no-unused-vars
execSync(`mkcert -cert-file ${cacheDirectory}/localhost.pem -key-file ${cacheDirectory}/localhost-key.pem localhost`)
}
}
// https://
const [cert, key] = await Promise.all([readFile(httpsCert), readFile(httpsKey)]);
const serverOptions = {
cert,
key,
...httpServerOptions,
};
const {createServer} = await import('node:https');
server = createServer(serverOptions, app);
}
// immediately start listening and queueing up connections
let resolve, promise = new Promise((res) => {
resolve = res;
});
app.use(async (req, res, next) => {
await promise;
next();
});
server.listen(port, () => {
console.log(`Express server listening at http${isInsecure ? '' : 's'}://localhost:${port}`);
});
const requestHandlerOptions = {
getLoadContext: (req) => {
return hooks.call('@/sylvite/cha0s-remix:getRemixLoadContext', {}, req);
},
};
if (isProduction) {
// remix handler
requestHandlerOptions.build = () => import('./build/server/index.js');
// websocket handler
const {entry: {module: {handleUpgrade}}} = await requestHandlerOptions.build();
server.on('upgrade', handleUpgrade);
// serve assets
app.use('/assets', express.static('build/client/assets', {
// vite fingerprints its assets so we can cache forever.
immutable: true,
maxAge: '1y',
}));
app.use(express.static('build/client', {
// everything else (like favicon.ico) is cached for an hour.
maxAge: '1h',
}));
}
else {
const viteDevServer = await import('vite').then((vite) => (
vite.createServer({
server: {
middlewareMode: true,
},
})
));
// remix handler
requestHandlerOptions.build = () => viteDevServer.ssrLoadModule('virtual:remix/server-build');
const {createViteRuntime} = await import('vite');
// websocket handler
const runtime = await createViteRuntime(viteDevServer);
const {handleUpgrade} = await runtime.executeEntrypoint('/app/websocket.js');
server.on('upgrade', handleUpgrade);
// serve assets
app.use(viteDevServer.middlewares);
app.get('/storybook', async (req, res) => {
const url = new URL(req.url, `http${isInsecure ? '' : 's'}://${req.headers.host}`);
url.pathname = '/';
url.port = 11337;
res.redirect(url.href);
});
}
hooks.call('@/sylvite/cha0s-remix:additionalAssets', app);
app.use(morgan('tiny'));
hooks.call('@/sylvite/cha0s-remix:beforeExpressCatchAll', app);
// handle SSR requests
app.all('*', createRequestHandler(requestHandlerOptions));
// finalize; let requests resolve
resolve();