initial-commit
This commit is contained in:
commit
cda7cd10fe
|
|
@ -0,0 +1,22 @@
|
||||||
|
FROM node:20-alpine AS development-dependencies-env
|
||||||
|
COPY . /app
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
FROM node:20-alpine AS production-dependencies-env
|
||||||
|
COPY ./package.json package-lock.json /app/
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm ci --omit=dev
|
||||||
|
|
||||||
|
FROM node:20-alpine AS build-env
|
||||||
|
COPY . /app/
|
||||||
|
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM node:20-alpine
|
||||||
|
COPY ./package.json package-lock.json server.js /app/
|
||||||
|
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
|
||||||
|
COPY --from=build-env /app/build /app/build
|
||||||
|
WORKDIR /app
|
||||||
|
CMD ["npm", "run", "start"]
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
# Welcome to React Router!
|
||||||
|
|
||||||
|
A modern, production-ready template for building full-stack React applications using React Router.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🚀 Server-side rendering
|
||||||
|
- ⚡️ Hot Module Replacement (HMR)
|
||||||
|
- 📦 Asset bundling and optimization
|
||||||
|
- 🔄 Data loading and mutations
|
||||||
|
- 🔒 TypeScript by default
|
||||||
|
- 🎉 TailwindCSS for styling
|
||||||
|
- 📖 [React Router docs](https://reactrouter.com/)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Install the dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
Start the development server with HMR:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Your application will be available at `http://localhost:5173`.
|
||||||
|
|
||||||
|
## Building for Production
|
||||||
|
|
||||||
|
Create a production build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Docker Deployment
|
||||||
|
|
||||||
|
To build and run using Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t my-app .
|
||||||
|
|
||||||
|
# Run the container
|
||||||
|
docker run -p 3000:3000 my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
The containerized application can be deployed to any platform that supports Docker, including:
|
||||||
|
|
||||||
|
- AWS ECS
|
||||||
|
- Google Cloud Run
|
||||||
|
- Azure Container Apps
|
||||||
|
- Digital Ocean App Platform
|
||||||
|
- Fly.io
|
||||||
|
- Railway
|
||||||
|
|
||||||
|
### DIY Deployment
|
||||||
|
|
||||||
|
If you're familiar with deploying Node applications, the built-in app server is production-ready.
|
||||||
|
|
||||||
|
Make sure to deploy the output of `npm run build`
|
||||||
|
|
||||||
|
```
|
||||||
|
├── package.json
|
||||||
|
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
|
||||||
|
├── server.js
|
||||||
|
├── build/
|
||||||
|
│ ├── client/ # Static assets
|
||||||
|
│ └── server/ # Server-side code
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Built with ❤️ using React Router.
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
@import "tailwindcss";
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
|
||||||
|
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0!important;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--radius-2xl: calc(var(--radius) + 8px);
|
||||||
|
--radius-3xl: calc(var(--radius) + 12px);
|
||||||
|
--radius-4xl: calc(var(--radius) + 16px);
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--radius: 0.625rem;
|
||||||
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.145 0 0);
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.145 0 0);
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
|
--primary: oklch(0.205 0 0);
|
||||||
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
|
--secondary: oklch(0.97 0 0);
|
||||||
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
|
--muted: oklch(0.97 0 0);
|
||||||
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
|
--accent: oklch(0.97 0 0);
|
||||||
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
|
--border: oklch(0.922 0 0);
|
||||||
|
--input: oklch(0.922 0 0);
|
||||||
|
--ring: oklch(0.708 0 0);
|
||||||
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
|
--sidebar: oklch(0.985 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.145 0 0);
|
||||||
|
--foreground: oklch(0.985 0 0);
|
||||||
|
--card: oklch(0.205 0 0);
|
||||||
|
--card-foreground: oklch(0.985 0 0);
|
||||||
|
--popover: oklch(0.205 0 0);
|
||||||
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
|
--primary: oklch(0.922 0 0);
|
||||||
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
|
--secondary: oklch(0.269 0 0);
|
||||||
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
|
--muted: oklch(0.269 0 0);
|
||||||
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
|
--accent: oklch(0.269 0 0);
|
||||||
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.556 0 0);
|
||||||
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
|
--sidebar: oklch(0.205 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 650 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 649 KiB |
|
|
@ -0,0 +1,21 @@
|
||||||
|
.search-container {
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-label {
|
||||||
|
margin-left: 5px;
|
||||||
|
color: var(--input);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
color: var(--input);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-dark {
|
||||||
|
background-color: #282c34;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
.bg-paper {
|
||||||
|
background-image: url('../images/Hunt_paper_side.png');
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position-x: center;
|
||||||
|
background-attachment: scroll;
|
||||||
|
min-height: 200vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
padding-top: 20%;
|
||||||
|
padding-left: 20%;
|
||||||
|
padding-right: 20%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:300px) {
|
||||||
|
.list-container {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding-top: 25%;
|
||||||
|
padding-left: 25%;
|
||||||
|
padding-right: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import {Links, Meta, Outlet, Scripts} from "react-router";
|
||||||
|
import './app.css';
|
||||||
|
|
||||||
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Hunt Showdown Blacklist</title>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<Meta/>
|
||||||
|
<Links/>
|
||||||
|
<Scripts />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return <Outlet/>;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import {type RouteConfig, index, route} from "@react-router/dev/routes";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
index("routes/home.tsx"),
|
||||||
|
route("paper", "routes/paper.tsx"),
|
||||||
|
] satisfies RouteConfig;
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import {useState} from "react";
|
||||||
|
import '../assets/styles/home.css';
|
||||||
|
import {
|
||||||
|
createTheme,
|
||||||
|
Divider,
|
||||||
|
outlinedInputClasses,
|
||||||
|
TextField,
|
||||||
|
type Theme,
|
||||||
|
ThemeProvider,
|
||||||
|
useTheme
|
||||||
|
} from "@mui/material";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
|
||||||
|
const customTheme = (outerTheme: Theme) =>
|
||||||
|
createTheme({
|
||||||
|
palette: {
|
||||||
|
mode: outerTheme.palette.mode,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MuiTextField: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'--TextField-brandBorderColor': '#E0E3E7',
|
||||||
|
'--TextField-brandBorderHoverColor': '#B2BAC2',
|
||||||
|
'--TextField-brandBorderFocusedColor': '#6F7E8C',
|
||||||
|
'& label': {
|
||||||
|
color: 'var(--TextField-brandBorderColor)',
|
||||||
|
},
|
||||||
|
'& label.Mui-focused': {
|
||||||
|
color: 'var(--TextField-brandBorderColor)',
|
||||||
|
},
|
||||||
|
'& input': {
|
||||||
|
color: 'var(--TextField-brandBorderColor)',
|
||||||
|
borderColor: 'var(--TextField-brandBorderColor)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiOutlinedInput: {
|
||||||
|
styleOverrides: {
|
||||||
|
notchedOutline: {
|
||||||
|
borderColor: 'var(--TextField-brandBorderColor)',
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
[`&:hover .${outlinedInputClasses.notchedOutline}`]: {
|
||||||
|
borderColor: 'var(--TextField-brandBorderHoverColor)',
|
||||||
|
},
|
||||||
|
[`&.Mui-focused .${outlinedInputClasses.notchedOutline}`]: {
|
||||||
|
borderColor: 'var(--TextField-brandBorderFocusedColor)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiDivider: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'--TextField-brandBorderColor': '#E0E3E7',
|
||||||
|
borderColor: 'var(--TextField-brandBorderColor)',
|
||||||
|
borderWidth: 1,
|
||||||
|
width: '95%',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const outerTheme = useTheme();
|
||||||
|
|
||||||
|
function handleSearch(text: string) {
|
||||||
|
setSearchText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={customTheme(outerTheme)}>
|
||||||
|
<div className="bg-dark">
|
||||||
|
<div className="search-container">
|
||||||
|
<TextField
|
||||||
|
id="search-field"
|
||||||
|
label="Search"
|
||||||
|
variant="outlined"
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => handleSearch(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{justifyItems: 'center'}}>
|
||||||
|
<Divider sx={{marginTop: '10px', marginBottom: '10px'}}/>
|
||||||
|
</div>
|
||||||
|
<div className="list-container">
|
||||||
|
<p>{searchText} //</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import '../assets/styles/paper.css';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="bg-paper">
|
||||||
|
<div className="list-container">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "hunt-blacklist-frontend",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "react-router build",
|
||||||
|
"dev": "cross-env NODE_ENV=development node server.js",
|
||||||
|
"start": "node server.js",
|
||||||
|
"typecheck": "react-router typegen && tsc -b"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.1",
|
||||||
|
"@mui/material": "^7.3.7",
|
||||||
|
"@react-router/express": "7.10.1",
|
||||||
|
"@react-router/node": "7.10.1",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"compression": "^1.8.1",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"isbot": "^5.1.31",
|
||||||
|
"lucide-react": "^0.562.0",
|
||||||
|
"morgan": "^1.10.1",
|
||||||
|
"react": "^19.2.3",
|
||||||
|
"react-dom": "^19.2.3",
|
||||||
|
"react-router": "7.10.1",
|
||||||
|
"tailwind-merge": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@react-router/dev": "7.10.1",
|
||||||
|
"@tailwindcss/vite": "^4.1.13",
|
||||||
|
"@types/compression": "^1.8.1",
|
||||||
|
"@types/express": "^5.0.3",
|
||||||
|
"@types/express-serve-static-core": "^5.0.7",
|
||||||
|
"@types/morgan": "^1.9.10",
|
||||||
|
"@types/node": "^22",
|
||||||
|
"@types/react": "^19.2.7",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"cross-env": "^10.0.0",
|
||||||
|
"tailwindcss": "^4.1.13",
|
||||||
|
"tw-animate-css": "^1.4.0",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
"vite": "^7.1.7",
|
||||||
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
|
|
@ -0,0 +1,7 @@
|
||||||
|
import type { Config } from "@react-router/dev/config";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// Config options...
|
||||||
|
// Server-side render by default, to enable SPA mode set this to `false`
|
||||||
|
ssr: true,
|
||||||
|
} satisfies Config;
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import compression from "compression";
|
||||||
|
import express from "express";
|
||||||
|
import morgan from "morgan";
|
||||||
|
|
||||||
|
// Short-circuit the type-checking of the built output.
|
||||||
|
const BUILD_PATH = "./build/server/index.js";
|
||||||
|
const DEVELOPMENT = process.env.NODE_ENV === "development";
|
||||||
|
const PORT = Number.parseInt(process.env.PORT || "3000");
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(compression());
|
||||||
|
app.disable("x-powered-by");
|
||||||
|
|
||||||
|
if (DEVELOPMENT) {
|
||||||
|
console.log("Starting development server");
|
||||||
|
const viteDevServer = await import("vite").then((vite) =>
|
||||||
|
vite.createServer({
|
||||||
|
server: { middlewareMode: true },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
app.use(viteDevServer.middlewares);
|
||||||
|
app.use(async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const source = await viteDevServer.ssrLoadModule("./server/app.ts");
|
||||||
|
return await source.app(req, res, next);
|
||||||
|
} catch (error) {
|
||||||
|
if (typeof error === "object" && error instanceof Error) {
|
||||||
|
viteDevServer.ssrFixStacktrace(error);
|
||||||
|
}
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("Starting production server");
|
||||||
|
app.use(
|
||||||
|
"/assets",
|
||||||
|
express.static("build/client/assets", { immutable: true, maxAge: "1y" }),
|
||||||
|
);
|
||||||
|
app.use(morgan("tiny"));
|
||||||
|
app.use(express.static("build/client", { maxAge: "1h" }));
|
||||||
|
app.use(await import(BUILD_PATH).then((mod) => mod.app));
|
||||||
|
}
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on http://localhost:${PORT}`);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import "react-router";
|
||||||
|
import { createRequestHandler } from "@react-router/express";
|
||||||
|
import express from "express";
|
||||||
|
|
||||||
|
declare module "react-router" {
|
||||||
|
interface AppLoadContext {
|
||||||
|
VALUE_FROM_EXPRESS: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const app = express();
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
createRequestHandler({
|
||||||
|
build: () => import("virtual:react-router/server-build"),
|
||||||
|
getLoadContext() {
|
||||||
|
return {
|
||||||
|
VALUE_FROM_EXPRESS: "Hello from Express",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.node.json" },
|
||||||
|
{ "path": "./tsconfig.vite.json" }
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["server.js", "vite.config.ts"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"strict": true,
|
||||||
|
"types": ["node"],
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
".react-router/types/**/*",
|
||||||
|
"app/**/*",
|
||||||
|
"app/**/.server/**/*",
|
||||||
|
"app/**/.client/**/*",
|
||||||
|
"server/**/*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"strict": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"rootDirs": [".", "./.react-router/types"],
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./app/*"],
|
||||||
|
"@/*": ["./app/*"]
|
||||||
|
},
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { reactRouter } from "@react-router/dev/vite";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig(({ isSsrBuild }) => ({
|
||||||
|
build: {
|
||||||
|
rollupOptions: isSsrBuild
|
||||||
|
? {
|
||||||
|
input: "./server/app.ts",
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./app"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
Loading…
Reference in New Issue