Upgrade to ESLint 9 + share eslint config across workspaces (#3815)

This commit is contained in:
Chris Estreich 2025-05-21 21:30:21 -07:00 committed by GitHub
parent 3eb3169ad6
commit d5484b52fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 558 additions and 2480 deletions

4
.gitignore vendored
View file

@ -30,10 +30,6 @@ docs/_site/
.env.*
!.env.*.sample
# Local lint config
.eslintrc.local.json
# Logging
logs

View file

@ -28,3 +28,4 @@ else
fi
"$npx_cmd" lint-staged
"$pnpm_cmd" lint

View file

@ -12,7 +12,7 @@ else
pnpm_cmd="pnpm"
fi
"$pnpm_cmd" run lint check-types
"$pnpm_cmd" run check-types
# Check for new changesets.
NEW_CHANGESETS=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ')

View file

@ -1,25 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "import",
"format": ["camelCase", "PascalCase"]
}
],
"@typescript-eslint/semi": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }],
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": ["dist"]
}

View file

@ -1,23 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "import",
"format": ["camelCase", "PascalCase"]
}
],
"@typescript-eslint/semi": "off",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": ["out"]
}

4
e2e/eslint.config.mjs Normal file
View file

@ -0,0 +1,4 @@
import { config } from "@roo-code/config-eslint/base"
/** @type {import("eslint").Linter.Config} */
export default [...config]

View file

@ -2,7 +2,7 @@
"name": "@roo-code/vscode-e2e",
"private": true,
"scripts": {
"lint": "eslint src/**/*.ts --max-warnings=0",
"lint": "eslint **/*.ts --max-warnings=0",
"check-types": "tsc --noEmit",
"format": "prettier --write src",
"test:ci": "pnpm --filter roo-cline build:development && pnpm test:run",
@ -10,6 +10,8 @@
"clean": "rimraf out .turbo"
},
"devDependencies": {
"@roo-code/config-eslint": "workspace:^",
"@roo-code/config-typescript": "workspace:^",
"@roo-code/types": "^1.12.0",
"@types/mocha": "^10.0.10",
"@types/node": "^22.14.1",

View file

@ -23,7 +23,7 @@
"@dotenvx/dotenvx": "^1.34.0",
"@vscode/vsce": "3.3.2",
"esbuild": "^0.25.0",
"eslint": "^8.57.0",
"eslint": "^9.27.0",
"husky": "^9.1.7",
"knip": "^5.44.4",
"lint-staged": "^15.2.11",
@ -38,18 +38,6 @@
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,md}": [
"prettier --write"
],
"src/**/*.{ts,tsx}": [
"npx eslint -c src/.eslintrc.json --max-warnings=0 --fix"
],
"webview-ui/**/*.{ts,tsx}": [
"npx eslint -c webview-ui/.eslintrc.json --max-warnings=0 --fix"
],
"e2e/**/*.{ts,tsx}": [
"npx eslint -c src/.eslintrc.json --max-warnings=0 --fix"
],
"{apps,packages}/**/*.{ts,tsx}": [
"npx eslint --max-warnings=0 --fix"
]
}
}

View file

@ -1,25 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "import",
"format": ["camelCase", "PascalCase"]
}
],
"@typescript-eslint/semi": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }],
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": ["dist"]
}

View file

@ -0,0 +1,4 @@
import { config } from "@roo-code/config-eslint/base"
/** @type {import("eslint").Linter.Config} */
export default [...config]

View file

@ -3,19 +3,22 @@
"description": "ESBuild utilities for Roo Code.",
"private": true,
"type": "module",
"exports": "./dist/index.js",
"main": "./dist/index.js",
"types": "./src/index.ts",
"scripts": {
"lint": "eslint src --ext .ts,.tsx --max-warnings=0",
"lint": "eslint src/**/*.ts --max-warnings=0",
"check-types": "tsc --noEmit",
"test": "vitest --globals --run",
"build": "tsc",
"clean": "rimraf dist .turbo"
},
"devDependencies": {
"@types/node": "^22.15.20",
"vitest": "^3.1.3"
},
"dependencies": {
"zod": "^3.24.2"
},
"devDependencies": {
"@roo-code/config-eslint": "workspace:^",
"@roo-code/config-typescript": "workspace:^",
"@types/node": "^22.15.20",
"vitest": "^3.1.3"
}
}

