feat: add ESLint configuration and testing infrastructure

Add comprehensive ESLint setup with React and TypeScript support, create basic integration tests for the shutdown utilities, and enhance the Justfile with a new check command that runs all validation steps (cargo check, clippy, tests, and linting).
This commit is contained in:
Ryan Walters
2025-11-03 02:21:35 -06:00
parent 47c23459f1
commit b1ed2434f8
9 changed files with 2248 additions and 6 deletions

63
web/eslint.config.js Normal file
View File

@@ -0,0 +1,63 @@
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
export default tseslint.config(
// Ignore generated files and build outputs
{
ignores: ['dist', 'node_modules', 'src/routeTree.gen.ts', '*.config.js'],
},
// Base configs
js.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
// React plugin configuration
{
files: ['**/*.{ts,tsx}'],
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
ecmaFeatures: {
jsx: true,
},
},
},
settings: {
react: {
version: '19.0',
},
},
rules: {
// React rules
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
// React Refresh
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
// TypeScript overrides
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-explicit-any': 'warn',
// Disable prop-types since we're using TypeScript
'react/prop-types': 'off',
},
}
);

View File

@@ -7,7 +7,8 @@
"start": "vite --port 3000",
"build": "vite build && tsc",
"serve": "vite preview",
"test": "vitest run"
"test": "vitest run",
"lint": "tsc && eslint . --ext .ts,.tsx"
},
"dependencies": {
"@radix-ui/themes": "^3.2.1",
@@ -23,14 +24,20 @@
"recharts": "^3.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.2.0",
"@types/node": "^24.3.3",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.39.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"jsdom": "^26.0.0",
"typescript": "^5.7.2",
"typescript-eslint": "^8.46.2",
"vite": "^6.3.5",
"vitest": "^3.0.5",
"web-vitals": "^4.2.4"

2129
web/pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,7 @@ export class BannerApiClient {
);
}
return response.json();
return (await response.json()) as T;
}
async getHealth(): Promise<HealthResponse> {

View File

@@ -1,6 +1,6 @@
const reportWebVitals = (onPerfEntry?: () => void) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
void import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => {
onCLS(onPerfEntry)
onINP(onPerfEntry)
onFCP(onPerfEntry)

View File

@@ -237,11 +237,11 @@ function App() {
}
// Schedule the next request after the current one completes
timeoutId = setTimeout(fetchData, REFRESH_INTERVAL);
timeoutId = setTimeout(() => void fetchData(), REFRESH_INTERVAL);
};
// Start the first request immediately
fetchData();
void fetchData();
return () => {
if (timeoutId) {

View File

@@ -11,6 +11,7 @@
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noEmit": true,
/* Linting */