chore: reorganize files and add some boilerplate
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
NODE_ENV=development
|
||||
PORT=5000
|
||||
@@ -22,3 +22,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.env
|
||||
|
||||
+5
-1
@@ -9,8 +9,12 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"localforage": "^1.10.0",
|
||||
"match-sorter": "^6.3.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.14.2",
|
||||
"sort-by": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.28",
|
||||
|
||||
Generated
+89
@@ -5,12 +5,24 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
localforage:
|
||||
specifier: ^1.10.0
|
||||
version: 1.10.0
|
||||
match-sorter:
|
||||
specifier: ^6.3.1
|
||||
version: 6.3.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-router-dom:
|
||||
specifier: ^6.14.2
|
||||
version: 6.14.2(react-dom@18.2.0)(react@18.2.0)
|
||||
sort-by:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
|
||||
devDependencies:
|
||||
'@types/react':
|
||||
@@ -45,6 +57,13 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/@babel/runtime@7.22.6:
|
||||
resolution: {integrity: sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.13.11
|
||||
dev: false
|
||||
|
||||
/@esbuild/android-arm64@0.17.14:
|
||||
resolution: {integrity: sha512-eLOpPO1RvtsP71afiFTvS7tVFShJBCT0txiv/xjFBo5a7R7Gjw7X0IgIaFoLKhqXYAXhahoXm7qAmRXhY4guJg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -298,6 +317,11 @@ packages:
|
||||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/@remix-run/router@1.7.2:
|
||||
resolution: {integrity: sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==}
|
||||
engines: {node: '>=14'}
|
||||
dev: false
|
||||
|
||||
/@swc/core-darwin-arm64@1.3.44:
|
||||
resolution: {integrity: sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -668,6 +692,10 @@ packages:
|
||||
function-bind: 1.1.1
|
||||
dev: true
|
||||
|
||||
/immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
dev: false
|
||||
|
||||
/inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
dependencies:
|
||||
@@ -718,6 +746,12 @@ packages:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
dev: false
|
||||
|
||||
/lie@3.1.1:
|
||||
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
dev: false
|
||||
|
||||
/lilconfig@2.1.0:
|
||||
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -727,6 +761,12 @@ packages:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: true
|
||||
|
||||
/localforage@1.10.0:
|
||||
resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
|
||||
dependencies:
|
||||
lie: 3.1.1
|
||||
dev: false
|
||||
|
||||
/loose-envify@1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
@@ -734,6 +774,13 @@ packages:
|
||||
js-tokens: 4.0.0
|
||||
dev: false
|
||||
|
||||
/match-sorter@6.3.1:
|
||||
resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.6
|
||||
remove-accents: 0.4.2
|
||||
dev: false
|
||||
|
||||
/merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -791,6 +838,11 @@ packages:
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/object-path@0.6.0:
|
||||
resolution: {integrity: sha512-fxrwsCFi3/p+LeLOAwo/wyRMODZxdGBtUlWRzsEpsUVrisZbEfZ21arxLGfaWfcnqb8oHPNihIb4XPE8CQPN5A==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
dev: false
|
||||
|
||||
/once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
@@ -909,6 +961,29 @@ packages:
|
||||
scheduler: 0.23.0
|
||||
dev: false
|
||||
|
||||
/react-router-dom@6.14.2(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
react-dom: '>=16.8'
|
||||
dependencies:
|
||||
'@remix-run/router': 1.7.2
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-router: 6.14.2(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-router@6.14.2(react@18.2.0):
|
||||
resolution: {integrity: sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
dependencies:
|
||||
'@remix-run/router': 1.7.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react@18.2.0:
|
||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -929,6 +1004,14 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
dev: false
|
||||
|
||||
/remove-accents@0.4.2:
|
||||
resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==}
|
||||
dev: false
|
||||
|
||||
/resolve@1.22.1:
|
||||
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
|
||||
hasBin: true
|
||||
@@ -972,6 +1055,12 @@ packages:
|
||||
loose-envify: 1.4.0
|
||||
dev: false
|
||||
|
||||
/sort-by@1.2.0:
|
||||
resolution: {integrity: sha512-aRyW65r3xMnf4nxJRluCg0H/woJpksU1dQxRtXYzau30sNBOmf5HACpDd9MZDhKh7ALQ5FgSOfMPwZEtUmMqcg==}
|
||||
dependencies:
|
||||
object-path: 0.6.0
|
||||
dev: false
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
+5
-230
@@ -1,43 +1,8 @@
|
||||
import wp from "./assets/pieces/wp.png";
|
||||
import wn from "./assets/pieces/wn.png";
|
||||
import wb from "./assets/pieces/wb.png";
|
||||
import wr from "./assets/pieces/wr.png";
|
||||
import wq from "./assets/pieces/wq.png";
|
||||
import wk from "./assets/pieces/wk.png";
|
||||
import Chess, { Move } from "../../server/src/chess/engine";
|
||||
import { useState } from "react";
|
||||
import ChessBoard from "./components/Chessboard";
|
||||
|
||||
import bp from "./assets/pieces/bp.png";
|
||||
import bn from "./assets/pieces/bn.png";
|
||||
import bb from "./assets/pieces/bb.png";
|
||||
import br from "./assets/pieces/br.png";
|
||||
import bq from "./assets/pieces/bq.png";
|
||||
import bk from "./assets/pieces/bk.png";
|
||||
|
||||
import Chess, {
|
||||
squareColor,
|
||||
Piece,
|
||||
PieceType,
|
||||
Color,
|
||||
algebraic,
|
||||
file,
|
||||
rank,
|
||||
FILE,
|
||||
RANK,
|
||||
squareIndex,
|
||||
Move,
|
||||
MOVE_FLAGS,
|
||||
PIECE_PROMOTION,
|
||||
COLOR,
|
||||
PiecePromotionType,
|
||||
} from "../../server/src/chess/engine";
|
||||
import { MouseEventHandler, useLayoutEffect, useState } from "react";
|
||||
import Show from "./utils/Show";
|
||||
|
||||
const PIECES: Record<Color, Record<PieceType, string>> = {
|
||||
w: { p: wp, n: wn, b: wb, r: wr, q: wq, k: wk },
|
||||
b: { p: bp, n: bn, b: bb, r: br, q: bq, k: bk },
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
const App = () => {
|
||||
const [chess] = useState(Chess.load());
|
||||
const [, setUpdate] = useState(false);
|
||||
|
||||
@@ -58,196 +23,6 @@ export default function App() {
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// taken form https://github.com/uidotdev/usehooks
|
||||
function useWindowSize() {
|
||||
const [size, setSize] = useState({
|
||||
width: -1,
|
||||
height: -1,
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const handleResize = () => {
|
||||
setSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
};
|
||||
|
||||
handleResize();
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
const ChessBoard: React.FC<{
|
||||
chess: Chess;
|
||||
sendMove: (move: Move) => void;
|
||||
blackPerspective?: boolean;
|
||||
}> = ({ chess, sendMove, blackPerspective }) => {
|
||||
const { width, height } = useWindowSize();
|
||||
const [activeTile, setActiveTile] = useState<number>(-1);
|
||||
const [promotionMove, setPromotionMove] = useState<
|
||||
(Pick<Move, "from" | "to"> & { color: Color }) | null
|
||||
>(null);
|
||||
|
||||
const gridSize = Math.min(width, height, 800);
|
||||
|
||||
const tileProps = new Array(64).fill(null).map((_, i) => ({
|
||||
tileNumber: i,
|
||||
piece: chess.getPiece(i),
|
||||
isAttacked: false,
|
||||
isPromotion: false,
|
||||
isActive: false,
|
||||
}));
|
||||
|
||||
if (activeTile != -1) {
|
||||
tileProps[activeTile].isActive = true;
|
||||
chess.getMovesForSquare(algebraic(activeTile)).forEach(({ to, flags }) => {
|
||||
const square = squareIndex(to);
|
||||
tileProps[square].isAttacked = true;
|
||||
tileProps[square].isPromotion =
|
||||
flags & MOVE_FLAGS.PROMOTION ? true : false;
|
||||
});
|
||||
}
|
||||
|
||||
const tiles = tileProps.map((props, i) => (
|
||||
<Tile key={i} {...props} blackPerspective={blackPerspective} />
|
||||
));
|
||||
|
||||
const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (promotionMove != null) return;
|
||||
|
||||
// TODO: fix types
|
||||
// @ts-ignore
|
||||
const tile = parseInt(e.target.dataset.tile);
|
||||
|
||||
if (isNaN(tile)) return;
|
||||
|
||||
if (activeTile != -1 && tileProps[tile].isAttacked) {
|
||||
const moveObj = {
|
||||
from: algebraic(activeTile),
|
||||
to: algebraic(tile),
|
||||
color: (chess.getPiece(activeTile) as Piece).color,
|
||||
};
|
||||
|
||||
if (tileProps[tile].isPromotion) return setPromotionMove(moveObj);
|
||||
|
||||
sendMove(moveObj);
|
||||
setActiveTile(-1);
|
||||
}
|
||||
|
||||
if (tileProps[tile].piece == null) return setActiveTile(-1);
|
||||
|
||||
if (tile == activeTile) setActiveTile(-1);
|
||||
else setActiveTile(tile);
|
||||
};
|
||||
|
||||
const sendPromotionMove = (promotion: PiecePromotionType) => {
|
||||
if (promotionMove == null) return;
|
||||
|
||||
const moveObj: Move = {
|
||||
from: promotionMove.from,
|
||||
to: promotionMove.to,
|
||||
promotion,
|
||||
};
|
||||
|
||||
sendMove(moveObj);
|
||||
setPromotionMove(null);
|
||||
setActiveTile(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row h-fit items-center">
|
||||
<div
|
||||
className="relative grid grid-rows-8 grid-cols-8 aspect-square border-8 border-black rounded-lg"
|
||||
onClick={handleClick}
|
||||
style={{ width: `${gridSize}px` }}
|
||||
>
|
||||
{blackPerspective == true ? tiles.reverse() : tiles}
|
||||
</div>
|
||||
{promotionMove == null ? (
|
||||
<></>
|
||||
) : (
|
||||
<div className="w-fit flex flex-col bg-black rounded-r-2xl p-1">
|
||||
{PIECE_PROMOTION.map((p) => (
|
||||
<img
|
||||
key={p}
|
||||
src={PIECES[promotionMove.color][p]}
|
||||
style={{ width: `${gridSize / 8}px`, aspectRatio: 1 }}
|
||||
onClick={() => sendPromotionMove(p)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TILE_COLORS = Object.freeze({
|
||||
w: {
|
||||
bg: "bg-white-tile",
|
||||
text: "text-black-tile",
|
||||
active: "bg-sky-400",
|
||||
},
|
||||
b: {
|
||||
bg: "bg-black-tile",
|
||||
text: "text-white-tile",
|
||||
active: "bg-sky-700",
|
||||
},
|
||||
} as const);
|
||||
|
||||
const Tile: React.FC<{
|
||||
tileNumber: number;
|
||||
piece: Piece | null;
|
||||
isActive: boolean;
|
||||
isAttacked: boolean;
|
||||
blackPerspective?: boolean;
|
||||
}> = ({ tileNumber, piece, isActive, isAttacked, blackPerspective }) => {
|
||||
const color = squareColor(tileNumber);
|
||||
|
||||
const { bg: bgColor, text: textColor } = TILE_COLORS[color];
|
||||
const activeColor = isActive ? TILE_COLORS[color].active : "";
|
||||
|
||||
const tileFile = file(tileNumber);
|
||||
const tileRank = rank(tileNumber);
|
||||
const square = algebraic(tileNumber);
|
||||
|
||||
const isFirstColumn = tileFile == (blackPerspective ? FILE.H : FILE.A);
|
||||
const isLastRow = tileRank == (blackPerspective ? RANK.EIGHTH : RANK.FIRST);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative aspect-square font-bold text-xl isolate group [&>*]:pointer-events-none ${bgColor} ${activeColor}`}
|
||||
data-tile={tileNumber}
|
||||
>
|
||||
{piece == null ? <></> : <img src={PIECES[piece.color][piece.type]} />}
|
||||
<Show when={isAttacked == true}>
|
||||
<Show
|
||||
when={piece == null}
|
||||
fallback={
|
||||
<div className="absolute -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-full aspect-square border-8 border-gray-900/40 rounded-full"></div>
|
||||
}
|
||||
>
|
||||
<div className="absolute -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-[35%] aspect-square bg-gray-900/40 rounded-full group-hover:w-[45%]"></div>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show when={isFirstColumn}>
|
||||
<div className={`absolute top-1 left-1 -z-10 ${textColor}`}>
|
||||
{square[1]}
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={isLastRow}>
|
||||
<div className={`absolute bottom-1 right-1 -z-10 ${textColor}`}>
|
||||
{square[0]}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default App;
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
import wp from "../assets/pieces/wp.png";
|
||||
import wn from "../assets/pieces/wn.png";
|
||||
import wb from "../assets/pieces/wb.png";
|
||||
import wr from "../assets/pieces/wr.png";
|
||||
import wq from "../assets/pieces/wq.png";
|
||||
import wk from "../assets/pieces/wk.png";
|
||||
|
||||
import bp from "../assets/pieces/bp.png";
|
||||
import bn from "../assets/pieces/bn.png";
|
||||
import bb from "../assets/pieces/bb.png";
|
||||
import br from "../assets/pieces/br.png";
|
||||
import bq from "../assets/pieces/bq.png";
|
||||
import bk from "../assets/pieces/bk.png";
|
||||
|
||||
import Chess, {
|
||||
squareColor,
|
||||
Piece,
|
||||
PieceType,
|
||||
Color,
|
||||
algebraic,
|
||||
file,
|
||||
rank,
|
||||
FILE,
|
||||
RANK,
|
||||
squareIndex,
|
||||
Move,
|
||||
MOVE_FLAGS,
|
||||
PIECE_PROMOTION,
|
||||
PiecePromotionType,
|
||||
} from "../../../server/src/chess/engine";
|
||||
import { MouseEventHandler, useState } from "react";
|
||||
import Show from "../utils/Show";
|
||||
import useWindowSize from "../utils/useWindowSize";
|
||||
|
||||
|
||||
const PIECES: Record<Color, Record<PieceType, string>> = {
|
||||
w: { p: wp, n: wn, b: wb, r: wr, q: wq, k: wk },
|
||||
b: { p: bp, n: bn, b: bb, r: br, q: bq, k: bk },
|
||||
};
|
||||
const ChessBoard: React.FC<{
|
||||
chess: Chess;
|
||||
sendMove: (move: Move) => void;
|
||||
blackPerspective?: boolean;
|
||||
}> = ({ chess, sendMove, blackPerspective }) => {
|
||||
const { width, height } = useWindowSize();
|
||||
const [activeTile, setActiveTile] = useState<number>(-1);
|
||||
const [promotionMove, setPromotionMove] = useState<
|
||||
(Pick<Move, "from" | "to"> & { color: Color }) | null
|
||||
>(null);
|
||||
|
||||
const gridSize = Math.min(width, height, 800);
|
||||
|
||||
const tileProps = new Array(64).fill(null).map((_, i) => ({
|
||||
tileNumber: i,
|
||||
piece: chess.getPiece(i),
|
||||
isAttacked: false,
|
||||
isPromotion: false,
|
||||
isActive: false,
|
||||
}));
|
||||
|
||||
if (activeTile != -1) {
|
||||
tileProps[activeTile].isActive = true;
|
||||
chess.getMovesForSquare(algebraic(activeTile)).forEach(({ to, flags }) => {
|
||||
const square = squareIndex(to);
|
||||
tileProps[square].isAttacked = true;
|
||||
tileProps[square].isPromotion =
|
||||
flags & MOVE_FLAGS.PROMOTION ? true : false;
|
||||
});
|
||||
}
|
||||
|
||||
const tiles = tileProps.map((props, i) => (
|
||||
<Tile key={i} {...props} blackPerspective={blackPerspective} />
|
||||
));
|
||||
|
||||
const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (promotionMove != null) return;
|
||||
|
||||
// TODO: fix types
|
||||
// @ts-ignore
|
||||
const tile = parseInt(e.target.dataset.tile);
|
||||
|
||||
if (isNaN(tile)) return;
|
||||
|
||||
if (activeTile != -1 && tileProps[tile].isAttacked) {
|
||||
const moveObj = {
|
||||
from: algebraic(activeTile),
|
||||
to: algebraic(tile),
|
||||
color: (chess.getPiece(activeTile) as Piece).color,
|
||||
};
|
||||
|
||||
if (tileProps[tile].isPromotion) return setPromotionMove(moveObj);
|
||||
|
||||
sendMove(moveObj);
|
||||
setActiveTile(-1);
|
||||
}
|
||||
|
||||
if (tileProps[tile].piece == null) return setActiveTile(-1);
|
||||
|
||||
if (tile == activeTile) setActiveTile(-1);
|
||||
else setActiveTile(tile);
|
||||
};
|
||||
|
||||
const sendPromotionMove = (promotion: PiecePromotionType) => {
|
||||
if (promotionMove == null) return;
|
||||
|
||||
const moveObj: Move = {
|
||||
from: promotionMove.from,
|
||||
to: promotionMove.to,
|
||||
promotion,
|
||||
};
|
||||
|
||||
sendMove(moveObj);
|
||||
setPromotionMove(null);
|
||||
setActiveTile(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row h-fit items-center">
|
||||
<div
|
||||
className="relative grid grid-rows-8 grid-cols-8 aspect-square border-8 border-black rounded-lg"
|
||||
onClick={handleClick}
|
||||
style={{ width: `${gridSize}px` }}
|
||||
>
|
||||
{blackPerspective == true ? tiles.reverse() : tiles}
|
||||
</div>
|
||||
{promotionMove == null ? (
|
||||
<></>
|
||||
) : (
|
||||
<div className="w-fit flex flex-col bg-black rounded-r-2xl p-1">
|
||||
{PIECE_PROMOTION.map((p) => (
|
||||
<img
|
||||
key={p}
|
||||
src={PIECES[promotionMove.color][p]}
|
||||
style={{ width: `${gridSize / 8}px`, aspectRatio: 1 }}
|
||||
onClick={() => sendPromotionMove(p)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TILE_COLORS = Object.freeze({
|
||||
w: {
|
||||
bg: "bg-white-tile",
|
||||
text: "text-black-tile",
|
||||
active: "bg-sky-400",
|
||||
},
|
||||
b: {
|
||||
bg: "bg-black-tile",
|
||||
text: "text-white-tile",
|
||||
active: "bg-sky-700",
|
||||
},
|
||||
} as const);
|
||||
|
||||
const Tile: React.FC<{
|
||||
tileNumber: number;
|
||||
piece: Piece | null;
|
||||
isActive: boolean;
|
||||
isAttacked: boolean;
|
||||
blackPerspective?: boolean;
|
||||
}> = ({ tileNumber, piece, isActive, isAttacked, blackPerspective }) => {
|
||||
const color = squareColor(tileNumber);
|
||||
|
||||
const { bg: bgColor, text: textColor } = TILE_COLORS[color];
|
||||
const activeColor = isActive ? TILE_COLORS[color].active : "";
|
||||
|
||||
const tileFile = file(tileNumber);
|
||||
const tileRank = rank(tileNumber);
|
||||
const square = algebraic(tileNumber);
|
||||
|
||||
const isFirstColumn = tileFile == (blackPerspective ? FILE.H : FILE.A);
|
||||
const isLastRow = tileRank == (blackPerspective ? RANK.EIGHTH : RANK.FIRST);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative aspect-square font-bold text-xl isolate group [&>*]:pointer-events-none ${bgColor} ${activeColor}`}
|
||||
data-tile={tileNumber}
|
||||
>
|
||||
{piece == null ? <></> : <img src={PIECES[piece.color][piece.type]} />}
|
||||
<Show when={isAttacked == true}>
|
||||
<Show
|
||||
when={piece == null}
|
||||
fallback={
|
||||
<div className="absolute -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-full aspect-square border-8 border-gray-900/40 rounded-full"></div>
|
||||
}
|
||||
>
|
||||
<div className="absolute -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 w-[35%] aspect-square bg-gray-900/40 rounded-full group-hover:w-[45%]"></div>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show when={isFirstColumn}>
|
||||
<div className={`absolute top-1 left-1 -z-10 ${textColor}`}>
|
||||
{square[1]}
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={isLastRow}>
|
||||
<div className={`absolute bottom-1 right-1 -z-10 ${textColor}`}>
|
||||
{square[0]}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChessBoard;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useLayoutEffect, useState } from "react";
|
||||
|
||||
// taken form https://github.com/uidotdev/usehooks
|
||||
export default function useWindowSize() {
|
||||
const [size, setSize] = useState({
|
||||
width: -1,
|
||||
height: -1,
|
||||
});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const handleResize = () => {
|
||||
setSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
};
|
||||
|
||||
handleResize();
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return size;
|
||||
}
|
||||
+12
-3
@@ -1,7 +1,16 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:5000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,23 +4,29 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node dist/server.js",
|
||||
"dev": "nodemon src/server.ts",
|
||||
"build": "tsc -p .",
|
||||
"start": "node server/dist/server.js",
|
||||
"server": "nodemon server/src/server.ts",
|
||||
"client": "npm run dev --prefix client",
|
||||
"dev": "concurrently \"npm run server\" \"npm run client\"",
|
||||
"build:server": "tsc -p .",
|
||||
"build:client":"npm run build --prefix client",
|
||||
"build": "concurrently \"npm run build:server\" \"npm run build:client\"",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/node": "^18.15.11",
|
||||
"concurrently": "^8.2.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.0.3",
|
||||
"vitest": "^0.29.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+202
@@ -1,6 +1,13 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
dotenv:
|
||||
specifier: ^16.3.1
|
||||
version: 16.3.1
|
||||
express:
|
||||
specifier: ^4.18.2
|
||||
version: 4.18.2
|
||||
@@ -12,6 +19,9 @@ devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^18.15.11
|
||||
version: 18.15.11
|
||||
concurrently:
|
||||
specifier: ^8.2.0
|
||||
version: 8.2.0
|
||||
nodemon:
|
||||
specifier: ^2.0.22
|
||||
version: 2.0.22
|
||||
@@ -27,6 +37,13 @@ devDependencies:
|
||||
|
||||
packages:
|
||||
|
||||
/@babel/runtime@7.22.6:
|
||||
resolution: {integrity: sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.13.11
|
||||
dev: true
|
||||
|
||||
/@cspotcode/source-map-support@0.8.1:
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -391,6 +408,13 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
dev: true
|
||||
|
||||
/ansi-styles@5.2.0:
|
||||
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -494,6 +518,14 @@ packages:
|
||||
type-detect: 4.0.8
|
||||
dev: true
|
||||
|
||||
/chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/check-error@1.0.2:
|
||||
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
|
||||
dev: true
|
||||
@@ -521,10 +553,46 @@ packages:
|
||||
string-width: 5.1.2
|
||||
dev: true
|
||||
|
||||
/cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
dev: true
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
dev: true
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: true
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
||||
/concurrently@8.2.0:
|
||||
resolution: {integrity: sha512-nnLMxO2LU492mTUj9qX/az/lESonSZu81UznYDoXtz1IQf996ixVqPAgHXwvHiHCAef/7S8HIK+fTFK7Ifk8YA==}
|
||||
engines: {node: ^14.13.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
date-fns: 2.30.0
|
||||
lodash: 4.17.21
|
||||
rxjs: 7.8.1
|
||||
shell-quote: 1.8.1
|
||||
spawn-command: 0.0.2
|
||||
supports-color: 8.1.1
|
||||
tree-kill: 1.2.2
|
||||
yargs: 17.7.2
|
||||
dev: true
|
||||
|
||||
/content-disposition@0.5.4:
|
||||
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -550,6 +618,13 @@ packages:
|
||||
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
|
||||
dev: true
|
||||
|
||||
/date-fns@2.30.0:
|
||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||
engines: {node: '>=0.11'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.6
|
||||
dev: true
|
||||
|
||||
/debug@2.6.9:
|
||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||
peerDependencies:
|
||||
@@ -612,6 +687,11 @@ packages:
|
||||
engines: {node: '>=0.3.1'}
|
||||
dev: true
|
||||
|
||||
/dotenv@16.3.1:
|
||||
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
dev: true
|
||||
@@ -620,6 +700,10 @@ packages:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
dev: false
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
dev: true
|
||||
@@ -659,6 +743,11 @@ packages:
|
||||
'@esbuild/win32-x64': 0.17.15
|
||||
dev: true
|
||||
|
||||
/escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/escape-html@1.0.3:
|
||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||
dev: false
|
||||
@@ -750,6 +839,11 @@ packages:
|
||||
/function-bind@1.1.1:
|
||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||
|
||||
/get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
dev: true
|
||||
|
||||
/get-func-name@2.0.0:
|
||||
resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
|
||||
dev: true
|
||||
@@ -774,6 +868,11 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/has-symbols@1.0.3:
|
||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -834,6 +933,11 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-fullwidth-code-point@4.0.0:
|
||||
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -860,6 +964,10 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
||||
/loupe@2.3.6:
|
||||
resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==}
|
||||
dependencies:
|
||||
@@ -1087,6 +1195,15 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/resolve@1.22.1:
|
||||
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
|
||||
hasBin: true
|
||||
@@ -1104,6 +1221,12 @@ packages:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
|
||||
/rxjs@7.8.1:
|
||||
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
|
||||
dependencies:
|
||||
tslib: 2.6.0
|
||||
dev: true
|
||||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: false
|
||||
@@ -1159,6 +1282,10 @@ packages:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
dev: false
|
||||
|
||||
/shell-quote@1.8.1:
|
||||
resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
|
||||
dev: true
|
||||
|
||||
/side-channel@1.0.4:
|
||||
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
|
||||
dependencies:
|
||||
@@ -1196,6 +1323,10 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/spawn-command@0.0.2:
|
||||
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
|
||||
dev: true
|
||||
|
||||
/stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
dev: true
|
||||
@@ -1209,6 +1340,15 @@ packages:
|
||||
resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==}
|
||||
dev: true
|
||||
|
||||
/string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/string-width@5.1.2:
|
||||
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1218,6 +1358,13 @@ packages:
|
||||
strip-ansi: 7.0.1
|
||||
dev: true
|
||||
|
||||
/strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
dev: true
|
||||
|
||||
/strip-ansi@7.0.1:
|
||||
resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1238,6 +1385,20 @@ packages:
|
||||
has-flag: 3.0.0
|
||||
dev: true
|
||||
|
||||
/supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/supports-color@8.1.1:
|
||||
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1276,6 +1437,11 @@ packages:
|
||||
nopt: 1.0.10
|
||||
dev: true
|
||||
|
||||
/tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/ts-node@10.9.1(@types/node@18.15.11)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
|
||||
hasBin: true
|
||||
@@ -1307,6 +1473,10 @@ packages:
|
||||
yn: 3.1.1
|
||||
dev: true
|
||||
|
||||
/tslib@2.6.0:
|
||||
resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==}
|
||||
dev: true
|
||||
|
||||
/type-detect@4.0.8:
|
||||
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -1481,6 +1651,38 @@ packages:
|
||||
stackback: 0.0.2
|
||||
dev: true
|
||||
|
||||
/wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
escalade: 3.1.1
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
dev: true
|
||||
|
||||
/yn@3.1.1:
|
||||
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||
engines: {node: '>=6'}
|
||||
+28
-3
@@ -1,11 +1,36 @@
|
||||
import express, { Application, Request, Response } from "express";
|
||||
import path from "path";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
dotenv.config();
|
||||
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
const app: Application = express();
|
||||
|
||||
app.get("/", (req: Request, res: Response) => {
|
||||
res.send("Hello, world!");
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
const __dirname = path.resolve();
|
||||
app.use(express.static(path.join(__dirname, "client", "dist")));
|
||||
|
||||
app.get("*", (_req, res) =>
|
||||
res.sendFile(path.resolve(__dirname, "client", "dist", "index.html"))
|
||||
);
|
||||
} else app.get("/", (_req, res) => res.send("Hello, world!"));
|
||||
|
||||
app.use((req, res, next) => {
|
||||
const error = new Error(`Not Found - ${req.originalUrl}`);
|
||||
res.status(404);
|
||||
next(error);
|
||||
});
|
||||
|
||||
app.use((err: Error, _req: Request, res: Response) => {
|
||||
let status = res.statusCode == 200 ? 500 : res.statusCode;
|
||||
let message = err.message;
|
||||
|
||||
res.status(status).json({
|
||||
message,
|
||||
stack: process.env.NODE_ENV !== "production" ? err.stack : null,
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(PORT, () =>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||
"outDir": "./server/dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
@@ -101,9 +101,9 @@
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"exclude": [
|
||||
"src/__test__/"
|
||||
"server/src/__test__/"
|
||||
],
|
||||
"include": [
|
||||
"src/"
|
||||
"server/src/"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user