View file

@ -1,22 +1,8 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@roo-code/config-typescript/base.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022",
"types": ["vitest/globals"],
"outDir": "dist"
"types": ["vitest/globals"]
},
"include": ["src"]
"include": ["src"],
"exclude": ["node_modules"]
}

View file

@ -0,0 +1,44 @@
import js from "@eslint/js"
import eslintConfigPrettier from "eslint-config-prettier"
import turboPlugin from "eslint-plugin-turbo"
import tseslint from "typescript-eslint"
import onlyWarn from "eslint-plugin-only-warn"
/**
* A shared ESLint configuration for the repository.
*
* @type {import("eslint").Linter.Config[]}
* */
export const config = [
js.configs.recommended,
eslintConfigPrettier,
...tseslint.configs.recommended,
{
plugins: {
turbo: turboPlugin,
},
rules: {
"turbo/no-undeclared-env-vars": "off",
},
},
{
plugins: {
onlyWarn,
},
},
{
ignores: ["dist/**"],
},
{
rules: {
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
},
},
]

View file

@ -0,0 +1,19 @@
import pluginNext from "@next/eslint-plugin-next"
import { reactConfig } from "./react.js"
/**
* @type {import("eslint").Linter.Config[]}
*/
export const nextJsConfig = [
...reactConfig,
{
plugins: {
"@next/next": pluginNext,
},
rules: {
...pluginNext.configs.recommended.rules,
...pluginNext.configs["core-web-vitals"].rules,
},
},
]

View file

@ -0,0 +1,22 @@
{
"name": "@roo-code/config-eslint",
"private": true,
"type": "module",
"exports": {
"./base": "./base.js",
"./react": "./react.js",
"./next-js": "./next.js"
},
"devDependencies": {
"@eslint/js": "^9.22.0",
"@next/eslint-plugin-next": "^15.2.1",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-only-warn": "^1.1.0",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-turbo": "^2.4.4",
"globals": "^16.0.0",
"typescript-eslint": "^8.26.0"
}
}

38
packages/config-eslint/react.js vendored Normal file
View file

@ -0,0 +1,38 @@
import js from "@eslint/js"
import eslintConfigPrettier from "eslint-config-prettier"
import typescriptEslint from "typescript-eslint"
import pluginReactHooks from "eslint-plugin-react-hooks"
import pluginReact from "eslint-plugin-react"
import globals from "globals"
import { config } from "./base.js"
/**
* @type {import("eslint").Linter.Config[]}
*/
export const reactConfig = [
...config,
js.configs.recommended,
eslintConfigPrettier,
...typescriptEslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
languageOptions: {
...pluginReact.configs.flat.recommended.languageOptions,
globals: {
...globals.serviceworker,
},
},
},
{
plugins: {
"react-hooks": pluginReactHooks,
},
settings: { react: { version: "detect" } },
rules: {
...pluginReactHooks.configs.recommended.rules,
// React scope no longer necessary with new JSX transform.
"react/react-in-jsx-scope": "off",
},
},
]

View file

@ -0,0 +1,19 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022"
}
}

View file

@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node",
"esModuleInterop": true,
"target": "ES2022",
"lib": ["ES2022", "ESNext.Disposable", "DOM"],
"sourceMap": true,
"strict": true,
"skipLibCheck": true,
"useUnknownInCatchVariables": false
}
}

View file

@ -0,0 +1,12 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"plugins": [{ "name": "next" }],
"module": "ESNext",
"moduleResolution": "bundler",
"allowJs": true,
"jsx": "preserve",
"noEmit": true
}
}

View file

@ -0,0 +1,7 @@
{
"name": "@roo-code/config-typescript",
"private": true,
"publishConfig": {
"access": "public"
}
}

