guides/eslint-migration.md
This guide describes how to migrate all packages in the Cypress monorepo to the new unified ESLint configuration (@packages/eslint-config).
@cypress/eslint-plugin-dev.@typescript-eslint).During the migration period, the root package.json uses explicit lint-staged patterns to ensure each package gets the correct linting treatment:
npm/grep): Use yarn lint:fix which runs ESLint from the package directory with the correct configeslint --fix which runs from the root with the root config*.{js,jsx,ts,tsx,json,eslintrc,vue} patternThis configuration will be simplified once all packages are migrated to the unified ESLint setup.
For each package in the batch:
.eslintrc, .eslintrc.json, or .eslintrc.js in the package.@cypress/eslint-plugin-dev in package.json (if present).tslint.json and remove tslint dependencies from package.json.eslint.config.ts in the package root:
import baseConfig from '@packages/eslint-config'
export default baseConfig
@typescript-eslint).@packages/eslint-config to devDependencies (use a relative file path if not published to npm).@packages/eslint-config has ESLint as a peer dependency, add eslint: "^9.18.0" to devDependencies.lint-staged section to the package's package.json:
{
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": "eslint --fix"
}
}
npx eslint . --ext .js,.ts,.tsx,.jsx --fix
tsconfig.json that works with the new ESLint config.npx tsc --noEmit to check for TypeScript compilation errors.chore(npm/grep): migrate to @packages/eslint-config and remove legacy eslint-plugin-dev
eslint.config.ts (prefer to upstream to the shared config if possible).@cypress/eslint-plugin-dev from the repo and CI.After all packages are migrated, simplify the lint-staged configuration in root package.json:
{
"lint-staged": {
"**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix",
}
}
package.json/eslint.config.ts.npm/grep, npm/puppeteer, npm/mount-utils, npm/cypress-schematic.eslintrc* fileseslint.config.ts as aboveError: 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.
Solution:
jiti: "^2.4.2" to the package's devDependenciesError: was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject
Solutions:
tsconfig.json that extends the base config:
{
"extends": "../../packages/ts/tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"allowJs": true,
"checkJs": false
},
"include": [
"src/**/*",
"cypress/**/*",
"*.js",
"*.ts",
"*.jsx",
"*.tsx"
],
"exclude": ["node_modules", "dist"]
}
cypress/tsconfig.json includes all test files:
{
"compilerOptions": {
"types": ["cypress"]
},
"include": [
"**/*.ts",
"**/*.js"
]
}
allowDefaultProject: true to ESLint config for problematic files:
{
files: ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx'],
languageOptions: {
parserOptions: {
allowDefaultProject: true,
},
},
}
Error: @cypress/dev/skip-comment rule violations for it.skip() tests
Solution:
eslint-disable-next-line @cypress/dev/skip-comment with proper explanatory comments:
// NOTE: This test is skipped for demonstration purposes
it.skip('first test', () => {})
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)
Root Cause: Pre-commit hook runs ESLint from root directory using root-level config, not package-specific config
Solution:
package.json now includes explicit lint-staged patterns for each package:
"lint-staged": {
"npm/grep/**/*.{js,jsx,ts,tsx}": "yarn lint:fix",
"*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix",
"cli/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix",
"packages/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix",
// ... explicit patterns for each directory
}
yarn lint:fix which runs the lerna command to execute ESLint from the package directoryeslint --fix which runs from the root with the root configBefore ESLint 9.x:
"lint": "eslint . --ext .js,.ts"
After ESLint 9.x:
"lint": "eslint"
ESLint 9.x auto-detects file extensions, so --ext flag is no longer needed.
Required additions to package.json:
{
"devDependencies": {
"@packages/eslint-config": "0.0.0-development",
"eslint": "^9.18.0",
"jiti": "^2.4.2"
}
}
If a package needs custom rules, extend the base config:
import baseConfig from '@packages/eslint-config'
export default [
...baseConfig,
{
files: ['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx'],
rules: {
'no-console': 'off',
'no-restricted-syntax': 'off',
},
},
{
files: ['cypress/**/*.js', 'cypress/**/*.ts'],
languageOptions: {
globals: {
Cypress: 'readonly',
cy: 'readonly',
},
},
},
]
For each package, ensure you've completed:
.eslintrc* fileseslint.config.ts with proper configurationeslint, @packages/eslint-config, jiti)package.jsontsconfig.json that extends base config--ext flag)yarn lint successfullyHappy linting!