Improve dev server performance on startup and navigation (#3233)

This commit is contained in:
hfcRed 2026-07-18 03:30:08 -04:00 committed by GitHub
parent e1bf629223
commit 4914912e59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 368 additions and 77 deletions

View File

@ -55,7 +55,7 @@ Another key objective is to bridge the gap between casual and competitive player
### Prerequisites
- [Git](https://git-scm.com/)
- [Node.js v22](https://nodejs.org/en)
- [Node.js v24](https://nodejs.org/en) (see [.nvmrc](./.nvmrc) for the exact version)
- [pnpm](https://pnpm.io/installation)
Optionally [nvm](https://github.com/nvm-sh/nvm) can be convenient for managing multiple Node.js installs
@ -73,7 +73,7 @@ pnpm --version
You should see something like:
```
v22.13.0
v24.18.0
git version 2.39.5 (Apple Git-154)
10.33.0
```
@ -114,6 +114,26 @@ docker compose up -d
Minio admin UI to manage uploaded photos should be up and running at http://localhost:9001
#### Windows performance tips
The dev server does many small file operations which are slower on Windows by default. Two optional tweaks can improve performance:
**Windows Defender exclusions**
Excluding the project folder and the pnpm store from Windows Defender speeds up installs, dev server startup and first navigations. In a PowerShell ran as administrator:
```powershell
Add-MpPreference -ExclusionPath "C:\path\to\sendou.ink"
Add-MpPreference -ExclusionPath "$(pnpm store path)"
```
Be aware that excluded folders are not scanned at all, and compromised npm packages can land in `node_modules`. This should not be much of an issue since pnpm does not allow running dependency lifecycle scripts unless explicitly allowed, and exact dependency versions are being pinned by the lockfile, but it should be kept in mind.
Undo at any time with `Remove-MpPreference -ExclusionPath "..."`.
**Dev Drive**
Alternatively, keep the repository on a [Dev Drive](https://learn.microsoft.com/en-us/windows/dev-drive/). On a Dev Drive, Defender scans files asynchronously instead of blocking, so you keep most of the speedup without giving up scanning entirely.
## Contributing
- **Developers**: Read [CONTRIBUTING.md](./CONTRIBUTING.md)

View File

@ -9,9 +9,9 @@ import type { z } from "zod";
import { FormMessage } from "~/components/FormMessage";
import { SubmitButton } from "~/components/SubmitButton";
import { FormField as FormFieldComponent } from "./FormField";
import { formRegistry } from "./fields";
import { getFormFieldMetadata } from "./fields";
import styles from "./SendouForm.module.css";
import type { FormField, TypedFormFieldComponent } from "./types";
import type { TypedFormFieldComponent } from "./types";
import {
buildFieldPath,
errorMessageId,
@ -625,9 +625,7 @@ function buildInitialValues<T extends z.ZodRawShape>(
const result: Record<string, unknown> = {};
for (const [key, fieldSchema] of Object.entries(schema.shape)) {
const formField = formRegistry.get(fieldSchema as z.ZodType) as
| FormField
| undefined;
const formField = getFormFieldMetadata(fieldSchema as z.ZodType);
const defaultValue = defaultValues?.[key as keyof typeof defaultValues];
if (defaultValue !== undefined) {

View File

@ -36,6 +36,18 @@ import type {
export const formRegistry = z.registry<FormField>();
/**
* Looks up a schemas form field metadata. Needed to bypass the
* registrys deep generic `get` signature which causes
* "Type instantiation is excessively deep" errors.
*/
export function getFormFieldMetadata(schema: z.ZodType): FormField | undefined {
const registry = formRegistry as {
get(schema: z.ZodType): FormField | undefined;
};
return registry.get(schema);
}
export type RequiresDefault<T extends z.ZodType> = T & {
_requiresDefault: true;
};

View File

@ -1,5 +1,5 @@
import type { z } from "zod";
import { formRegistry } from "./fields";
import { getFormFieldMetadata } from "./fields";
import type { FormField } from "./types";
function infoMessageId(fieldId: string) {
@ -94,12 +94,6 @@ export function setNestedValue(
};
}
// Casting away the registry's deep generic signature avoids "Type instantiation
// is excessively deep" errors when looking up field metadata by schema.
const typedRegistry = formRegistry as {
get(schema: z.ZodType): FormField | undefined;
};
/**
* The default value object for a `fieldset` field, built from each sub-field's
* own `initialValue` (e.g. a `select`'s first option). Returns `{}` for
@ -113,7 +107,7 @@ export function fieldsetDefaults(
const shape = fieldsetMeta.fields.shape as Record<string, z.ZodType>;
const result: Record<string, unknown> = {};
for (const [key, fieldSchema] of Object.entries(shape)) {
const fieldMeta = typedRegistry.get(fieldSchema);
const fieldMeta = getFormFieldMetadata(fieldSchema);
if (fieldMeta) result[key] = fieldMeta.initialValue;
}
return result;
@ -142,7 +136,7 @@ export function seedArrayItemDefaults(
const itemSchema = getNestedSchema(schema, itemPath);
if (!itemSchema) return values;
const itemMeta = typedRegistry.get(itemSchema);
const itemMeta = getFormFieldMetadata(itemSchema);
if (itemMeta?.type !== "fieldset") return values;
const existing = getNestedValue(values, itemPath) as
@ -228,7 +222,7 @@ export function validateField(
// array) belongs to that child — attributing it to the parent would surface
// the wrong message at the wrong field. Other composite fields (e.g. a custom
// tuple) render as a single control, so their nested issues belong to them.
const fieldMeta = typedRegistry.get(fieldSchema);
const fieldMeta = getFormFieldMetadata(fieldSchema);
const childrenRenderOwnErrors =
fieldMeta?.type === "array" || fieldMeta?.type === "fieldset";
const issue = childrenRenderOwnErrors

View File

@ -3,7 +3,7 @@ import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { expect, type Page } from "@playwright/test";
import type { z } from "zod";
import { formRegistry } from "~/form/fields";
import { getFormFieldMetadata } from "~/form/fields";
import type { FormField } from "~/form/types";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@ -81,7 +81,7 @@ export function createFormHelpers<T extends z.ZodRawShape>(
const getFieldMetadata = (name: string): FormField | undefined => {
const fieldSchema = schema.shape[name];
if (!fieldSchema) return undefined;
return formRegistry.get(fieldSchema) as FormField | undefined;
return getFormFieldMetadata(fieldSchema as z.ZodType);
};
const getLabel = (name: string): string => {

View File

@ -9,6 +9,7 @@
"deploy": "pnpm install --frozen-lockfile && pnpm run build",
"build": "react-router build",
"dev": "cross-env DB_PATH=db.sqlite3 pnpm run migrate up && pnpm run setup && react-router dev --host",
"dev:fast": "react-router dev --host",
"dev:sentry": "NODE_OPTIONS='--import ./instrument.server.mjs' pnpm dev",
"dev:prod": "cross-env DB_PATH=db-prod.sqlite3 VITE_PROD_MODE=true react-router dev --host",
"start": "pnpm run migrate up && NODE_ENV=production NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js",
@ -118,7 +119,7 @@
"knip": "6.25.0",
"ley": "0.8.1",
"sql-formatter": "15.8.2",
"typescript": "6.0.3",
"typescript": "7.0.2",
"vite": "8.1.3",
"vite-node": "6.0.0",
"vite-plugin-babel": "1.7.3",

View File

@ -47,16 +47,16 @@ importers:
version: 3.12.2
'@react-router/node':
specifier: 8.1.0
version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
'@react-router/serve':
specifier: 8.1.0
version: 8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
version: 8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
'@remix-run/form-data-parser':
specifier: 0.17.4
version: 0.17.4
'@sentry/react-router':
specifier: 10.64.0
version: 10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
version: 10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
'@tldraw/tldraw':
specifier: 3.12.1
version: 3.12.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
@ -86,7 +86,7 @@ importers:
version: 4.0.3
i18next:
specifier: 26.3.4
version: 26.3.4(typescript@6.0.3)
version: 26.3.4(typescript@7.0.2)
i18next-browser-languagedetector:
specifier: 8.2.1
version: 8.2.1
@ -155,7 +155,7 @@ importers:
version: 7.2.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-i18next:
specifier: 17.0.8
version: 17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)
version: 17.0.8(i18next@26.3.4(typescript@7.0.2))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2)
react-router:
specifier: 8.1.0
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
@ -173,7 +173,7 @@ importers:
version: 3.4.1(remix-auth@4.2.0)
remix-i18next:
specifier: 8.0.0
version: 8.0.0(i18next@26.3.4(typescript@6.0.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
version: 8.0.0(i18next@26.3.4(typescript@7.0.2))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
slugify:
specifier: 1.6.9
version: 1.6.9
@ -201,7 +201,7 @@ importers:
version: 1.61.1
'@react-router/dev':
specifier: 8.1.0
version: 8.1.0(@react-router/serve@8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)(vite@8.1.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0))
version: 8.1.0(@react-router/serve@8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)(vite@8.1.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0))
'@types/better-sqlite3':
specifier: 7.6.13
version: 7.6.13
@ -245,8 +245,8 @@ importers:
specifier: 15.8.2
version: 15.8.2
typescript:
specifier: 6.0.3
version: 6.0.3
specifier: 7.0.2
version: 7.0.2
vite:
specifier: 8.1.3
version: 8.1.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0)
@ -2676,6 +2676,126 @@ packages:
'@types/web-push@3.6.4':
resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==}
'@typescript/typescript-aix-ppc64@7.0.2':
resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==}
engines: {node: '>=16.20.0'}
cpu: [ppc64]
os: [aix]
'@typescript/typescript-darwin-arm64@7.0.2':
resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [darwin]
'@typescript/typescript-darwin-x64@7.0.2':
resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [darwin]
'@typescript/typescript-freebsd-arm64@7.0.2':
resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [freebsd]
'@typescript/typescript-freebsd-x64@7.0.2':
resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [freebsd]
'@typescript/typescript-linux-arm64@7.0.2':
resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [linux]
'@typescript/typescript-linux-arm@7.0.2':
resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==}
engines: {node: '>=16.20.0'}
cpu: [arm]
os: [linux]
'@typescript/typescript-linux-loong64@7.0.2':
resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==}
engines: {node: '>=16.20.0'}
cpu: [loong64]
os: [linux]
'@typescript/typescript-linux-mips64el@7.0.2':
resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==}
engines: {node: '>=16.20.0'}
cpu: [mips64el]
os: [linux]
'@typescript/typescript-linux-ppc64@7.0.2':
resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==}
engines: {node: '>=16.20.0'}
cpu: [ppc64]
os: [linux]
'@typescript/typescript-linux-riscv64@7.0.2':
resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==}
engines: {node: '>=16.20.0'}
cpu: [riscv64]
os: [linux]
'@typescript/typescript-linux-s390x@7.0.2':
resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==}
engines: {node: '>=16.20.0'}
cpu: [s390x]
os: [linux]
'@typescript/typescript-linux-x64@7.0.2':
resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [linux]
'@typescript/typescript-netbsd-arm64@7.0.2':
resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [netbsd]
'@typescript/typescript-netbsd-x64@7.0.2':
resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [netbsd]
'@typescript/typescript-openbsd-arm64@7.0.2':
resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [openbsd]
'@typescript/typescript-openbsd-x64@7.0.2':
resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [openbsd]
'@typescript/typescript-sunos-x64@7.0.2':
resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [sunos]
'@typescript/typescript-win32-arm64@7.0.2':
resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [win32]
'@typescript/typescript-win32-x64@7.0.2':
resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [win32]
'@use-gesture/core@10.3.1':
resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==}
@ -4370,9 +4490,9 @@ packages:
types-ramda@0.31.0:
resolution: {integrity: sha512-vaoC35CRC3xvL8Z6HkshDbi6KWM1ezK0LHN0YyxXWUn9HKzBNg/T3xSGlJZjCYspnOD3jE7bcizsp0bUXZDxnQ==}
typescript@6.0.3:
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'}
typescript@7.0.2:
resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==}
engines: {node: '>=16.20.0'}
hasBin: true
uc.micro@2.1.0:
@ -5801,7 +5921,7 @@ snapshots:
'@radix-ui/rect@1.1.1': {}
'@react-router/dev@8.1.0(@react-router/serve@8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)(vite@8.1.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0))':
'@react-router/dev@8.1.0(@react-router/serve@8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)(vite@8.1.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0))':
dependencies:
'@babel/core': 7.29.7
'@babel/generator': 7.29.7
@ -5810,7 +5930,7 @@ snapshots:
'@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
'@babel/traverse': 7.29.7
'@babel/types': 7.29.7
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
'@remix-run/node-fetch-server': 0.13.3
babel-dead-code-elimination: 1.0.12
chokidar: 5.0.0
@ -5829,34 +5949,34 @@ snapshots:
react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
semver: 7.8.4
tinyglobby: 0.2.17
valibot: 1.4.1(typescript@6.0.3)
valibot: 1.4.1(typescript@7.0.2)
vite: 8.1.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0)
optionalDependencies:
'@react-router/serve': 8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
typescript: 6.0.3
'@react-router/serve': 8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
typescript: 7.0.2
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
'@react-router/express@8.1.0(express@5.2.1)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)':
'@react-router/express@8.1.0(express@5.2.1)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)':
dependencies:
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
express: 5.2.1
react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
optionalDependencies:
typescript: 6.0.3
typescript: 7.0.2
'@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)':
'@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)':
dependencies:
'@remix-run/node-fetch-server': 0.13.3
react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
optionalDependencies:
typescript: 6.0.3
typescript: 7.0.2
'@react-router/serve@8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)':
'@react-router/serve@8.1.0(patch_hash=4d79b209948d4237f1ca3a5cca417c6d11519d8b1ab7084c75c46a39e3ac27d6)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)':
dependencies:
'@react-router/express': 8.1.0(express@5.2.1)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
'@react-router/express': 8.1.0(express@5.2.1)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
'@remix-run/node-fetch-server': 0.13.3
compression: 1.8.1
express: 5.2.1
@ -6058,11 +6178,11 @@ snapshots:
'@sentry/conventions': 0.15.1
'@sentry/core': 10.64.0
'@sentry/react-router@10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)':
'@sentry/react-router@10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1)
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)
'@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@7.0.2)
'@sentry/browser': 10.64.0
'@sentry/cli': 2.58.6
'@sentry/conventions': 0.15.1
@ -7294,6 +7414,66 @@ snapshots:
dependencies:
'@types/node': 26.1.1
'@typescript/typescript-aix-ppc64@7.0.2':
optional: true
'@typescript/typescript-darwin-arm64@7.0.2':
optional: true
'@typescript/typescript-darwin-x64@7.0.2':
optional: true
'@typescript/typescript-freebsd-arm64@7.0.2':
optional: true
'@typescript/typescript-freebsd-x64@7.0.2':
optional: true
'@typescript/typescript-linux-arm64@7.0.2':
optional: true
'@typescript/typescript-linux-arm@7.0.2':
optional: true
'@typescript/typescript-linux-loong64@7.0.2':
optional: true
'@typescript/typescript-linux-mips64el@7.0.2':
optional: true
'@typescript/typescript-linux-ppc64@7.0.2':
optional: true
'@typescript/typescript-linux-riscv64@7.0.2':
optional: true
'@typescript/typescript-linux-s390x@7.0.2':
optional: true
'@typescript/typescript-linux-x64@7.0.2':
optional: true
'@typescript/typescript-netbsd-arm64@7.0.2':
optional: true
'@typescript/typescript-netbsd-x64@7.0.2':
optional: true
'@typescript/typescript-openbsd-arm64@7.0.2':
optional: true
'@typescript/typescript-openbsd-x64@7.0.2':
optional: true
'@typescript/typescript-sunos-x64@7.0.2':
optional: true
'@typescript/typescript-win32-arm64@7.0.2':
optional: true
'@typescript/typescript-win32-x64@7.0.2':
optional: true
'@use-gesture/core@10.3.1': {}
'@use-gesture/react@10.3.1(react@19.2.7)':
@ -7930,9 +8110,9 @@ snapshots:
picomatch: 4.0.4
yargs: 17.7.2
i18next@26.3.4(typescript@6.0.3):
i18next@26.3.4(typescript@7.0.2):
optionalDependencies:
typescript: 6.0.3
typescript: 7.0.2
iconv-lite@0.7.2:
dependencies:
@ -8604,16 +8784,16 @@ snapshots:
react: 19.2.7
react-dom: 19.2.7(react@19.2.7)
react-i18next@17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3):
react-i18next@17.0.8(i18next@26.3.4(typescript@7.0.2))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2):
dependencies:
'@babel/runtime': 7.29.2
html-parse-stringify: 3.0.1
i18next: 26.3.4(typescript@6.0.3)
i18next: 26.3.4(typescript@7.0.2)
react: 19.2.7
use-sync-external-store: 1.6.0(react@19.2.7)
optionalDependencies:
react-dom: 19.2.7(react@19.2.7)
typescript: 6.0.3
typescript: 7.0.2
react-is@16.13.1: {}
@ -8690,9 +8870,9 @@ snapshots:
remix-auth@4.2.0: {}
remix-i18next@8.0.0(i18next@26.3.4(typescript@6.0.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
remix-i18next@8.0.0(i18next@26.3.4(typescript@7.0.2))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
dependencies:
i18next: 26.3.4(typescript@6.0.3)
i18next: 26.3.4(typescript@7.0.2)
react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
require-directory@2.1.1: {}
@ -9021,7 +9201,28 @@ snapshots:
dependencies:
ts-toolbelt: 9.6.0
typescript@6.0.3: {}
typescript@7.0.2:
optionalDependencies:
'@typescript/typescript-aix-ppc64': 7.0.2
'@typescript/typescript-darwin-arm64': 7.0.2
'@typescript/typescript-darwin-x64': 7.0.2
'@typescript/typescript-freebsd-arm64': 7.0.2
'@typescript/typescript-freebsd-x64': 7.0.2
'@typescript/typescript-linux-arm': 7.0.2
'@typescript/typescript-linux-arm64': 7.0.2
'@typescript/typescript-linux-loong64': 7.0.2
'@typescript/typescript-linux-mips64el': 7.0.2
'@typescript/typescript-linux-ppc64': 7.0.2
'@typescript/typescript-linux-riscv64': 7.0.2
'@typescript/typescript-linux-s390x': 7.0.2
'@typescript/typescript-linux-x64': 7.0.2
'@typescript/typescript-netbsd-arm64': 7.0.2
'@typescript/typescript-netbsd-x64': 7.0.2
'@typescript/typescript-openbsd-arm64': 7.0.2
'@typescript/typescript-openbsd-x64': 7.0.2
'@typescript/typescript-sunos-x64': 7.0.2
'@typescript/typescript-win32-arm64': 7.0.2
'@typescript/typescript-win32-x64': 7.0.2
uc.micro@2.1.0: {}
@ -9060,9 +9261,9 @@ snapshots:
util-deprecate@1.0.2: {}
valibot@1.4.1(typescript@6.0.3):
valibot@1.4.1(typescript@7.0.2):
optionalDependencies:
typescript: 6.0.3
typescript: 7.0.2
vary@1.1.2: {}

View File

@ -1,4 +1,3 @@
import { seed } from "~/db/seed";
import { db } from "~/db/sql";
import { logger } from "~/utils/logger";
import { seedImages } from "./seed-images";
@ -10,6 +9,9 @@ async function main() {
if (dbEmpty) {
logger.info("🌱 Seeding database...");
try {
// Dynamically imported so we skip transforming the large
// seed import graph if the database is already seeded
const { seed } = await import("~/db/seed");
await seed();
logger.info("Database seeded successfully");
} catch (err) {

View File

@ -5,9 +5,14 @@ import babel from "vite-plugin-babel";
export default defineConfig((config) => {
const env = loadEnv(config.mode, process.cwd(), "");
const isBuild = config.command === "build";
return {
server: {
port: Number(env.PORT) || 5173,
warmup: {
clientFiles: ["./app/entry.client.tsx", "./app/root.tsx"],
ssrFiles: ["./app/entry.server.tsx"],
},
},
ssr: {
noExternal: ["react-charts", "react-use"],
@ -33,25 +38,31 @@ export default defineConfig((config) => {
},
},
reactRouter(),
babel({
include: /\.[jt]sx?$/,
babelConfig: {
presets: ["@babel/preset-typescript"],
plugins: [["babel-plugin-react-compiler", {}]],
},
}),
sentryReactRouter(
{
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
telemetry: false,
unstable_sentryVitePluginOptions: {
applicationKey: "sendou-ink",
},
},
config,
),
// React Compiler and Sentry are skipped in dev where their per-module
// transform cost outweighs their value.
...(isBuild
? [
babel({
include: /\.[jt]sx?$/,
babelConfig: {
presets: ["@babel/preset-typescript"],
plugins: [["babel-plugin-react-compiler", {}]],
},
}),
sentryReactRouter(
{
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
telemetry: false,
unstable_sentryVitePluginOptions: {
applicationKey: "sendou-ink",
},
},
config,
),
]
: []),
],
test: {
@ -73,6 +84,58 @@ export default defineConfig((config) => {
},
optimizeDeps: {
exclude: ["@sentry/react-router"],
// Dependencies which are only imported by specific route modules.
// Pre-bundling them at startup avoids mid-session re-optimization
// and full page reloads on first navigations.
include: [
"@date-fns/tz",
"@dnd-kit/core",
"@dnd-kit/modifiers",
"@dnd-kit/sortable",
"@dnd-kit/utilities",
"@epic-web/cachified",
"@internationalized/date",
"@remix-run/form-data-parser",
"@tldraw/tldraw",
"@zumer/snapdom",
"better-sqlite3",
"chart.js",
"compressorjs",
"date-fns/locale/da",
"date-fns/locale/de",
"date-fns/locale/en-US",
"date-fns/locale/es",
"date-fns/locale/fr",
"date-fns/locale/fr-CA",
"date-fns/locale/he",
"date-fns/locale/it",
"date-fns/locale/ja",
"date-fns/locale/ko",
"date-fns/locale/nl",
"date-fns/locale/pl",
"date-fns/locale/pt-BR",
"date-fns/locale/ru",
"date-fns/locale/zh-CN",
"edmonds-blossom-fixed",
"i18next-browser-languagedetector",
"i18next-http-backend",
"jsoncrush",
"kysely",
"kysely/helpers/sqlite",
"markdown-to-jsx",
"neverthrow",
"openskill",
"partysocket",
"qrcode.react",
"react-chartjs-2",
"react-flip-toolkit",
"remeda",
"remix-auth",
"remix-auth-oauth2",
"remix-i18next",
"sql-formatter",
"swr/immutable",
],
},
};
});