2609
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,25 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "import",
"format": ["camelCase", "PascalCase"]
}
],
"@typescript-eslint/semi": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }],
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": ["out", "dist", "**/*.d.ts", "!roo-code.d.ts"]
}

30
src/eslint.config.mjs Normal file
View file

@ -0,0 +1,30 @@
import { config } from "@roo-code/config-eslint/base"
/** @type {import("eslint").Linter.Config} */
export default [
...config,
{
rules: {
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "off",
},
},
{
files: ["i18n/setup.ts", "utils/tts.ts"],
rules: {
"@typescript-eslint/no-require-imports": "off",
},
},
{
files: ["shared/support-prompt.ts"],
rules: {
"no-prototype-builtins": "off",
},
},
{
files: ["shared/combineApiRequests.ts", "utils/tts.ts"],
rules: {
"no-empty": "off",
},
},
]

View file

@ -318,7 +318,7 @@
}
},
"scripts": {
"lint": "eslint **/*.ts",
"lint": "eslint **/*.ts --max-warnings=0",
"check-types": "tsc --noEmit",
"pretest": "pnpm bundle",
"test": "jest -w=40% && vitest run",
@ -400,6 +400,8 @@
"@changesets/cli": "^2.27.10",
"@changesets/types": "^6.0.0",
"@jest/globals": "^29.7.0",
"@roo-code/config-eslint": "workspace:^",
"@roo-code/config-typescript": "workspace:^",
"@roo-code/build": "workspace:^",
"@types/clone-deep": "^4.0.4",
"@types/debug": "^4.1.12",
@ -416,12 +418,9 @@
"@types/tmp": "^0.2.6",
"@types/turndown": "^5.0.5",
"@types/vscode": "^1.84.0",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.11.0",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "3.3.2",
"esbuild": "^0.25.0",
"eslint": "^8.57.0",
"execa": "^9.5.2",
"glob": "^11.0.1",
"jest": "^29.7.0",

View file

@ -14,9 +14,9 @@ export async function injectEnv(config: string | Record<PropertyKey, any>, notFo
_config = _config.replace(/\$\{env:([\w]+)\}/g, (_, name) => {
// Check if null or undefined
// intentionally using == to match null | undefined
// eslint-disable-next-line eqeqeq
if (process.env[name] == null)
if (process.env[name] == null) {
console.warn(`[injectEnv] env variable ${name} referenced but not found in process.env`)
}
return process.env[name] ?? notFoundValue
})

View file

@ -1,17 +0,0 @@
{
"extends": "react-app",
"ignorePatterns": ["!.storybook"],
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }]
},
"overrides": [
{
"files": ["webview-ui/src/__tests__/utils/command-validation.test.ts"],
"rules": {
"no-template-curly-in-string": "off",
"no-useless-escape": "off"
}
}
]
}

View file

@ -0,0 +1,18 @@
import { reactConfig } from "@roo-code/config-eslint/react"
/** @type {import("eslint").Linter.Config} */
export default [
...reactConfig,
{
rules: {
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "off",
},
},
{
files: ["src/utils/context-mentions.ts", "src/utils/highlighter.ts"],
rules: {
"prefer-const": "off",
},
},
]

View file

@ -3,7 +3,7 @@
"private": true,
"type": "module",
"scripts": {
"lint": "eslint src --ext .ts,.tsx --max-warnings=0",
"lint": "eslint src/**/*.{ts,tsx} --max-warnings=0",
"check-types": "tsc",
"test": "jest -w=40%",
"format": "prettier --write src",
@ -74,6 +74,8 @@
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@roo-code/config-eslint": "workspace:^",
"@roo-code/config-typescript": "workspace:^",
"@roo-code/build": "workspace:^",
"@storybook/addon-essentials": "^8.5.6",
"@storybook/blocks": "^8.5.6",
@ -89,13 +91,7 @@
"@types/shell-quote": "^1.7.5",
"@types/testing-library__jest-dom": "^5.14.5",
"@types/vscode-webview": "^1.57.5",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.3.4",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-storybook": "^0.12.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",