mirror of
https://github.com/tiennm99/loto.git
synced 2026-10-15 08:17:07 +00:00
refactor: convert from TypeScript to JavaScript with JSDoc
Author types in JSDoc comments. jsconfig.json keeps checkJs: true so the editor's bundled TS server still validates them; CI can validate with npx -p typescript tsc --noEmit on demand. Renames (history preserved via git mv): - next.config.ts -> next.config.mjs - app/layout.tsx -> app/layout.jsx - app/page.tsx -> app/page.jsx - app/master/page.tsx -> app/master/page.jsx - components/player-board.tsx -> components/player-board.jsx - lib/game-logic.ts -> lib/game-logic.js - tsconfig.json -> jsconfig.json (same @/* alias) Drops typescript and @types/node, @types/react, @types/react-dom from devDependencies. Removes vendored next-env.d.ts. eslint config no longer pulls in eslint-config-next/typescript. Behavior unchanged. Build, lint, and dev profiles verified.
This commit is contained in:
1 parent
254a6dc39a
commit
95545c9308
19 files changed
+563
-208
No files matched your search
+29
-15
@@ -2,12 +2,12 @@
|
||||
|
||||
## File Naming & Structure
|
||||
|
||||
- **Kebab-case** for all files: `player-board.tsx`, `game-logic.ts`.
|
||||
- **Kebab-case** for all files: `player-board.jsx`, `game-logic.js`.
|
||||
- **Descriptive names**: Long names are preferred for self-documentation. Avoid ambiguity.
|
||||
- **Single responsibility**: Each file has one primary export (component or utilities).
|
||||
- **Max 200 lines per file**: Split larger components into smaller focused ones.
|
||||
|
||||
## React & TypeScript Conventions
|
||||
## React & JavaScript Conventions
|
||||
|
||||
### Hooks
|
||||
- **useState**: For local UI state (grid, crossed, form inputs).
|
||||
@@ -15,10 +15,12 @@
|
||||
- **useCallback**: For event handlers to stabilize function identity across renders.
|
||||
- **useRef**: For mutable values that don't trigger renders (timers, celebratedRows set).
|
||||
|
||||
### Typing
|
||||
- Explicit `export interface Props { ... }` for component props.
|
||||
- Use `Readonly<{ children: React.ReactNode }>` for layout children.
|
||||
### Typing (JSDoc)
|
||||
- Author types in JSDoc comments — `jsconfig.json` has `checkJs: true`, so `tsc --noEmit` validates them.
|
||||
- Component props: `@typedef {Object} Props { ... }` followed by `@param {Props} props` on the function.
|
||||
- Layout children: `@param {{ children: React.ReactNode }} props`.
|
||||
- Avoid `any`; use precise types (e.g., `number[][]` for grid).
|
||||
- Generics: `@template T` then reference `T` in `@param` / `@returns`.
|
||||
|
||||
### Client-Only Constraint
|
||||
- All interactive pages must have `"use client"` at the top.
|
||||
@@ -47,15 +49,23 @@
|
||||
## localStorage Patterns
|
||||
|
||||
### Saving
|
||||
```typescript
|
||||
function saveGrid(grid: number[][], prefix = "loto"): void {
|
||||
```js
|
||||
/**
|
||||
* @param {number[][]} grid
|
||||
* @param {string} [prefix]
|
||||
*/
|
||||
function saveGrid(grid, prefix = "loto") {
|
||||
localStorage.setItem(`${prefix}_grid`, JSON.stringify(grid));
|
||||
}
|
||||
```
|
||||
|
||||
### Loading
|
||||
```typescript
|
||||
function loadGrid(prefix = "loto"): number[][] | null {
|
||||
```js
|
||||
/**
|
||||
* @param {string} [prefix]
|
||||
* @returns {number[][] | null}
|
||||
*/
|
||||
function loadGrid(prefix = "loto") {
|
||||
const data = localStorage.getItem(`${prefix}_grid`);
|
||||
if (!data) return null;
|
||||
try {
|
||||
@@ -81,18 +91,22 @@ function loadGrid(prefix = "loto"): number[][] | null {
|
||||
| camelCase | `handleCellClick`, `storagePrefix` | variables, functions, props |
|
||||
| PascalCase | `PlayerBoard`, `MasterPage` | components, types |
|
||||
| UPPER_SNAKE | `STORAGE_KEY`, `NUM_ROWS` | constants |
|
||||
| kebab-case | `player-board.tsx` | file names |
|
||||
| kebab-case | `player-board.jsx` | file names |
|
||||
|
||||
## Comment Style
|
||||
|
||||
- Document **why**, not **what**. The code shows what it does.
|
||||
- Use `/** JSDoc */` for exported functions.
|
||||
- Inline comments for complex logic (e.g., weighted random selection in `lib/game-logic.ts:19–28`).
|
||||
- Inline comments for complex logic (e.g., weighted random selection in `lib/game-logic.js:19–28`).
|
||||
|
||||
### Example
|
||||
```typescript
|
||||
/** Weighted random selection of a column index */
|
||||
function randomANumberInRow(weights: number[]): number {
|
||||
```js
|
||||
/**
|
||||
* Weighted random selection of a column index.
|
||||
* @param {number[]} weights
|
||||
* @returns {number}
|
||||
*/
|
||||
function randomANumberInRow(weights) {
|
||||
// Convert weights to cumulative distribution for O(n) lookup
|
||||
const tempWeight = [...weights];
|
||||
for (let i = 1; i < tempWeight.length; i++) {
|
||||
@@ -108,7 +122,7 @@ function randomANumberInRow(weights: number[]): number {
|
||||
2. Third-party imports
|
||||
3. Local component/utility imports
|
||||
|
||||
```typescript
|
||||
```js
|
||||
import { useCallback, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import PlayerBoard from "@/components/player-board";
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
### Routing & Layout
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `app/layout.tsx` | Root HTML layout. Sets Vietnamese lang, imports Geist font, applies global flex layout. |
|
||||
| `app/page.tsx` | Player page (`/`). Instructions toggle, PlayerBoard component, indigo gradient branding. |
|
||||
| `app/master/page.tsx` | Host page (`/master`). Controls (new game, draw number), 9×10 master board, host's player card. |
|
||||
| `app/layout.jsx` | Root HTML layout. Sets Vietnamese lang, imports Geist font, applies global flex layout. |
|
||||
| `app/page.jsx` | Player page (`/`). Instructions toggle, PlayerBoard component, indigo gradient branding. |
|
||||
| `app/master/page.jsx` | Host page (`/master`). Controls (new game, draw number), 9×10 master board, host's player card. |
|
||||
|
||||
### Shared Components
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `components/player-board.tsx` | Reusable player card (9×9 grid). Handles crossed state, bingo popup, "Chờ X" toast. Accepts `storagePrefix` prop for multi-card isolation. |
|
||||
| `components/player-board.jsx` | Reusable player card (9×9 grid). Handles crossed state, bingo popup, "Chờ X" toast. Accepts `storagePrefix` prop for multi-card isolation. |
|
||||
|
||||
### Game Logic
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `lib/game-logic.ts` | Stateless utilities: generateGrid (weighted column selection), saveGrid, loadGrid, saveCrossedState, loadCrossedState, isRowComplete, getWaitingNumber. |
|
||||
| `lib/game-logic.js` | Stateless utilities: generateGrid (weighted column selection), saveGrid, loadGrid, saveCrossedState, loadCrossedState, isRowComplete, getWaitingNumber. |
|
||||
|
||||
### Styling
|
||||
| File | Purpose |
|
||||
@@ -27,7 +27,7 @@
|
||||
### Configuration
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `next.config.ts` | Dual basePath: prod `/loto`, codeserver `/absproxy/{PORT}`. Exports static HTML. HMR-aware. |
|
||||
| `next.config.mjs` | Dual basePath: prod `/loto`, codeserver `/absproxy/{PORT}`. Exports static HTML. HMR-aware. |
|
||||
| `package.json` | Next 16.2.2, React 19.2.4, Tailwind 4. Scripts: dev, dev:codeserver, build, start, lint. |
|
||||
| `eslint.config.mjs` | ESLint 9 config (Next.js preset). |
|
||||
| `.gitignore` | Excludes node_modules, .next, .env.local, etc. |
|
||||
@@ -66,10 +66,10 @@ RootLayout
|
||||
|
||||
| Function | Location | Effect |
|
||||
|----------|----------|--------|
|
||||
| `generateGrid()` | game-logic.ts:52 | Creates 9×9 with weighted column selection (5 nums/row). |
|
||||
| `isRowComplete()` | game-logic.ts:108 | Boolean: all non-zero cells in row crossed? |
|
||||
| `getWaitingNumber()` | game-logic.ts:120 | Returns the single uncrossed number in row, or null. |
|
||||
| `handleCellClick()` | player-board.tsx:112 | Toggle crossed[row][col]. |
|
||||
| `handleDrawNext()` | master/page.tsx:88 | Pop first number from remaining, add to called. |
|
||||
| `generateGrid()` | game-logic.js:52 | Creates 9×9 with weighted column selection (5 nums/row). |
|
||||
| `isRowComplete()` | game-logic.js:108 | Boolean: all non-zero cells in row crossed? |
|
||||
| `getWaitingNumber()` | game-logic.js:120 | Returns the single uncrossed number in row, or null. |
|
||||
| `handleCellClick()` | player-board.jsx:112 | Toggle crossed[row][col]. |
|
||||
| `handleDrawNext()` | master/page.jsx:88 | Pop first number from remaining, add to called. |
|
||||
|
||||
Last reviewed: 2026-04-26
|
||||
@@ -11,7 +11,7 @@ The app auto-deploys from the `master` branch via `.github/workflows/deploy.yml`
|
||||
3. Static export written to `out/` directory
|
||||
4. Pages deployed to `https://{user}.github.io/loto`
|
||||
|
||||
**Note**: basePath is set to `/loto` in production (`next.config.ts:23`).
|
||||
**Note**: basePath is set to `/loto` in production (`next.config.mjs:23`).
|
||||
|
||||
### Manual Deploy (if needed)
|
||||
```bash
|
||||
@@ -55,7 +55,7 @@ Replace `your-machine.example.com` with your actual hostname/IP (must match the
|
||||
npm run dev:codeserver
|
||||
```
|
||||
|
||||
This sets `NEXT_DEV_PROFILE=codeserver`, triggering the code-server config path in `next.config.ts`.
|
||||
This sets `NEXT_DEV_PROFILE=codeserver`, triggering the code-server config path in `next.config.mjs`.
|
||||
|
||||
**4. Access via browser**:
|
||||
Navigate to:
|
||||
@@ -88,7 +88,7 @@ Generates:
|
||||
- `.next/` — Build cache (not needed for deployment)
|
||||
|
||||
### Export Settings
|
||||
- `output: "export"` in `next.config.ts`
|
||||
- `output: "export"` in `next.config.mjs`
|
||||
- No server-side rendering
|
||||
- All pages pre-rendered to HTML + JS bundles
|
||||
|
||||
|
||||
@@ -86,9 +86,9 @@ All pages use `"use client"` because:
|
||||
- Hydration requires client-only state initialization
|
||||
|
||||
Files with `"use client"`:
|
||||
- `app/page.tsx`
|
||||
- `app/master/page.tsx`
|
||||
- `components/player-board.tsx`
|
||||
- `app/page.jsx`
|
||||
- `app/master/page.jsx`
|
||||
- `components/player-board.jsx`
|
||||
|
||||
## Data Flow: Mark a Cell
|
||||
|
||||
|
||||
Reference in new issue
Block a user