build(ts): TypeScript foundation — tsconfig + typecheck in CI (T1)

Adopting TypeScript incrementally (the move off plain JS). Upgraded jsconfig ->
tsconfig with strict + noUncheckedIndexedAccess, but allowJs + checkJs:false so
every existing .js/.jsx keeps resolving and running untouched — only .ts/.tsx
get type-checked. Added 'npm run typecheck' (tsc --noEmit) and wired it into
'npm run ci'. Installed typescript 6 + @types/react/react-dom 19. Client-scoped
(Vite resolves the '@/' paths); the CommonJS server is a separate TS story.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-03 21:33:26 -05:00
parent 47c031f1e4
commit 907a407399
4 changed files with 76 additions and 16 deletions

View File

@ -1,15 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"ignoreDeprecations": "6.0",
"paths": {
"@/*": ["./client/*"]
},
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true
},
"include": ["client/**/*"],
"exclude": ["node_modules", "dist"]
}

44
package-lock.json generated
View File

@ -54,6 +54,8 @@
"@eslint/js": "^9.39.4", "@eslint/js": "^9.39.4",
"@playwright/test": "^1.50.1", "@playwright/test": "^1.50.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"babel-plugin-react-compiler": "^1.0.0", "babel-plugin-react-compiler": "^1.0.0",
@ -65,6 +67,7 @@
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"tailwindcss": "^3.4.14", "tailwindcss": "^3.4.14",
"typescript": "^6.0.3",
"vite": "^5.4.10", "vite": "^5.4.10",
"vite-plugin-pwa": "^1.3.0", "vite-plugin-pwa": "^1.3.0",
"vitest": "^4.1.8" "vitest": "^4.1.8"
@ -6026,6 +6029,26 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/react": {
"version": "19.2.17",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
"integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
}
},
"node_modules/@types/resolve": { "node_modules/@types/resolve": {
"version": "1.20.2", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@ -7198,6 +7221,13 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/data-urls": { "node_modules/data-urls": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
@ -12969,6 +12999,20 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",

View File

@ -10,6 +10,7 @@
"build": "vite build", "build": "vite build",
"check:server": "find server.js db middleware routes services utils -name '*.js' -print0 | xargs -0 -n1 node --check", "check:server": "find server.js db middleware routes services utils -name '*.js' -print0 | xargs -0 -n1 node --check",
"lint": "eslint client", "lint": "eslint client",
"typecheck": "tsc --noEmit -p tsconfig.json",
"check": "npm run check:server && npm run build", "check": "npm run check:server && npm run build",
"test": "node --test tests/*.test.js", "test": "node --test tests/*.test.js",
"test:client": "vitest run", "test:client": "vitest run",
@ -19,7 +20,7 @@
"test:e2e:update": "node e2e/setup/prepare-db.js && playwright test --project=chromium-desktop --project=chromium-mobile --update-snapshots", "test:e2e:update": "node e2e/setup/prepare-db.js && playwright test --project=chromium-desktop --project=chromium-mobile --update-snapshots",
"test:e2e:probe": "node e2e/setup/prepare-db.js && playwright test --project=probe", "test:e2e:probe": "node e2e/setup/prepare-db.js && playwright test --project=probe",
"smoke:prod": "bash scripts/prod-smoke.sh", "smoke:prod": "bash scripts/prod-smoke.sh",
"ci": "npm run lint && npm run check:server && npm run test:all && npm run build", "ci": "npm run lint && npm run typecheck && npm run check:server && npm run test:all && npm run build",
"start": "node server.js" "start": "node server.js"
}, },
"dependencies": { "dependencies": {
@ -68,6 +69,8 @@
"@eslint/js": "^9.39.4", "@eslint/js": "^9.39.4",
"@playwright/test": "^1.50.1", "@playwright/test": "^1.50.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"babel-plugin-react-compiler": "^1.0.0", "babel-plugin-react-compiler": "^1.0.0",
@ -79,6 +82,7 @@
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"tailwindcss": "^3.4.14", "tailwindcss": "^3.4.14",
"typescript": "^6.0.3",
"vite": "^5.4.10", "vite": "^5.4.10",
"vite-plugin-pwa": "^1.3.0", "vite-plugin-pwa": "^1.3.0",
"vitest": "^4.1.8" "vitest": "^4.1.8"

27
tsconfig.json Normal file
View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"paths": { "@/*": ["./client/*"] },
"resolveJsonModule": true,
"esModuleInterop": true,
"isolatedModules": true,
"skipLibCheck": true,
"noEmit": true,
// Gradual adoption: existing .js/.jsx still resolve and run untouched
// (checkJs off = not type-checked), while new/converted .ts/.tsx are checked
// under full strict mode. Convert file-by-file; nothing breaks in between.
"allowJs": true,
"checkJs": false,
"strict": true,
"noUncheckedIndexedAccess": true,
"types": ["vite/client"]
},
"include": ["client/**/*"],
"exclude": ["node_modules", "dist", "client/**/*.test.*"]
}