commit ce4705fdecbc3f878714b1c28c210c9ec41a5206
parent 2174f77f7699f7a300bdc7342d0989ffe3ab0a67
Author: hrryt <harry@mayesfield.uk>
Date: Thu, 6 Aug 2026 20:19:20 +0100
Refactor to use preact
Diffstat:
27 files changed, 23 insertions(+), 729 deletions(-)
diff --git a/.dockerignore b/.dockerignore
@@ -1,4 +0,0 @@
-.react-router
-build
-node_modules
-README.md
-\ No newline at end of file
diff --git a/.gitignore b/.gitignore
@@ -1,8 +1,3 @@
-.DS_Store
-.env
-/node_modules/
package-lock.json
-
-# React Router
-/.react-router/
-/build/
+node_modules/
+dist/
diff --git a/Dockerfile b/Dockerfile
@@ -1,22 +0,0 @@
-FROM node:24-alpine AS development-dependencies-env
-COPY . /app
-WORKDIR /app
-RUN npm ci
-
-FROM node:24-alpine AS production-dependencies-env
-COPY ./package.json package-lock.json /app/
-WORKDIR /app
-RUN npm ci --omit=dev
-
-FROM node:24-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:24-alpine
-COPY ./package.json package-lock.json /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"]
diff --git a/README.md b/README.md
@@ -1,87 +1,7 @@
-# Welcome to React Router!
+## Scripts
-A modern, production-ready template for building full-stack React applications using React Router.
+- `npm run dev` - Starts a dev server at http://localhost:5173/
-[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
+- `npm run build` - Builds for production, emitting to `dist/`. Prerenders all found routes in app to static HTML
-## 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)
-├── 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.
+- `npm run preview` - Starts a server at http://localhost:4173/ to test production build locally
diff --git a/app/components/Equation.jsx b/app/components/Equation.jsx
@@ -1,12 +0,0 @@
-import * as React from 'react';
-import { render } from 'kern-typ';
-
-export default function Equation({ equation, displayMode = false }) {
- const ref = React.useRef(null);
-
- React.useEffect(() => {
- render(equation, ref.current, { displayMode: displayMode });
- }, [equation]);
-
- return <div ref={ref} />;
-}
diff --git a/app/components/MultipleSelection.jsx b/app/components/MultipleSelection.jsx
@@ -1,26 +0,0 @@
-import * as React from 'react';
-
-export default function MultipleSelection({ variable, setVariable, id, legend, values, labels = values }) {
- const checked = values.map(value => variable.includes(value));
-
- function updateVariable(e, index) {
- const newChecked = checked.map((value, i) => i === index ? e.target.checked : value);
- const validChecked = newChecked.some(Boolean) ? newChecked : checked.map((value) => !value);
- setVariable(values.filter((_, i) => validChecked[i]));
- }
-
- return (
- <fieldset key={id}>
- <legend>{legend}</legend>
- {values.map((value, i) => {
- const name = `${id}:${value}`;
- return (
- <div key={value} className="field-row">
- <input type="checkbox" id={name} name={name} checked={checked[i]} onChange={e => updateVariable(e, i)} />
- <label htmlFor={name}>{labels[i]}</label>
- </div>
- );
- })}
- </fieldset>
- );
-}
diff --git a/app/components/Question.jsx b/app/components/Question.jsx
@@ -1,10 +0,0 @@
-import * as React from 'react';
-import Equation from './Equation.jsx';
-
-export default function Question({ question, answer, showAnswer }) {
- return (
- <li>
- <Equation equation={showAnswer ? answer : question} />
- </li>
- );
-}
diff --git a/app/components/QuestionList.jsx b/app/components/QuestionList.jsx
@@ -1,11 +0,0 @@
-import * as React from 'react';
-
-export default function QuestionList({ children }) {
- return (
- <li>
- <ol className="question-list">
- {children}
- </ol>
- </li>
- );
-}
diff --git a/app/components/QuestionWindow.jsx b/app/components/QuestionWindow.jsx
@@ -1,112 +0,0 @@
-import * as React from 'react';
-import QuestionList from './QuestionList.jsx';
-import { getRandomInt, getRandomNumber, getRandomChoice } from '../scripts/utils.js';
-
-import MultipleSelection from './MultipleSelection';
-
-import AddingFractionsQuestion from './questions/AddingFractionsQuestion.jsx';
-import QuadraticEquationQuestion from './questions/QuadraticEquationQuestion.jsx';
-import Window from './Window.jsx';
-
-function chooseInputComponent(type) {
- switch (type) {
- case 'multiple-selection':
- return MultipleSelection;
- }
-}
-
-function chooseQuestionComponent(type) {
- switch (type) {
- case 'quadratic-equation':
- return QuadraticEquationQuestion;
- case 'adding-fractions':
- return AddingFractionsQuestion;
- }
-}
-
-function resolveParameters(question, variables) {
- const parameters = question.constants || { };
- const variableKeys = question.variables || { };
- Object.keys(variableKeys).forEach((key) => {
- parameters[key] = variables[variableKeys[key]];
- })
- return parameters;
-}
-
-function renderQuestion(seed, showAnswer, variables) {
- return (question, i) => {
- const QuestionComponent = chooseQuestionComponent(question.type);
- const parameters = resolveParameters(question, variables);
- return <QuestionComponent key={i} seed={seed+i} showAnswer={showAnswer} {...parameters} />;
- };
-}
-
-function getQuestionList(id, questions, showAnswers, seed, variables) {
- return (
- <QuestionList key={id}>
- {questions.map(renderQuestion(seed, showAnswers, variables))}
- </QuestionList>
- );
-}
-
-function shuffleArray(arr, n, seed) {
- return Array.from({ length: n }).map(() => getRandomChoice(arr, ++seed));
-}
-
-export default function QuestionWindow({ data }) {
-
- const defaultVariables = Object.fromEntries(
- data.variables.map(input => [input.id, input.default ?? input.values])
- );
-
- const [showAnswers, setShowAnswers] = React.useState(false);
- const [seed , setSeed ] = React.useState(Math.random());
- const [variables , setVariables ] = React.useState(defaultVariables);
-
- function setVariable(id) {
- return (value) => {
- setVariables({
- ...variables,
- [id]: value
- });
- }
- }
-
- function revealAnswers() { setShowAnswers(!showAnswers); }
- function triggerRefresh() { setSeed(getRandomNumber(seed)); }
-
- let current_seed = seed;
- const questions = data.questionLists.map(questionList => {
- const { id: id, n: n, questions: unique_questions } = questionList;
- const questions = shuffleArray(unique_questions, n, current_seed);
- const randomQuestionList = getQuestionList(id, questions, showAnswers, current_seed, variables);
- current_seed += n;
- return randomQuestionList;
- });
-
- const fields = data.variables.map(input => {
- const { type: type, ...props } = input;
- const InputComponent = chooseInputComponent(type);
- return <InputComponent key={input.id} variable={variables[input.id]} setVariable={setVariable(input.id)} {...props} />;
- });
-
- return (
- <Window title="Questions">
- {fields}
- <fieldset>
- <legend>Options</legend>
- <button onClick={revealAnswers}>
- {showAnswers ? "Hide" : "Show"} Answers
- </button>
- <button onClick={triggerRefresh}>
- Refresh Questions
- </button>
- </fieldset>
- <div className="field-border question-field kern">
- <ol className="master-question-list">
- {questions}
- </ol>
- </div>
- </Window>
- );
-}
diff --git a/app/components/TestQuestionWindow.jsx b/app/components/TestQuestionWindow.jsx
@@ -1,7 +0,0 @@
-import * as React from 'react';
-import QuestionWindow from './QuestionWindow.jsx';
-import { questionWindow } from '../data/test-question-window.js';
-
-export default function TestQuestionPanel() {
- return <QuestionWindow data={questionWindow} />;
-}
diff --git a/app/components/Window.jsx b/app/components/Window.jsx
@@ -1,19 +0,0 @@
-import * as React from 'react'
-
-export default function Window({ children, title, maximised }){
- return (
- <div className="window">
- <div className="title-bar">
- <div className="title-bar-text">{title}</div>
- <div className="title-bar-controls">
- <button aria-label="Minimize" />
- <button aria-label={maximised ? "Restore" : "Maximize"} />
- <button aria-label="Close" />
- </div>
- </div>
- <div className="window-body">
- {children}
- </div>
- </div>
- )
-}
-\ No newline at end of file
diff --git a/app/components/questions/AddingFractionsQuestion.jsx b/app/components/questions/AddingFractionsQuestion.jsx
@@ -1,42 +0,0 @@
-import * as React from 'react';
-import Question from '../Question.jsx';
-import { getRandomInt, getHCF } from '../../scripts/utils.js';
-
-function formatFraction(num, denom) {
- if (denom == 1) { return `${num}`; }
- return num + '/' + denom;
-}
-
-function simplifyFraction(num, denom) {
- const hcf = getHCF(num, denom);
- return [num / hcf, denom / hcf];
-}
-
-function getRandomFraction(seed) {
- const num0 = getRandomInt(1, 5, seed);
- const denom0 = getRandomInt(2, 10, seed + .05);
- const [num, denom] = simplifyFraction(num0, denom0);
- if (denom == 1) { return getRandomFraction(seed + .1); }
- return [num, denom];
-}
-
-function getRandomFractions(seed) {
- const [num1, denom1] = getRandomFraction(seed);
- const [num2, denom2] = getRandomFraction(seed + .5);
- return [num1, denom1, num2, denom2];
-}
-
-export default function AddingFractionsQuestion({ seed, showAnswer }) {
- const [num1, denom1, num2, denom2] = getRandomFractions(seed);
-
- const question = formatFraction(num1, denom1) + ' + ' + formatFraction(num2, denom2);
-
- const hcf = getHCF(denom1, denom2);
- const sum = (num1 * denom2 + num2 * denom1) / hcf;
- const lcm = denom1 * denom2 / hcf;
- const [num, denom] = simplifyFraction(sum, lcm);
-
- const answer = formatFraction(num, denom);
-
- return <Question question={question} answer={answer} showAnswer={showAnswer} />;
-}
diff --git a/app/components/questions/QuadraticEquationQuestion.jsx b/app/components/questions/QuadraticEquationQuestion.jsx
@@ -1,69 +0,0 @@
-import * as React from 'react';
-import Question from '../Question.jsx';
-import { getRandomInt, getRandomBoolean, getRandomChoice, formatNumber } from '../../scripts/utils.js';
-
-function formatQuadraticExpression(a, b, c) {
- const fa = formatNumber(a, {showPlus: false, showOne: false, showZero: false, suffix: 'x^2'});
- const fb = formatNumber(b, {showPlus: true, showOne: false, showZero: false, suffix: 'x'});
- const fc = formatNumber(c, {showPlus: true, showOne: true, showZero: false});
- return `${fa} ${fb} ${fc}`;
-}
-
-function formatRoots(root1, root2) {
- const froot2 = formatNumber(root2, {showPlus: false, showOne: true, showZero: true});
- if (root1 == root2) { return `x = ${froot2}`; }
- if (Math.abs(root1) == root2) { return `x = plus.minus ${root2}`; }
- const froot1 = formatNumber(root1, {showPlus: false, showOne: true, showZero: true});
- return `x = ${froot1} thick "or" thick x = ${froot2}`;
-}
-
-function formatFactor(root) {
- if (root == 0) { return "x"; }
- const froot = formatNumber(root, {showPlus: true, showOne: true, showZero: false});
- return `(x${froot})`;
-}
-
-function sortFactors(root1, root2) {
- if (root1 > 0 && root2 < 0) { return -1; }
- if (root1 < 0 && root2 > 0) { return +1; }
- return Math.abs(root1) - Math.abs(root2);
-}
-
-function formatFactors(root1, root2, a) {
- const fa = formatNumber(a, {showPlus: false, showOne: false, showZero: false});
- [root1, root2] = [root1, root2].sort(sortFactors);
- const froot2 = formatFactor(root2);
- if (root1 == root2) { return `${fa}${froot2}^2`; }
- const froot1 = formatFactor(root1);
- return `${fa}${froot1}${froot2}`;
-}
-
-function getRandomRoots(seed) {
- return [
- getRandomInt(-9, 9, seed),
- getRandomInt(-9, 9, seed + .1),
- ].sort((a, b) => a - b);
-}
-
-export default function QuadraticEquationQuestion({ seed, showAnswer, aPlurality, framing }) {
- const aChoices = Array();
- aPlurality.includes("a=1") && aChoices.push(1);
- aPlurality.includes("a>1") && aChoices.push(2, 3);
- const a = getRandomChoice(aChoices, seed);
-
- const factoriseChoices = Array();
- framing.includes("Solve") && factoriseChoices.push(false);
- framing.includes("Factorise") && factoriseChoices.push(true);
- const factorise = getRandomChoice(factoriseChoices, seed + .1);
-
- const [root1, root2] = getRandomRoots(seed + .2);
-
- const b = a * -(root1 + root2);
- const c = a * root1 * root2;
-
- const quadratic = formatQuadraticExpression(a, b, c);
- const question = factorise ? `"Factorise" thick ${quadratic}` : `${quadratic} = 0`;
- const answer = factorise ? formatFactors(root1, root2, a) : formatRoots(root1, root2);
-
- return <Question question={question} answer={answer} showAnswer={showAnswer} />;
-}
diff --git a/app/data/test-question-window.js b/app/data/test-question-window.js
@@ -1,27 +0,0 @@
-export const questionWindow = {
- variables: [{
- id: 'framing',
- legend: 'Quadratic Equations: Framing',
- type: 'multiple-selection',
- values: ["Solve", "Factorise"],
- }, {
- id: 'aPlurality',
- legend: 'Quadratic Equations: Coefficient a',
- type: 'multiple-selection',
- values: ["a=1", "a>1"],
- }],
- questionLists: [{
- id: 0,
- n: 10,
- questions: [{
- type: 'quadratic-equation',
- variables: { aPlurality: 'aPlurality', framing: 'framing' },
- }]
- }, {
- id: 1,
- n: 5,
- questions: [{
- type: 'adding-fractions',
- }]
- }]
-}
diff --git a/app/root.tsx b/app/root.tsx
@@ -1,81 +0,0 @@
-import {
- isRouteErrorResponse,
- Links,
- Meta,
- NavLink,
- Outlet,
- Scripts,
- ScrollRestoration,
-} from "react-router";
-
-import type { Route } from "./+types/root";
-import "./styles/index.css";
-
-import Window from "./components/Window.jsx";
-
-// for like googy fonts and stuff?
-export const links: Route.LinksFunction = () => [
- {
- rel: "stylesheet",
- href: "https://unpkg.com/98.css"
- }
-];
-
-export function Layout({ children }: { children: React.ReactNode }) {
- return (
- <html lang="en">
- <head>
- <meta charSet="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <Meta />
- <Links />
- </head>
- <body>
- {children}
- <ScrollRestoration />
- <Scripts />
- </body>
- </html>
- );
-}
-
-export default function App() {
- return (
- <>
- <nav>
- <NavLink to='/' exact className='nav-element' activeClassName="active">Home</NavLink>
- <NavLink to='/questions' className='nav-element' activeClassName="active">Questions</NavLink>
- </nav>
- <Outlet />
- </>
- );
-}
-
-export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
- let message = "Oops!";
- let details = "An unexpected error occurred.";
- let stack: string | undefined;
-
- if (isRouteErrorResponse(error)) {
- message = error.status === 404 ? "404" : "Error";
- details =
- error.status === 404
- ? "The requested page could not be found."
- : error.statusText || details;
- } else if (import.meta.env.DEV && error && error instanceof Error) {
- details = error.message;
- stack = error.stack;
- }
-
- return (
- <Window title="404 Error! (probably)">
- <h1>{message}</h1>
- <p>{details}</p>
- {stack && (
- <pre className="w-full p-4 overflow-x-auto">
- <code>{stack}</code>
- </pre>
- )}
- </Window>
- );
-}
diff --git a/app/routes.ts b/app/routes.ts
@@ -1,6 +0,0 @@
-import { type RouteConfig, index, route } from "@react-router/dev/routes";
-
-export default [
- index("routes/home.tsx"),
- route("questions", "./routes/Questions.tsx")
-] satisfies RouteConfig;
diff --git a/app/routes/Home.tsx b/app/routes/Home.tsx
@@ -1,15 +0,0 @@
-import * as React from 'react';
-import Window from '../components/Window.jsx';
-import type { Route } from "./+types/home";
-
-export function meta({}: Route.MetaArgs) {
- return [
- { title: "TTMaths" },
- { name: "description", content: "Home Page" },
- ];
-}
-
-
-export default function Home() {
- return <Window title="poopwindow">poop butt</Window>;
-}
diff --git a/app/routes/Questions.tsx b/app/routes/Questions.tsx
@@ -1,14 +0,0 @@
-import * as React from 'react';
-import type { Route } from "./+types/home";
-import TestQuestionWindow from '../components/TestQuestionWindow.jsx';
-
-export function meta({}: Route.MetaArgs) {
- return [
- { title: "qestion page :)" },
- { name: "description", content: "Nor sure" },
- ];
-}
-
-export default function Questions() {
- return <TestQuestionWindow />;
-}
diff --git a/app/scripts/auto-maths.js b/app/scripts/auto-maths.js
@@ -1,8 +0,0 @@
-import renderMathInElement from 'kern-typ/auto-render';
-
-renderMathInElement(document.body, {
- delimiters: [
- { left: '$$', right: '$$', display: true },
- { left: '$', right: '$', display: false },
- ],
-});
diff --git a/app/scripts/utils.js b/app/scripts/utils.js
@@ -1,30 +0,0 @@
-export function getRandomInt(min, max, seed) {
- return min + Math.floor((max - min + 1) * getRandomNumber(seed));
-}
-
-export function getRandomChoice(arr, seed) {
- return arr[getRandomInt(0, arr.length - 1, seed)];
-}
-
-export function getRandomBoolean(seed) {
- return getRandomNumber(seed) < 0.5;
-}
-
-export function getRandomNumber(seed) {
- const x = 1e4 * Math.sin(7 * seed);
- return x - Math.floor(x);
-}
-
-export function getHCF(a, b) {
- return b == 0 ? a : getHCF(b, a % b);
-}
-
-export function formatNumber(x, parameters = {showPlus: false, showOne: true, showZero: true, suffix: ''}) {
- if (!parameters.showZero && x == 0) { return ''; }
- const plus = parameters.showPlus ? '+' : '';
- const sign = x < 0 ? '-' : plus;
- x = Math.abs(x);
- parameters.showOne || x == 1 && (x = '');
- parameters.suffix || (parameters.suffix = '') // if suffix undefined
- return sign + x + parameters.suffix;
-}
diff --git a/app/styles/index.css b/app/styles/index.css
@@ -1,3 +0,0 @@
-@import 'kern-typ/kern.css';
-@import './main.css';
-/*98.css is imported in root.tsx now*/
-\ No newline at end of file
diff --git a/app/styles/main.css b/app/styles/main.css
@@ -1,45 +0,0 @@
-body {
- background-color: #008080;
- margin: 0;
-}
-
-nav {
- width: 100vw;
- border-bottom: 2px solid black;
- background-color: #C0C0C0;
- margin-bottom: 10px;
-}
-.nav-element {
- padding: 3px 8px;
- width: max-content;
- min-width: 100px;
- font-weight: bold;
- display: inline-block;
- margin: 5px;
-}
-
-.window {
- min-width: min-content;
- max-width: max-content;
-}
-
-fieldset {
- margin-bottom: 7px;
-}
-
-.field-border {
- font-size: 1.25rem;
- padding: 20px;
-}
-
-.master-question-list {
- margin: 0;
- padding-left: 20px;
- display: flex;
- flex-wrap: wrap;
- gap: 40px;
-}
-
-.question-list {
- list-style-type: lower-latin;
-}
diff --git a/package.json b/package.json
@@ -1,31 +1,19 @@
{
- "name": "react-template",
- "private": true,
- "type": "module",
- "scripts": {
- "build": "react-router build",
- "dev": "react-router dev",
- "start": "react-router-serve ./build/server/index.js",
- "typecheck": "react-router typegen && tsc"
- },
- "dependencies": {
- "@react-router/node": "^8",
- "@react-router/serve": "^8",
- "98.css": "^0.1.21",
- "isbot": "^5.1.36",
- "kern-typ": "^0.2.0",
- "react": "^19.2.7",
- "react-dom": "^19.2.7",
- "react-router": "^8"
- },
- "devDependencies": {
- "@react-router/dev": "^8",
- "@tailwindcss/vite": "^4.2.2",
- "@types/node": "^22",
- "@types/react": "^19.2.14",
- "@types/react-dom": "^19.2.3",
- "tailwindcss": "^4.2.2",
- "typescript": "^5.9.3",
- "vite": "^8.0.3"
- }
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "98.css": "^0.1.21",
+ "kern-typ": "^0.2.0",
+ "preact": "^10.26.9",
+ "preact-iso": "^2.12.1"
+ },
+ "devDependencies": {
+ "@preact/preset-vite": "^2.10.2",
+ "vite": "^7.0.4"
+ }
}
diff --git a/public/favicon.ico b/public/favicon.ico
Binary files differ.
diff --git a/react-router.config.ts b/react-router.config.ts
@@ -1,7 +0,0 @@
-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: false,
-} satisfies Config;
diff --git a/tsconfig.json b/tsconfig.json
@@ -1,26 +0,0 @@
-{
- "include": [
- "**/*",
- "**/.server/**/*",
- "**/.client/**/*",
- ".react-router/types/**/*"
- ],
- "compilerOptions": {
- "lib": ["DOM", "DOM.Iterable", "ES2022"],
- "types": ["node", "vite/client"],
- "target": "ES2022",
- "module": "ES2022",
- "moduleResolution": "bundler",
- "jsx": "react-jsx",
- "rootDirs": [".", "./.react-router/types"],
- "paths": {
- "~/*": ["./app/*"]
- },
- "esModuleInterop": true,
- "verbatimModuleSyntax": true,
- "noEmit": true,
- "resolveJsonModule": true,
- "skipLibCheck": true,
- "strict": true
- }
-}
diff --git a/vite.config.ts b/vite.config.ts
@@ -1,10 +0,0 @@
-import { reactRouter } from "@react-router/dev/vite";
-import tailwindcss from "@tailwindcss/vite";
-import { defineConfig } from "vite";
-
-export default defineConfig({
- plugins: [tailwindcss(), reactRouter()],
- resolve: {
- tsconfigPaths: true,
- },
-});