|
| 1 | +# ESLint Migration Guide: Monorepo Alignment |
| 2 | + |
| 3 | +## Migration Checklist |
| 4 | + |
| 5 | +### Batch 1: Small npm utilities |
| 6 | +- [x] npm/grep ✅ **COMPLETED** - Uses unified ESLint config with package-specific lint-staged handling |
| 7 | +- [ ] npm/puppeteer |
| 8 | +- [ ] npm/mount-utils |
| 9 | +- [ ] npm/cypress-schematic |
| 10 | + |
| 11 | +### Batch 2: Framework adapters |
| 12 | +- [ ] npm/react |
| 13 | +- [ ] npm/vue |
| 14 | +- [ ] npm/svelte |
| 15 | + |
| 16 | +### Batch 3: Build-related |
| 17 | +- [ ] npm/webpack-batteries-included-preprocessor |
| 18 | +- [ ] npm/webpack-preprocessor |
| 19 | +- [ ] npm/webpack-dev-server |
| 20 | +- [ ] npm/vite-plugin-cypress-esm |
| 21 | +- [ ] npm/vite-dev-server |
| 22 | + |
| 23 | +### Batch 4a: Core packages (part 1) |
| 24 | +- [ ] packages/frontend-shared |
| 25 | +- [ ] packages/icons |
| 26 | +- [ ] packages/launcher |
| 27 | +- [ ] packages/https-proxy |
| 28 | +- [ ] packages/proxy |
| 29 | +- [ ] packages/net-stubbing |
| 30 | +- [ ] packages/driver |
| 31 | +- [ ] packages/rewriter |
| 32 | +- [ ] packages/reporter |
| 33 | +- [ ] packages/server |
| 34 | + |
| 35 | +### Batch 4b: Core packages (part 2) |
| 36 | +- [ ] packages/runner |
| 37 | +- [ ] packages/extension |
| 38 | +- [ ] packages/graphql |
| 39 | +- [ ] packages/network |
| 40 | +- [ ] packages/socket |
| 41 | +- [ ] packages/telemetry |
| 42 | +- [ ] packages/launchpad |
| 43 | +- [ ] packages/errors |
| 44 | +- [ ] packages/data-context |
| 45 | +- [ ] packages/app |
| 46 | + |
| 47 | +### Batch 4c: Core packages (part 3) |
| 48 | +- [ ] packages/config |
| 49 | +- [ ] packages/root |
| 50 | +- [ ] packages/resolve-dist |
| 51 | +- [ ] packages/packherd-require |
| 52 | +- [ ] packages/v8-snapshot-require |
| 53 | +- [ ] packages/web-config |
| 54 | +- [ ] packages/types |
| 55 | +- [ ] packages/example |
| 56 | + |
| 57 | +### Batch 5: Test and script folders |
| 58 | +- [ ] system-tests |
| 59 | +- [ ] scripts |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +This guide describes how to migrate all packages in the Cypress monorepo to the new unified ESLint configuration (`@packages/eslint-config`). |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Why Migrate? |
| 68 | +- **Consistency:** Enforce the same linting rules and plugins across all packages. |
| 69 | +- **Simplicity:** Remove the need for custom plugins like `@cypress/eslint-plugin-dev`. |
| 70 | +- **Maintainability:** Make it easier to update and maintain lint rules. |
| 71 | +- **Modernization:** Replace obsolete TSLint with the appropriate ESLint TypeScript plugin (`@typescript-eslint`). |
| 72 | + |
| 73 | +--- |
| 74 | + |
| 75 | +## Migration Strategy |
| 76 | + |
| 77 | +### 1. **Lint-Staged Configuration** |
| 78 | +During the migration period, the root `package.json` uses explicit lint-staged patterns to ensure each package gets the correct linting treatment: |
| 79 | + |
| 80 | +- **Migrated packages** (like `npm/grep`): Use `yarn lint:fix` which runs ESLint from the package directory with the correct config |
| 81 | +- **Non-migrated packages**: Use `eslint --fix` which runs from the root with the root config |
| 82 | +- **Root files**: Covered by `*.{js,jsx,ts,tsx,json,eslintrc,vue}` pattern |
| 83 | + |
| 84 | +This configuration will be simplified once all packages are migrated to the unified ESLint setup. |
| 85 | + |
| 86 | +### 2. **Batch Packages for Migration** |
| 87 | +- **Batch by directory/type** to keep PRs manageable and reduce risk. |
| 88 | +- **Batch size:** 4–8 packages per PR, grouped by similarity. |
| 89 | + |
| 90 | +### 2. **Migration Steps for Each Package** |
| 91 | + |
| 92 | +For each package in the batch: |
| 93 | + |
| 94 | +1. **Remove old ESLint config and plugin references:** |
| 95 | + - Delete `.eslintrc`, `.eslintrc.json`, or `.eslintrc.js` in the package. |
| 96 | + - Remove any references to `@cypress/eslint-plugin-dev` in `package.json` (if present). |
| 97 | + - **Remove TSLint configs:** Delete `tslint.json` and remove `tslint` dependencies from `package.json`. |
| 98 | +2. **Add a new ESLint config file:** |
| 99 | + - Create `eslint.config.ts` in the package root: |
| 100 | + ```ts |
| 101 | + import baseConfig from '@packages/eslint-config' |
| 102 | + export default baseConfig |
| 103 | + ``` |
| 104 | +3. **Ensure dependencies are up to date:** |
| 105 | + - Remove any package-local ESLint plugins now provided by the shared config. |
| 106 | + - Remove TSLint-related dependencies (the new config includes `@typescript-eslint`). |
| 107 | + - **Add the shared ESLint config as a dev dependency:** |
| 108 | + - In each migrated package, add `@packages/eslint-config` to `devDependencies` (use a relative file path if not published to npm). |
| 109 | + - **Add ESLint as a dev dependency:** |
| 110 | + - Since `@packages/eslint-config` has ESLint as a peer dependency, add `eslint: "^9.18.0"` to `devDependencies`. |
| 111 | +4. **Run lint and autofix:** |
| 112 | + - From the package root, run: |
| 113 | + ``` |
| 114 | + npx eslint . --ext .js,.ts,.tsx,.jsx --fix |
| 115 | + ``` |
| 116 | + - Manually fix any remaining lint errors. |
| 117 | +5. **Verify TypeScript configuration:** |
| 118 | + - Ensure the package has a valid `tsconfig.json` that works with the new ESLint config. |
| 119 | + - Run `npx tsc --noEmit` to check for TypeScript compilation errors. |
| 120 | + - Verify that the new ESLint config can properly parse TypeScript files in the package. |
| 121 | +6. **Run tests for the package** to ensure nothing broke. |
| 122 | +7. **Commit changes** with a clear message, e.g.: |
| 123 | + ``` |
| 124 | + chore(npm/grep): migrate to @packages/eslint-config and remove legacy eslint-plugin-dev |
| 125 | + ``` |
| 126 | +
|
| 127 | +### 3. **Open a PR for Each Batch** |
| 128 | +- Keep each migration PR focused (one batch per PR). |
| 129 | +- List all affected packages in the PR description. |
| 130 | +- Include a checklist for each package: |
| 131 | + - [ ] Removed old ESLint config |
| 132 | + - [ ] Added new config |
| 133 | + - [ ] Ran lint and fixed errors |
| 134 | + - [ ] Ran tests |
| 135 | +
|
| 136 | +### 4. **Document Issues or Gaps** |
| 137 | +- If you hit any missing rules or plugin gaps, note them for follow-up. |
| 138 | +- If a package needs a custom override, add it in a local `eslint.config.ts` (prefer to upstream to the shared config if possible). |
| 139 | +
|
| 140 | +### 5. **Deprecate and Remove Old Plugin** |
| 141 | +- Once all packages are migrated, remove `@cypress/eslint-plugin-dev` from the repo and CI. |
| 142 | +
|
| 143 | +### 6. **Simplify Lint-Staged Configuration** |
| 144 | +After all packages are migrated, simplify the lint-staged configuration in root `package.json`: |
| 145 | +
|
| 146 | +```json |
| 147 | +{ |
| 148 | + "lint-staged": { |
| 149 | + "**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", |
| 150 | + "*workflows.yml": "node scripts/format-workflow-file.js" |
| 151 | + } |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +### 7. **Update Lerna/Monorepo Config** |
| 156 | +- Ensure all packages reference the new config in their `package.json`/`eslint.config.ts`. |
| 157 | +- Update documentation and developer onboarding guides. |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## Example: Migrating a Batch |
| 162 | + |
| 163 | +1. **Select batch:** e.g., `npm/grep`, `npm/puppeteer`, `npm/mount-utils`, `npm/cypress-schematic` |
| 164 | +2. **For each package:** |
| 165 | + - Remove `.eslintrc*` files |
| 166 | + - Add `eslint.config.ts` as above |
| 167 | + - Remove local plugin deps |
| 168 | + - Run lint and fix errors |
| 169 | + - Run tests |
| 170 | +3. **Commit and open PR** |
| 171 | + |
| 172 | +--- |
| 173 | + |
| 174 | +## Troubleshooting Guide |
| 175 | + |
| 176 | +### Common Issues and Solutions |
| 177 | + |
| 178 | +#### 1. **Jiti Version Compatibility Error** |
| 179 | +**Error:** `Error: You are using an outdated version of the 'jiti' library. Please update to the latest version of 'jiti' to ensure compatibility and access to the latest features.` |
| 180 | + |
| 181 | +**Solution:** |
| 182 | +- Add `jiti: "^2.4.2"` to the package's `devDependencies` |
| 183 | +- ESLint 9.x requires jiti >= 2.2.0, but monorepo dependencies might provide older versions |
| 184 | + |
| 185 | +#### 2. **TypeScript Project Service Errors** |
| 186 | +**Error:** `was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject` |
| 187 | + |
| 188 | +**Solutions:** |
| 189 | +- **Create/update `tsconfig.json`** that extends the base config: |
| 190 | + ```json |
| 191 | + { |
| 192 | + "extends": "../../packages/ts/tsconfig.json", |
| 193 | + "compilerOptions": { |
| 194 | + "esModuleInterop": true, |
| 195 | + "allowJs": true, |
| 196 | + "checkJs": false |
| 197 | + }, |
| 198 | + "include": [ |
| 199 | + "src/**/*", |
| 200 | + "cypress/**/*", |
| 201 | + "*.js", |
| 202 | + "*.ts", |
| 203 | + "*.jsx", |
| 204 | + "*.tsx" |
| 205 | + ], |
| 206 | + "exclude": ["node_modules", "dist"] |
| 207 | + } |
| 208 | + ``` |
| 209 | +- **For Cypress packages**, ensure `cypress/tsconfig.json` includes all test files: |
| 210 | + ```json |
| 211 | + { |
| 212 | + "compilerOptions": { |
| 213 | + "types": ["cypress"] |
| 214 | + }, |
| 215 | + "include": [ |
| 216 | + "**/*.ts", |
| 217 | + "**/*.js" |
| 218 | + ] |
| 219 | + } |
| 220 | + ``` |
| 221 | +- **Add `allowDefaultProject: true`** to ESLint config for problematic files: |
| 222 | + ```ts |
| 223 | + { |
| 224 | + files: ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx'], |
| 225 | + languageOptions: { |
| 226 | + parserOptions: { |
| 227 | + allowDefaultProject: true, |
| 228 | + }, |
| 229 | + }, |
| 230 | + } |
| 231 | + ``` |
| 232 | + |
| 233 | +#### 3. **Skip Comment Rule Violations** |
| 234 | +**Error:** `@cypress/dev/skip-comment` rule violations for `it.skip()` tests |
| 235 | + |
| 236 | +**Solution:** |
| 237 | +- Replace `eslint-disable-next-line @cypress/dev/skip-comment` with proper explanatory comments: |
| 238 | + ```js |
| 239 | + // NOTE: This test is skipped for demonstration purposes |
| 240 | + it.skip('first test', () => {}) |
| 241 | + ``` |
| 242 | + |
| 243 | +#### 4. **Rule Mismatches in Pre-commit Hook** |
| 244 | +**Error:** ESLint rule violations that don't match the package's ESLint configuration (e.g., `Unexpected console statement` when `no-console` is off in package config) |
| 245 | + |
| 246 | +**Root Cause:** Pre-commit hook runs ESLint from root directory using root-level config, not package-specific config |
| 247 | + |
| 248 | +**Solution:** |
| 249 | +- The root `package.json` now includes explicit lint-staged patterns for each package: |
| 250 | + ```json |
| 251 | + "lint-staged": { |
| 252 | + "npm/grep/**/*.{js,jsx,ts,tsx}": "yarn lint:fix", |
| 253 | + "*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", |
| 254 | + "cli/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", |
| 255 | + "packages/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", |
| 256 | + // ... explicit patterns for each directory |
| 257 | + } |
| 258 | + ``` |
| 259 | +- **For migrated packages:** Use `yarn lint:fix` which runs the lerna command to execute ESLint from the package directory |
| 260 | +- **For non-migrated packages:** Use `eslint --fix` which runs from the root with the root config |
| 261 | +- **Note:** This verbose configuration is temporary during migration and will be simplified once all packages are migrated |
| 262 | + |
| 263 | +#### 5. **ESLint Script Changes** |
| 264 | +**Before ESLint 9.x:** |
| 265 | +```json |
| 266 | +"lint": "eslint . --ext .js,.ts" |
| 267 | +``` |
| 268 | + |
| 269 | +**After ESLint 9.x:** |
| 270 | +```json |
| 271 | +"lint": "eslint" |
| 272 | +``` |
| 273 | +ESLint 9.x auto-detects file extensions, so `--ext` flag is no longer needed. |
| 274 | + |
| 275 | +#### 6. **Package Dependencies** |
| 276 | +**Required additions to `package.json`:** |
| 277 | +```json |
| 278 | +{ |
| 279 | + "devDependencies": { |
| 280 | + "@packages/eslint-config": "0.0.0-development", |
| 281 | + "eslint": "^9.18.0", |
| 282 | + "jiti": "^2.4.2" |
| 283 | + } |
| 284 | +} |
| 285 | +``` |
| 286 | + |
| 287 | +#### 7. **Custom Package Rules** |
| 288 | +If a package needs custom rules, extend the base config: |
| 289 | +```ts |
| 290 | +import baseConfig from '@packages/eslint-config' |
| 291 | + |
| 292 | +export default [ |
| 293 | + ...baseConfig, |
| 294 | + { |
| 295 | + files: ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx'], |
| 296 | + rules: { |
| 297 | + 'no-console': 'off', |
| 298 | + 'no-restricted-syntax': 'off', |
| 299 | + }, |
| 300 | + }, |
| 301 | + { |
| 302 | + files: ['cypress/**/*.js', 'cypress/**/*.ts'], |
| 303 | + languageOptions: { |
| 304 | + globals: { |
| 305 | + Cypress: 'readonly', |
| 306 | + cy: 'readonly', |
| 307 | + }, |
| 308 | + }, |
| 309 | + }, |
| 310 | +] |
| 311 | +``` |
| 312 | + |
| 313 | +### Migration Checklist Template |
| 314 | + |
| 315 | +For each package, ensure you've completed: |
| 316 | + |
| 317 | +- [ ] Removed `.eslintrc*` files |
| 318 | +- [ ] Created `eslint.config.ts` with proper configuration |
| 319 | +- [ ] Added required dependencies (`eslint`, `@packages/eslint-config`, `jiti`) |
| 320 | +- [ ] Created/updated `tsconfig.json` that extends base config |
| 321 | +- [ ] Updated ESLint scripts (removed `--ext` flag) |
| 322 | +- [ ] Ran `yarn lint` successfully |
| 323 | +- [ ] Ran tests to ensure nothing broke |
| 324 | + |
| 325 | +--- |
| 326 | + |
| 327 | +## Tips |
| 328 | +- Use a tracking issue or project board to coordinate and document progress. |
| 329 | +- If a package is especially noisy, consider splitting it into its own PR. |
| 330 | +- Communicate with the team about the migration timeline and process. |
| 331 | +- **Test the pre-commit hook** before pushing to ensure lint-staged works correctly. |
| 332 | + |
| 333 | +## Recent Updates |
| 334 | +- **Fixed lint-staged configuration** to properly handle migrated vs non-migrated packages |
| 335 | +- **Added explicit directory patterns** to ensure complete coverage during migration period |
| 336 | + |
| 337 | +--- |
| 338 | + |
| 339 | +**Happy linting!** |
0 commit comments