Compare commits
4
Commits
v2.0.0-rc2
..
v2.0.1
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
.git
|
||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
npm-debug.log*
|
||||||
|
tsconfig.tsbuildinfo
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"next/core-web-vitals",
|
|
||||||
"next/typescript"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+4
-4
@@ -3,9 +3,9 @@
|
|||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
# IMPORTANT: Node.js Version Maintenance
|
# IMPORTANT: Node.js Version Maintenance
|
||||||
# This Dockerfile uses Node.js 24.13.0-slim, which was the latest LTS version at the time of writing.
|
# This Dockerfile uses Node.js 24.22.1-slim, which was the latest LTS version at the time of writing.
|
||||||
# To ensure security and compatibility, regularly update the NODE_VERSION ARG to the latest LTS version.
|
# To ensure security and compatibility, regularly update the NODE_VERSION ARG to the latest LTS version.
|
||||||
ARG NODE_VERSION=24.13.0-slim
|
ARG NODE_VERSION=24.22.1-slim
|
||||||
|
|
||||||
FROM node:${NODE_VERSION} AS dependencies
|
FROM node:${NODE_VERSION} AS dependencies
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ FROM node:${NODE_VERSION} AS dependencies
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy package-related files first to leverage Docker's caching mechanism
|
# Copy package-related files first to leverage Docker's caching mechanism
|
||||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* pnpm-workspace.yaml* .npmrc* ./
|
||||||
|
|
||||||
# Install project dependencies with frozen lockfile for reproducible builds
|
# Install project dependencies with frozen lockfile for reproducible builds
|
||||||
RUN --mount=type=cache,target=/root/.npm \
|
RUN --mount=type=cache,target=/root/.npm \
|
||||||
@@ -109,4 +109,4 @@ USER node
|
|||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Start Next.js standalone server
|
# Start Next.js standalone server
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Connect4 Moderator Observer UI
|
||||||
|
|
||||||
|
A website interface for connecting to the [Connect4 WebSocket server](https://github.com/joshuafhiggins/connect4-moderator-server) as an observer or player, watching live matches, and managing tournaments.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Linux/macOS terminal or Windows PowerShell
|
||||||
|
- Git
|
||||||
|
- Docker (optional, for containerized runs)
|
||||||
|
|
||||||
|
## Run Locally (Node.js + npm)
|
||||||
|
|
||||||
|
### 1) Install Node.js (includes npm)
|
||||||
|
|
||||||
|
#### Option A: Install with `nvm` (recommended, including Windows support)
|
||||||
|
|
||||||
|
On Linux/macOS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
||||||
|
source ~/.zshrc
|
||||||
|
nvm install --lts
|
||||||
|
nvm use --lts
|
||||||
|
node -v
|
||||||
|
npm -v
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows (PowerShell), use `nvm-windows`:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
winget install CoreyButler.NVMforWindows
|
||||||
|
nvm install lts
|
||||||
|
nvm use lts
|
||||||
|
node -v
|
||||||
|
npm -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) Install dependencies
|
||||||
|
|
||||||
|
From the project root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3) Start the UI in development mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000).
|
||||||
|
|
||||||
|
## Run with Docker
|
||||||
|
|
||||||
|
This repository includes:
|
||||||
|
|
||||||
|
- `Dockerfile` (multi-stage production build)
|
||||||
|
- `docker-compose.yml`
|
||||||
|
- `docker_build.sh`
|
||||||
|
|
||||||
|
### Build image directly from `Dockerfile`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build . -t joshuafhiggins/connect4-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
or use the provided script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./docker_build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run container directly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -p 3000:3000 --name connect4-ui joshuafhiggins/connect4-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run with Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run detached:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful npm Scripts
|
||||||
|
|
||||||
|
- `npm run dev` - start Next.js dev server
|
||||||
|
- `npm run build` - build for production
|
||||||
|
- `npm run start` - run production build
|
||||||
|
- `npm run lint` - lint the project
|
||||||
+23
-14
@@ -23,6 +23,8 @@ export default function PlayPage() {
|
|||||||
role,
|
role,
|
||||||
username,
|
username,
|
||||||
status,
|
status,
|
||||||
|
isInMatch,
|
||||||
|
shouldGoFirst,
|
||||||
send,
|
send,
|
||||||
subscribe,
|
subscribe,
|
||||||
reconnectAttempts,
|
reconnectAttempts,
|
||||||
@@ -47,6 +49,20 @@ export default function PlayPage() {
|
|||||||
const myColorRef = useRef<1 | 2 | null>(null);
|
const myColorRef = useRef<1 | 2 | null>(null);
|
||||||
const isMyTurnRef = useRef(false);
|
const isMyTurnRef = useRef(false);
|
||||||
|
|
||||||
|
// Restore match state before showing Ready Up after a mid-match reconnect.
|
||||||
|
if (role === "player" && status === "connected" && gamePhase === "idle") {
|
||||||
|
setGamePhase(isInMatch ? "playing" : "connected");
|
||||||
|
if (isInMatch) {
|
||||||
|
setMyColor(shouldGoFirst ? 1 : 2);
|
||||||
|
setIsMyTurn(shouldGoFirst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
myColorRef.current = myColor;
|
||||||
|
isMyTurnRef.current = isMyTurn;
|
||||||
|
}, [myColor, isMyTurn]);
|
||||||
|
|
||||||
const resetGame = useCallback(() => {
|
const resetGame = useCallback(() => {
|
||||||
setBoard(createEmptyBoard());
|
setBoard(createEmptyBoard());
|
||||||
setLastMove(null);
|
setLastMove(null);
|
||||||
@@ -84,25 +100,18 @@ export default function PlayPage() {
|
|||||||
router.replace("/spectate");
|
router.replace("/spectate");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}, [role, status, router, shouldRedirectToConnect, clearRedirectFlag]);
|
||||||
if (status === "connected" && gamePhase === "idle") {
|
|
||||||
setGamePhase("connected");
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
role,
|
|
||||||
status,
|
|
||||||
router,
|
|
||||||
gamePhase,
|
|
||||||
shouldRedirectToConnect,
|
|
||||||
clearRedirectFlag,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = subscribe((msg: ParsedMessage) => {
|
const unsubscribe = subscribe((msg: ParsedMessage) => {
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case "CONNECT_ACK":
|
case "CONNECT_ACK":
|
||||||
case "RECONNECT_ACK":
|
case "RECONNECT_ACK":
|
||||||
setGamePhase((prev) => (prev === "idle" ? "connected" : prev));
|
setGamePhase((prev) => {
|
||||||
|
if (prev !== "idle") return prev;
|
||||||
|
if (isInMatch) return "playing";
|
||||||
|
return "connected";
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "ERROR":
|
case "ERROR":
|
||||||
@@ -193,7 +202,7 @@ export default function PlayPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [resetGame, send, subscribe]);
|
}, [resetGame, send, subscribe, isInMatch, username]);
|
||||||
|
|
||||||
const handleColumnClick = useCallback(
|
const handleColumnClick = useCallback(
|
||||||
(col: number) => {
|
(col: number) => {
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ export default function AdminSettingsPanel() {
|
|||||||
break;
|
break;
|
||||||
case "RESERVATION_LIST":
|
case "RESERVATION_LIST":
|
||||||
setReservations(message.reservations);
|
setReservations(message.reservations);
|
||||||
|
setActionFeedback("Loaded reservations.");
|
||||||
break;
|
break;
|
||||||
case "RESERVATION_ADD":
|
case "RESERVATION_ADD":
|
||||||
setReservations((prev) => {
|
setReservations((prev) => {
|
||||||
|
|||||||
+18
-16
@@ -30,12 +30,27 @@ export default function Board({
|
|||||||
column: number;
|
column: number;
|
||||||
row: number;
|
row: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const isFirstRender = useRef(true);
|
|
||||||
const animationTimeoutRef = useRef<number | null>(null);
|
const animationTimeoutRef = useRef<number | null>(null);
|
||||||
const chipDropSoundsRef = useRef<HTMLAudioElement[]>([]);
|
const chipDropSoundsRef = useRef<HTMLAudioElement[]>([]);
|
||||||
const canInteract = !disabled && !!onColumnClick;
|
const canInteract = !disabled && !!onColumnClick;
|
||||||
const lastMoveColumn = lastMove?.column ?? null;
|
const lastMoveColumn = lastMove?.column ?? null;
|
||||||
const lastMoveRow = lastMove?.row ?? null;
|
const lastMoveRow = lastMove?.row ?? null;
|
||||||
|
const [previousMove, setPreviousMove] = useState({
|
||||||
|
column: lastMoveColumn,
|
||||||
|
row: lastMoveRow,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
previousMove.column !== lastMoveColumn ||
|
||||||
|
previousMove.row !== lastMoveRow
|
||||||
|
) {
|
||||||
|
setPreviousMove({ column: lastMoveColumn, row: lastMoveRow });
|
||||||
|
setAnimatingMove(
|
||||||
|
lastMoveColumn === null || lastMoveRow === null
|
||||||
|
? null
|
||||||
|
: { column: lastMoveColumn, row: lastMoveRow },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
chipDropSoundsRef.current = CHIP_DROP_SOUND_PATHS.map((path) => {
|
chipDropSoundsRef.current = CHIP_DROP_SOUND_PATHS.map((path) => {
|
||||||
@@ -54,26 +69,13 @@ export default function Board({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFirstRender.current) {
|
if (animatingMove === null) return;
|
||||||
isFirstRender.current = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animationTimeoutRef.current !== null) {
|
|
||||||
window.clearTimeout(animationTimeoutRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastMoveColumn === null || lastMoveRow === null) {
|
|
||||||
setAnimatingMove(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefersReducedMotion = window.matchMedia(
|
const prefersReducedMotion = window.matchMedia(
|
||||||
"(prefers-reduced-motion: reduce)",
|
"(prefers-reduced-motion: reduce)",
|
||||||
).matches;
|
).matches;
|
||||||
const dropDurationMs = prefersReducedMotion ? 0 : CHIP_DROP_ANIMATION_MS;
|
const dropDurationMs = prefersReducedMotion ? 0 : CHIP_DROP_ANIMATION_MS;
|
||||||
|
|
||||||
setAnimatingMove({ column: lastMoveColumn, row: lastMoveRow });
|
|
||||||
animationTimeoutRef.current = window.setTimeout(() => {
|
animationTimeoutRef.current = window.setTimeout(() => {
|
||||||
const sounds = chipDropSoundsRef.current;
|
const sounds = chipDropSoundsRef.current;
|
||||||
const sound = sounds[Math.floor(Math.random() * sounds.length)];
|
const sound = sounds[Math.floor(Math.random() * sounds.length)];
|
||||||
@@ -94,7 +96,7 @@ export default function Board({
|
|||||||
window.clearTimeout(animationTimeoutRef.current);
|
window.clearTimeout(animationTimeoutRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [lastMoveColumn, lastMoveRow]);
|
}, [animatingMove]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center gap-3">
|
||||||
|
|||||||
+7
-6
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { SubmitEvent, useEffect, useState } from "react";
|
import { SubmitEvent, useState } from "react";
|
||||||
import { Settings, X } from "lucide-react";
|
import { Settings, X } from "lucide-react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
@@ -28,11 +28,12 @@ export default function Nav() {
|
|||||||
const disableRoleSwitch =
|
const disableRoleSwitch =
|
||||||
status === "connecting" || status === "reconnecting";
|
status === "connecting" || status === "reconnecting";
|
||||||
|
|
||||||
useEffect(() => {
|
if (
|
||||||
if (isConnectionPage || (status === "disconnected" && shouldRedirectToConnect)) {
|
showSettingsModal &&
|
||||||
setShowSettingsModal(false);
|
(isConnectionPage || (status === "disconnected" && shouldRedirectToConnect))
|
||||||
}
|
) {
|
||||||
}, [isConnectionPage, status, shouldRedirectToConnect]);
|
setShowSettingsModal(false);
|
||||||
|
}
|
||||||
|
|
||||||
const handleBecomePlayer = (event: SubmitEvent<HTMLFormElement>) => {
|
const handleBecomePlayer = (event: SubmitEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig, globalIgnores } from "eslint/config";
|
||||||
|
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||||
|
import nextTs from "eslint-config-next/typescript";
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
...nextVitals,
|
||||||
|
...nextTs,
|
||||||
|
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
|
||||||
|
]);
|
||||||
@@ -40,6 +40,7 @@ interface ConnectionContextValue {
|
|||||||
username: string;
|
username: string;
|
||||||
status: ConnectionStatus;
|
status: ConnectionStatus;
|
||||||
isInMatch: boolean;
|
isInMatch: boolean;
|
||||||
|
shouldGoFirst: boolean;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
reconnectAttempts: number;
|
reconnectAttempts: number;
|
||||||
shouldRedirectToConnect: boolean;
|
shouldRedirectToConnect: boolean;
|
||||||
@@ -70,6 +71,7 @@ export function ConnectionProvider({
|
|||||||
const [username, setUsername] = useState("");
|
const [username, setUsername] = useState("");
|
||||||
const [status, setStatus] = useState<ConnectionStatus>("idle");
|
const [status, setStatus] = useState<ConnectionStatus>("idle");
|
||||||
const [isInMatch, setIsInMatch] = useState(false);
|
const [isInMatch, setIsInMatch] = useState(false);
|
||||||
|
const [shouldGoFirst, setShouldGoFirst] = useState(false);
|
||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
||||||
const [shouldRedirectToConnect, setShouldRedirectToConnect] = useState(false);
|
const [shouldRedirectToConnect, setShouldRedirectToConnect] = useState(false);
|
||||||
@@ -81,6 +83,7 @@ export function ConnectionProvider({
|
|||||||
const reconnectDeadlineRef = useRef<number | null>(null);
|
const reconnectDeadlineRef = useRef<number | null>(null);
|
||||||
const reconnectActiveRef = useRef(false);
|
const reconnectActiveRef = useRef(false);
|
||||||
const isInMatchRef = useRef(false);
|
const isInMatchRef = useRef(false);
|
||||||
|
const shouldGoFirstRef = useRef(false);
|
||||||
const sessionRef = useRef<SessionState | null>(null);
|
const sessionRef = useRef<SessionState | null>(null);
|
||||||
|
|
||||||
const clearReconnectTimer = useCallback(() => {
|
const clearReconnectTimer = useCallback(() => {
|
||||||
@@ -202,6 +205,8 @@ export function ConnectionProvider({
|
|||||||
if (parsed.type === "GAME_START") {
|
if (parsed.type === "GAME_START") {
|
||||||
isInMatchRef.current = true;
|
isInMatchRef.current = true;
|
||||||
setIsInMatch(true);
|
setIsInMatch(true);
|
||||||
|
shouldGoFirstRef.current = parsed.goesFirst;
|
||||||
|
setShouldGoFirst(parsed.goesFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -377,6 +382,7 @@ export function ConnectionProvider({
|
|||||||
username,
|
username,
|
||||||
status,
|
status,
|
||||||
isInMatch,
|
isInMatch,
|
||||||
|
shouldGoFirst,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
reconnectAttempts,
|
reconnectAttempts,
|
||||||
shouldRedirectToConnect,
|
shouldRedirectToConnect,
|
||||||
@@ -394,6 +400,7 @@ export function ConnectionProvider({
|
|||||||
username,
|
username,
|
||||||
status,
|
status,
|
||||||
isInMatch,
|
isInMatch,
|
||||||
|
shouldGoFirst,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
reconnectAttempts,
|
reconnectAttempts,
|
||||||
shouldRedirectToConnect,
|
shouldRedirectToConnect,
|
||||||
|
|||||||
Vendored
+2
@@ -1,5 +1,7 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
import "./.next/types/routes.d.ts";
|
||||||
|
import "./.next/types/root-params.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
Generated
+1044
-407
File diff suppressed because it is too large.
Load diff
+11
-7
@@ -6,27 +6,31 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "eslint .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check ."
|
"format:check": "prettier --check ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lucide-react": "^1.8.0",
|
"lucide-react": "^1.8.0",
|
||||||
"next": "15.2.4",
|
"next": "16.4.2",
|
||||||
"react": "^19.0.0",
|
"react": "19.2.5",
|
||||||
"react-confetti": "^6.4.0",
|
"react-confetti": "^6.4.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "19.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "19.2.14",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "19.2.3",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.2.4",
|
"eslint-config-next": "16.4.2",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@types/react": "19.2.14",
|
||||||
|
"@types/react-dom": "19.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+19
-5
@@ -1,6 +1,10 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@@ -10,7 +14,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "react-jsx",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
@@ -18,10 +22,20 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"target": "ES2017"
|
"target": "ES2017"
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
".next/dev/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user