Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a62e3c4
In dev, use `tsx` instead of building server, to allow watching for c…
uday-rana Feb 20, 2025
68ee8a0
Remove `serverModuleFormat`
uday-rana Feb 20, 2025
9d7b43e
Re-add `serverModuleFormat`
uday-rana Feb 20, 2025
d68f348
Install Vite
uday-rana Feb 20, 2025
6c7c594
Replace `remix.config.js` with `vite.config.ts`
uday-rana Feb 20, 2025
b3ce9cc
Remove `<LiveReload />`
uday-rana Feb 20, 2025
ad596da
TypeScript integration
uday-rana Feb 20, 2025
f34879e
Migrate custom server
uday-rana Feb 20, 2025
5f4afb7
Update references to build output paths
uday-rana Feb 20, 2025
98c8379
Remove unused import `path`
uday-rana Feb 20, 2025
a8df7c4
Upgrade vitest to 3.0.6
uday-rana Feb 20, 2025
fb5b9b5
Switch to ESM
uday-rana Apr 3, 2025
0035f58
Disable "no unresolved imports" rule when importing build/server/inde…
uday-rana Apr 3, 2025
c9dd561
Remove serverModuleFormat: 'cjs'
uday-rana Apr 3, 2025
a3a4c77
add react-use to cjsinterop and optimizedeps
uday-rana Apr 3, 2025
b222fbd
Tell tsc to ignore importing build/server/index.js
uday-rana Apr 3, 2025
8af6850
Revert "add react-use to cjsinterop and optimizedeps"
uday-rana Apr 3, 2025
2ddfb0e
Add react-use to optimizeDeps
uday-rana Apr 3, 2025
7f9924c
add ssr: noExternal: react-use
uday-rana Apr 3, 2025
664b7f8
fix eslint ignore rule
uday-rana Apr 3, 2025
6c4a270
use esbuild, switch back to cjs
uday-rana Apr 7, 2025
8f73ff6
move build step into vite config buildEnd
uday-rana Apr 7, 2025
6490a5f
Add type: module
uday-rana Apr 7, 2025
f01abfc
remove SECRETS_OVERRIDE=1 from start script
uday-rana Apr 7, 2025
295b7b7
Unwrap IIFE
uday-rana Apr 7, 2025
81f70e8
Fix lint
uday-rana Apr 7, 2025
86f789c
Set sourceType: 'module' for eslint.config.mjs
uday-rana Apr 7, 2025
6107d94
Add @ts-expect-error
uday-rana Apr 7, 2025
dac1d87
Switch from @ts-expect-error to @ts-ignore
uday-rana Apr 7, 2025
5d78490
use remix dev -c in dev script
uday-rana Apr 7, 2025
9c7ee54
Remove "--require dotenv/config" from start script
uday-rana Apr 7, 2025
72a8c32
Reorder npm scripts
uday-rana Apr 7, 2025
63b04ff
Switch to remix-express-vite-plugin
uday-rana Apr 8, 2025
117e544
Remove async/await from createServer
uday-rana Apr 8, 2025
6c90c3a
await services before starting server
uday-rana Apr 8, 2025
631fad9
.
uday-rana Apr 8, 2025
b6378de
Add debug logs
uday-rana Apr 8, 2025
a6702bd
Use logger
uday-rana Apr 8, 2025
2fdd325
Remove createServer
uday-rana Apr 8, 2025
bcdc362
Await createExpressApp, add createServer
uday-rana Apr 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ docker/volumes/
docs/
node_modules/
playwright-report/
public/build/
test-results/
test/
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.cache
.env
build/
coverage/
docker/volumes/
node_modules/
playwright-report/
playwright/.cache/
public/build/
test-results/
test/e2e/.auth
1 change: 0 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"docs/**",
"node_modules/**",
"playwright-report/**",
"public/build/**",
"test/e2e/**",
"test-results/**",
"playwright.config.ts"
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ USER node
COPY --chown=node:node --from=production-deps /app/.npmrc ./.npmrc
COPY --chown=node:node --from=production-deps /app/node_modules ./node_modules
COPY --chown=node:node --from=build /app/node_modules/.prisma ./node_modules/.prisma
COPY --chown=node:node --from=build /app/build ./build
COPY --chown=node:node --from=build /app/public ./public
COPY --chown=node:node --from=build /app/build/server ./build/server
COPY --chown=node:node --from=build /app/build/client ./build/client
COPY --chown=node:node --from=build /app/prisma ./prisma

# Include the SAML IDP metadata in the image. Specify the file to use in the build arg
Expand All @@ -67,7 +67,7 @@ ENV SAML_IDP_METADATA_PATH=/app/config/idp-metadata.xml

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["/usr/bin/tini", "--", "docker-entrypoint.sh"]
CMD ["node", "./build/server.js"]
CMD ["node", "./build/server/index.js"]

