diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31076ae..cad9e7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,8 +109,6 @@ jobs: exit 1 fi - - # Run all pending migrations against the test database so # the schema is correct before tests start. - name: Run database migrations @@ -214,6 +212,9 @@ jobs: - name: Run ESLint run: npm run lint + - name: Check formatting + run: npm run format:check + # TypeScript type checking without emitting output files. # Catches type errors that ESLint does not catch. - name: Run type check @@ -299,4 +300,4 @@ jobs: refactor perf chore - requireScope: false \ No newline at end of file + requireScope: false \ No newline at end of file diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..a0b30b3 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,6 @@ +# Build output — never format generated files +dist/ +node_modules/ + +# Generated by TypeScript +*.tsbuildinfo \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..7a87c0d --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 80, + "arrowParens": "avoid", + "endOfLine": "lf" +} \ No newline at end of file diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index ef614d2..9304b71 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -4,19 +4,29 @@ import reactHooks from 'eslint-plugin-react-hooks' import reactRefresh from 'eslint-plugin-react-refresh' import tseslint from 'typescript-eslint' import { defineConfig, globalIgnores } from 'eslint/config' +import prettierConfig from 'eslint-config-prettier' export default defineConfig([ globalIgnores(['dist']), + js.configs.recommended, + ...tseslint.configs.recommended, { files: ['**/*.{ts,tsx}'], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs.flat.recommended, - reactRefresh.configs.vite, - ], languageOptions: { globals: globals.browser, }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, }, -]) + // Must be the absolute last object in the flat array to override formatting rules + prettierConfig, +]) \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aab84ca..b411a8b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,9 +18,11 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "eslint": "^10.3.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "prettier": "^3.8.3", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", "vite": "^8.0.12" @@ -1467,6 +1469,22 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", @@ -2363,6 +2381,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index d54e538..831124f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,7 +8,9 @@ "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "format": "prettier --write src", + "format:check": "prettier --check src" }, "dependencies": { "react": "^19.2.6", @@ -21,11 +23,13 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "eslint": "^10.3.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", + "prettier": "^3.8.3", "typescript": "~6.0.2", "typescript-eslint": "^8.59.2", "vite": "^8.0.12" } -} +} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a66b5ef..715ccdf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -24,7 +24,7 @@ function App() { diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..2caec89 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -6,5 +6,5 @@ import App from './App.tsx' createRoot(document.getElementById('root')!).render( - , + )