Migrate to ESLint flat config and modernize tooling#13
Migrate to ESLint flat config and modernize tooling#13amcdnl wants to merge 2 commits intomain-react-queryfrom
Conversation
Major dependency updates: - React 18 → 19 (with native <title> tag support, replacing react-helmet-async) - ESLint 8 → 9 (migrated from legacy .eslintrc.cjs to flat config eslint.config.js) - TypeScript 5.3 → 5.9 - Vite 7.1 → 7.3 - Vitest 3.2 → 4.0 - Storybook 9 alpha/mixed → 9.1.19 stable - Tailwind CSS 4.1 → 4.2 - Node.js 18 → 22 (.nvmrc) Breaking change fixes: - react-error-boundary v6: error type changed from Error to unknown - Storybook 9: consolidated packages (@storybook/blocks → @storybook/addon-docs/blocks, @storybook/theming → storybook/theming, @storybook/manager-api → storybook/manager-api) - Storybook 9: import Preview type from @storybook/react-vite instead of @storybook/react - Storybook 9: removed merged addons (essentials, storysource, interactions, mdx-gfm) - ESLint flat config: removed vite-plugin-eslint (abandoned), @typescript-eslint/parser, @typescript-eslint/eslint-plugin (replaced by typescript-eslint v8) - Prettier: renamed deprecated jsxBracketSameLine → bracketSameLine - Prettier: renamed deprecated --loglevel → --log-level - TSConfig: moduleResolution Node → Bundler - Husky: updated prepare script for v9 format - Removed unused PostCSS plugins (autoprefixer, postcss-nested, postcss-preset-env) https://claude.ai/code/session_01Wa78iFDCc7pT4C1RjMY2EN
Remove deprecated shebang and husky.sh sourcing that will fail in Husky v10. https://claude.ai/code/session_01Wa78iFDCc7pT4C1RjMY2EN
There was a problem hiding this comment.
Pull request overview
This PR comprehensively modernizes the project's development tooling by migrating from legacy ESLint configuration to the new flat config system and updating numerous dependencies to their latest versions. The changes simplify the build configuration, remove deprecated tooling, and adopt React 19's native metadata handling.
Changes:
- Migrated ESLint from
.eslintrc.cjsto flat config format (eslint.config.js) with consolidated ignore patterns - Updated major dependencies: React 19.1.0, ESLint 9.39.3, Vitest 4.0.18,
@hookform/resolvers5.2.2, and TypeScript 5.9.3 - Replaced
react-helmet-asyncwith React 19's native<title>tag hoisting capability - Simplified Vite configuration by removing
vite-plugin-eslintand consolidating CSS module configuration - Updated Storybook to 9.1.19 with new import paths and simplified addon configuration
- Modernized TypeScript configuration with ES2022 target and "Bundler" module resolution
- Updated Husky to v9 with simplified pre-commit hook
- Improved error handling with proper type guards for
unknownerror types
Reviewed changes
Copilot reviewed 26 out of 28 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| eslint.config.js | New ESLint flat config with TypeScript, React, and Storybook plugins |
| .eslintrc.cjs | Removed legacy ESLint configuration file |
| .eslintignore | Removed as ignore patterns moved to flat config |
| package.json | Updated dependencies (React 19, ESLint 9, Vitest 4, etc.) and simplified lint scripts |
| vite.config.ts | Removed ESLint plugin, simplified configuration structure, consolidated CSS module config |
| tsconfig.json | Updated to ES2022 target and Bundler module resolution |
| tsconfig.node.json | Updated to Bundler module resolution |
| src/index.tsx | Removed react-helmet-async, replaced with native <title> tag |
| src/App/NotFound/NotFound.tsx | Replaced Helmet with native <title> tag |
| src/shared/utils/ErrorBoundary/ErrorBoundary.tsx | Changed error type from Error to unknown (safer typing) |
| src/shared/utils/ErrorBoundary/ErrorComponent.tsx | Added type guard for error instanceof Error |
| .storybook/main.ts | Removed addon-essentials, addon-storysource, addon-interactions; kept addon-themes and addon-docs |
| .storybook/preview.tsx | Updated imports to new Storybook package paths |
| .storybook/manager.ts | Updated imports to use storybook/manager-api |
| .storybook/theme.ts | Updated imports to use storybook/theming |
| src/assets/**.mdx | Updated imports to @storybook/addon-docs/blocks |
| .prettierrc.json | Replaced deprecated jsxBracketSameLine with bracketSameLine |
| .husky/pre-commit | Simplified to Husky 9 format |
| .nvmrc | Updated Node version to v22.22.0 |
| Multiple files | Code formatting and cleanup (removed blank lines, simplified JSX) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| </HelmetProvider> | ||
| <ThemeProvider theme={theme}> | ||
| <ErrorBoundary> | ||
| <title>Starter</title> |
There was a problem hiding this comment.
The <title> tag should be placed in the document head, not as a child within the component tree. React 19 does support rendering metadata tags like <title> in components and will automatically hoist them to the document head, but placing it inside ErrorBoundary could cause issues if the error boundary catches an error before the title is rendered. Consider placing this at a higher level in the component tree (e.g., directly under React.StrictMode) or using a more robust solution for managing document metadata across routes.
| js.configs.recommended, | ||
| ...tseslint.configs.recommended, | ||
| { | ||
| files: ['src/**/*.{js,ts,tsx}'], |
There was a problem hiding this comment.
The ESLint configuration only targets files in 'src/**/*.{js,ts,tsx}', but there are TypeScript configuration files in the .storybook directory and root directory (like vite.config.ts, tsconfig.json) that won't be linted. Consider adding a separate configuration block to include these files, or explicitly add them to the files pattern if linting them is desired.
| files: ['src/**/*.{js,ts,tsx}'], | |
| files: ['src/**/*.{js,ts,tsx}', '.storybook/**/*.{js,ts,tsx}', 'vite.config.ts', 'tsconfig.json'], |
| addons: [ | ||
| '@storybook/addon-storysource', | ||
| '@storybook/addon-essentials', | ||
| '@storybook/addon-themes', |
There was a problem hiding this comment.
The removal of '@storybook/addon-essentials' removes several commonly used addons (actions, controls, docs, viewport, backgrounds, toolbars, measure, outline). While '@storybook/addon-docs' is explicitly added, other essential addons like actions and controls are no longer included. Verify that this is intentional and that the removed addons are not needed for the project's Storybook stories.
| '@storybook/addon-themes', | |
| '@storybook/addon-themes', | |
| '@storybook/addon-essentials', |
| "dependencies": { | ||
| "@hookform/resolvers": "^3.10.0", | ||
| "@tanstack/react-query": "^5.87.1", | ||
| "@hookform/resolvers": "^5.2.2", | ||
| "@tanstack/react-query": "^5.90.21", | ||
| "classnames": "^2.5.1", | ||
| "date-fns": "^4.1.0", | ||
| "motion": "^12.23.12", | ||
| "nanoid": "^5.1.5", | ||
| "reablocks": "^9.2.1", | ||
| "react": "18.3.1", | ||
| "react-dom": "18.3.1", | ||
| "react-error-boundary": "^6.0.0", | ||
| "react-helmet-async": "^2.0.5", | ||
| "react-hook-form": "^7.62.0", | ||
| "react-router": "^7.8.2", | ||
| "motion": "^12.34.3", | ||
| "nanoid": "^5.1.6", | ||
| "reablocks": "^9.4.1", | ||
| "react": "^19.1.0", | ||
| "react-dom": "^19.1.0", | ||
| "react-error-boundary": "^6.1.1", | ||
| "react-hook-form": "^7.71.2", | ||
| "react-router": "^7.13.1", | ||
| "react-use": "^17.6.0", | ||
| "yup": "^1.7.0" | ||
| "yup": "^1.7.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@7nohe/openapi-react-query-codegen": "^1.6.2", | ||
| "@eslint/js": "^10.0.0", | ||
| "@storybook/addon-actions": "^9.0.8", | ||
| "@storybook/addon-docs": "^9.1.5", | ||
| "@storybook/addon-essentials": "^9.0.0-alpha.12", | ||
| "@storybook/addon-interactions": "^9.0.0-alpha.10", | ||
| "@storybook/addon-mdx-gfm": "^9.0.0-alpha.13", | ||
| "@storybook/addon-onboarding": "^9.1.5", | ||
| "@storybook/addon-storysource": "^9.0.0-alpha.14", | ||
| "@storybook/addon-themes": "^9.1.5", | ||
| "@storybook/blocks": "^9.0.0-alpha.17", | ||
| "@storybook/manager-api": "^9.0.0-alpha.1", | ||
| "@storybook/preview-api": "^9.0.0-alpha.1", | ||
| "@storybook/react": "^9.1.5", | ||
| "@storybook/react-vite": "^9.1.5", | ||
| "@storybook/test": "^9.0.0-alpha.2", | ||
| "@storybook/theming": "^9.0.0-alpha.1", | ||
| "@tailwindcss/postcss": "^4.1.13", | ||
| "@types/eslint": "^9.6.1", | ||
| "@types/react": "^18.3.24", | ||
| "@types/react-dom": "^18.3.7", | ||
| "@typescript-eslint/eslint-plugin": "^6.21.0", | ||
| "@typescript-eslint/parser": "^6.21.0", | ||
| "@vitejs/plugin-react": "^5.0.2", | ||
| "@vitest/coverage-v8": "^3.2.4", | ||
| "autoprefixer": "^10.4.21", | ||
| "eslint": "^8.57.1", | ||
| "eslint-config-prettier": "^10.1.5", | ||
| "@eslint/js": "^9.39.3", | ||
| "@storybook/addon-docs": "^9.1.19", | ||
| "@storybook/addon-themes": "^9.1.19", | ||
| "@storybook/react-vite": "^9.1.19", | ||
| "@tailwindcss/postcss": "^4.2.1", | ||
| "@types/react": "^19.1.0", | ||
| "@types/react-dom": "^19.1.0", | ||
| "@vitejs/plugin-react": "^5.1.4", | ||
| "@vitest/coverage-v8": "^4.0.18", | ||
| "eslint": "^9.39.3", | ||
| "eslint-config-prettier": "^10.1.8", | ||
| "eslint-plugin-react": "^7.37.5", | ||
| "eslint-plugin-react-hooks": "^5.2.0", | ||
| "eslint-plugin-storybook": "^9.1.5", | ||
| "globals": "^16.3.0", | ||
| "eslint-plugin-storybook": "^9.1.19", | ||
| "globals": "^17.3.0", | ||
| "husky": "^9.1.7", | ||
| "jsdom": "^26.1.0", | ||
| "lint-staged": "^16.1.6", | ||
| "jsdom": "^28.1.0", | ||
| "lint-staged": "^16.2.7", | ||
| "postcss": "^8.5.6", | ||
| "postcss-nested": "^7.0.2", | ||
| "postcss-preset-env": "^10.3.1", | ||
| "prettier": "^3.6.2", | ||
| "storybook": "^9.1.5", | ||
| "tailwindcss": "^4.1.13", | ||
| "typescript": "^5.3.3", | ||
| "typescript-eslint": "^8.43.0", | ||
| "vite": "^7.1.5", | ||
| "vite-plugin-checker": "^0.10.3", | ||
| "vite-plugin-eslint": "^1.8.1", | ||
| "prettier": "^3.8.1", | ||
| "storybook": "^9.1.19", | ||
| "tailwindcss": "^4.2.1", | ||
| "typescript": "^5.9.3", | ||
| "typescript-eslint": "^8.56.1", | ||
| "vite": "^7.3.1", | ||
| "vite-plugin-checker": "^0.12.0", | ||
| "vite-plugin-svgr": "^4.5.0", | ||
| "vite-tsconfig-paths": "^5.1.4", | ||
| "vitest": "^3.2.4" | ||
| "vite-tsconfig-paths": "^6.1.1", | ||
| "vitest": "^4.0.18" |
There was a problem hiding this comment.
Multiple major version updates are included in this PR (React 18→19, @hookform/resolvers 3→5, Vitest 3→4, ESLint 8→9). While these updates are valuable for modernization, each major version typically includes breaking changes. Ensure that:
- React 19 breaking changes have been tested (e.g., new ref handling, changes to Context, etc.)
@hookform/resolversv5 breaking changes are compatible with the existing form implementations- Vitest 4 test configurations and assertions still work correctly
- The codebase has been thoroughly tested after these updates
Consider splitting major version updates into separate PRs to make it easier to identify and fix any issues that arise from specific version bumps.
| tsconfigPaths(), | ||
| react(), | ||
| checker({ | ||
| typescript: true, |
There was a problem hiding this comment.
The vite-plugin-eslint has been removed from the Vite configuration, which means ESLint errors will no longer be displayed in the browser during development. While this simplifies the configuration and improves build performance, developers will need to rely on their IDE or running the lint command separately to catch linting issues. Ensure this change is communicated to the team and that the development workflow accounts for this change.
| typescript: true, | |
| typescript: true, | |
| eslint: { | |
| lintCommand: 'eslint "./src/**/*.{ts,tsx,js,jsx}"', | |
| }, |
Summary
This PR modernizes the project's linting and build tooling by migrating from the legacy ESLint configuration format to the new flat config system, removing unnecessary dependencies, and updating to newer versions of core libraries.
Key Changes
ESLint Migration
.eslintrc.cjsto neweslint.config.jsflat config formatvite-plugin-eslintfrom Vite configuration (linting now handled separately)src/directory directly--extflagsDependency Updates
Configuration Cleanup
react-helmet-asyncdependency and replaced with native<title>elementsjsxBracketSameLine→bracketSameLinestorybook/manager-api,storybook/theming)@storybook/addon-docs/blockstargetES2022,moduleResolutionto "Bundler"Code Formatting
Notable Implementation Details
tseslint.config()for better TypeScript supportnpx lint-staged.eslintignorefile removed as ignore patterns are now ineslint.config.jshttps://claude.ai/code/session_01Wa78iFDCc7pT4C1RjMY2EN