HEALTHCHECK CMD curl --fail http://localhost:${PORT}/healthcheck || exit 1
EXPOSE ${PORT}
73 changes: 73 additions & 0 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import { renderToPipeableStream } from 'react-dom/server';
import createEmotionServer from '@emotion/server/create-instance';
import { CacheProvider } from '@emotion/react';
import createEmotionCache from './createEmotionCache';
import { createExpressApp } from 'remix-create-express-app';
import * as crypto from 'crypto';
import helmet from 'helmet';
import cors from 'cors';
import compression from 'compression';
import express from 'express';
import * as services from 'services';

import type { Application, Request as ExpressRequest, Response as ExpressResponse } from 'express';

const ABORT_DELAY = 5000;

Expand Down Expand Up @@ -62,3 +71,67 @@ export default function handleRequest(
setTimeout(abort, ABORT_DELAY);
});
}

export const app = await createExpressApp({
configure: (app: Application) => {
// setup additional express middleware here
const MODE = process.env.NODE_ENV;

app.use(compression());
app.use(cors());
app.use(
helmet({
contentSecurityPolicy: {
useDefaults: true,
directives: {
// Expect a nonce on scripts
scriptSrc: [
"'self'",
(_req, res) => `'nonce-${(res as ExpressResponse).locals.nonce}'`,
],
// Allow live reload to work over a web socket in development
connectSrc: MODE === 'production' ? ["'self'"] : ["'self'", 'ws:'],
// Don't force https unless in production
upgradeInsecureRequests: MODE === 'production' ? [] : null,
},
},
})
);

// Remix fingerprints its assets so we can cache forever.
app.use('/build', express.static('build/client', { immutable: true, maxAge: '1y' }));

// Everything else (like favicon.ico) is cached for an hour. You may want to be
// more aggressive with this caching.
app.use(express.static('public', { maxAge: '1h' }));

app.use((_req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('hex');
next();
});
app.use((req, res, next) => {
// /clean-urls/ -> /clean-urls
if (req.path.endsWith('/') && req.path.length > 1) {
const query = req.url.slice(req.path.length);
const safepath = req.path.slice(0, -1).replace(/\/+/g, '/');
res.redirect(301, safepath + query);
return;
}
next();
});
},
// Pass the nonce we're setting in the CSP headers down to the Remix Loader/Action functions
getLoadContext: (_req: ExpressRequest, res: ExpressResponse) => ({ nonce: res.locals.nonce }),
createServer: (app) => {
const PORT = process.env.PORT || 8080;

const server = app.listen(PORT, () => {
// start the various background jobs we run (reconciler, expire records, etc)
services.init().then(() => {
logger.info(`✅ app ready: http://localhost:${PORT}`);
});
});

return server;
},
});
11 changes: 1 addition & 10 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { ChakraProvider } from '@chakra-ui/react';
import { json } from '@remix-run/node';
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useLoaderData,
} from '@remix-run/react';
import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from '@remix-run/react';

import { getUser, getEffectiveUser, stopImpersonation } from './session.server';

Expand Down Expand Up @@ -70,7 +62,6 @@ function Document({ children }: { children: React.ReactNode }) {
{children}
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
<LiveReload nonce={nonce} />
</body>
</html>
);
Expand Down
36 changes: 18 additions & 18 deletions eslint.config.cjs → eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
const path = require('node:path');
import path from 'node:path';
import js from '@eslint/js';
import { includeIgnoreFile } from '@eslint/compat';

const js = require('@eslint/js');
const { includeIgnoreFile } = require('@eslint/compat');
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';

const typescriptEslint = require('@typescript-eslint/eslint-plugin');
const typescriptParser = require('@typescript-eslint/parser');
import importPlugin from 'eslint-plugin-import';
import reactPlugin from 'eslint-plugin-react';
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import oxlint from 'eslint-plugin-oxlint';

const importPlugin = require('eslint-plugin-import');
const reactPlugin = require('eslint-plugin-react');
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const reactHooksPlugin = require('eslint-plugin-react-hooks');
const oxlint = require('eslint-plugin-oxlint');
import globals from 'globals';

const globals = require('globals');

const gitignorePath = path.resolve(__dirname, '.gitignore');
const gitignorePath = path.resolve(import.meta.dirname, '.gitignore');

/** @type {import('eslint').Linter.Config[]} */
module.exports = [
export default [
js.configs.recommended,
importPlugin.flatConfigs.recommended,
includeIgnoreFile(gitignorePath),
Expand All @@ -33,7 +32,7 @@ module.exports = [
],
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
files: ['**/*.{js,jsx,ts,tsx,mjs}'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
Expand Down Expand Up @@ -91,7 +90,7 @@ module.exports = [
languageOptions: {
parser: typescriptParser,
parserOptions: {
tsconfigRootDir: __dirname,
tsconfigRootDir: import.meta.dirname,
project: ['./tsconfig.json'],
},
globals: {
Expand All @@ -112,10 +111,11 @@ module.exports = [
'no-undef': 'off',
},
},
// Node environment for eslint.config.cjs
// Node environment for eslint.config.mjs
{
files: ['eslint.config.cjs'],
files: ['eslint.config.mjs'],
languageOptions: {
sourceType: 'module',
globals: {
...globals.node,
},
Expand Down
Loading
Loading