diff --git a/package-lock.json b/package-lock.json index 685e860de..116422506 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13593,13 +13593,13 @@ "dependencies": { "@eslint/js": "^9.39.2", "@stylistic/eslint-plugin": "^5.7.0", - "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^17.0.0", "typescript-eslint": "^8.53.1" }, "devDependencies": { + "eslint": "^9.39.2", "typescript": "^5.8.3" } }, diff --git a/packages/eslint-config/eslint.config.js b/packages/eslint-config/eslint.config.js new file mode 100644 index 000000000..93359d83e --- /dev/null +++ b/packages/eslint-config/eslint.config.js @@ -0,0 +1,20 @@ +import { defineConfig } from "eslint/config"; + +import { baseConfigs } from "./src/baseConfigs.ts"; +import { tsConfigs } from "./src/tsConfigs.ts"; +import { nodeLanguageOptionsConfig } from "./src/languageOptionsConfigs.ts"; +import { stylisticConfigs } from "./src/stylisticConfigs.ts"; + +export default defineConfig([ + ...baseConfigs, + ...tsConfigs, + nodeLanguageOptionsConfig, + ...stylisticConfigs, + { + languageOptions: { + parserOptions: { + tsConfigRootDir: import.meta.dirname, + }, + }, + }, +]); diff --git a/packages/eslint-config/eslint.config.mjs b/packages/eslint-config/eslint.config.mjs deleted file mode 100644 index ee6a34479..000000000 --- a/packages/eslint-config/eslint.config.mjs +++ /dev/null @@ -1,143 +0,0 @@ -import js from "@eslint/js"; -import globals from "globals"; -import reactHooks from "eslint-plugin-react-hooks"; -import tseslint from "typescript-eslint"; -import stylistic from "@stylistic/eslint-plugin"; - -/** - * Base ESLint configuration with strict and stylistic rules - */ -export function createConfig({ - files = ["**/*.{ts,tsx}"], - languageOptions = {}, - react = false, - project = true, - tsconfigRootDir, -} = {}) { - const configs = [ - { ignores: ["dist", "node_modules", "**/*.d.ts"] }, - js.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - { - files, - languageOptions: (() => { - const mergedGlobals = { - ...globals.node, - ...globals.es2021, - ...(languageOptions.globals || {}), - }; - const mergedParserOptions = { - project, - ...(tsconfigRootDir && { tsconfigRootDir }), - ...(languageOptions.parserOptions || {}), - }; - const otherLanguageOptions = Object.fromEntries( - Object.entries(languageOptions).filter( - ([key]) => key !== "globals" && key !== "parserOptions", - ), - ); - - return { - ecmaVersion: 2020, - sourceType: "module", - globals: mergedGlobals, - parserOptions: mergedParserOptions, - ...otherLanguageOptions, - }; - })(), - plugins: { - "@stylistic": stylistic, - }, - rules: { - // TypeScript strict rules - "@typescript-eslint/no-unused-vars": [ - "error", - { - argsIgnorePattern: "^_", - varsIgnorePattern: "^_", - }, - ], - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-floating-promises": "error", - "@typescript-eslint/no-misused-promises": "error", - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - - // Stylistic rules - "@stylistic/quotes": ["error", "double", { avoidEscape: true }], - "@stylistic/semi": ["error", "always"], - "@stylistic/comma-dangle": ["error", "always-multiline"], - "@stylistic/indent": ["error", 2, { SwitchCase: 1 }], - "@stylistic/object-curly-spacing": ["error", "always"], - "@stylistic/array-bracket-spacing": ["error", "never"], - "@stylistic/comma-spacing": ["error", { before: false, after: true }], - "@stylistic/key-spacing": [ - "error", - { beforeColon: false, afterColon: true }, - ], - "@stylistic/space-before-blocks": ["error", "always"], - "@stylistic/space-before-function-paren": [ - "error", - { anonymous: "always", named: "never", asyncArrow: "always" }, - ], - "@stylistic/space-infix-ops": "error", - "@stylistic/arrow-spacing": ["error", { before: true, after: true }], - "@stylistic/no-trailing-spaces": "error", - "@stylistic/eol-last": ["error", "always"], - "@stylistic/max-len": [ - "warn", - { code: 120, ignoreUrls: true, ignoreStrings: true }, - ], - }, - }, - ]; - - if (react) { - configs.push({ - files, - plugins: { - "react-hooks": reactHooks, - }, - rules: { - ...reactHooks.configs.recommended.rules, - }, - }); - } - - return configs; -} - -/** - * Browser/React configuration - */ -export function browser(project = true, tsconfigRootDir) { - return createConfig({ - files: ["**/*.{ts,tsx}"], - languageOptions: { - globals: { - ...globals.browser, - }, - }, - react: true, - project, - tsconfigRootDir, - }); -} - -/** - * Node.js configuration - */ -export function node(project = true, tsconfigRootDir) { - return createConfig({ - files: ["**/*.{ts,tsx}"], - languageOptions: { - globals: { - ...globals.node, - }, - }, - react: false, - project, - tsconfigRootDir, - }); -} diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index ec01ffb4e..6eed5e728 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -3,22 +3,24 @@ "version": "1.0.0", "private": true, "description": "Shared ESLint configuration for Probo monorepo", - "main": "eslint.config.mjs", + "main": "./src/index.ts", "type": "module", - "scripts": {}, + "scripts": { + "lint": "eslint src" + }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@eslint/js": "^9.39.2", "@stylistic/eslint-plugin": "^5.7.0", - "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^17.0.0", "typescript-eslint": "^8.53.1" }, "devDependencies": { + "eslint": "^9.39.2", "typescript": "^5.8.3" } } diff --git a/packages/eslint-config/baseConfigs.ts b/packages/eslint-config/src/baseConfigs.ts similarity index 59% rename from packages/eslint-config/baseConfigs.ts rename to packages/eslint-config/src/baseConfigs.ts index cb8039ae2..d12437a08 100644 --- a/packages/eslint-config/baseConfigs.ts +++ b/packages/eslint-config/src/baseConfigs.ts @@ -1,6 +1,7 @@ -import * as js from "@eslint/js"; +import js from "@eslint/js"; export const baseConfigs = [ { ignores: ["dist", "node_modules", "**/*.d.ts"] }, + { files: ["**/*.js", "**/*.ts", "**/*.tsx"] }, js.configs.recommended, ]; diff --git a/packages/eslint-config/src/index.ts b/packages/eslint-config/src/index.ts new file mode 100644 index 000000000..f761ad272 --- /dev/null +++ b/packages/eslint-config/src/index.ts @@ -0,0 +1,16 @@ +import { baseConfigs } from "./baseConfigs"; +import { browserLanguageOptionsConfig, nodeLanguageOptionsConfig } from "./languageOptionsConfigs"; +import { tsConfigs } from "./tsConfigs"; +import { reactConfigs } from "./reactConfigs"; +import { stylisticConfigs } from "./stylisticConfigs"; + +export const configs = { + base: baseConfigs, + ts: tsConfigs, + languageOptions: { + browser: browserLanguageOptionsConfig, + node: nodeLanguageOptionsConfig, + }, + react: reactConfigs, + stylistic: stylisticConfigs, +}; diff --git a/packages/eslint-config/languageOptionsConfigs.ts b/packages/eslint-config/src/languageOptionsConfigs.ts similarity index 76% rename from packages/eslint-config/languageOptionsConfigs.ts rename to packages/eslint-config/src/languageOptionsConfigs.ts index 0e33fd3b0..a65546403 100644 --- a/packages/eslint-config/languageOptionsConfigs.ts +++ b/packages/eslint-config/src/languageOptionsConfigs.ts @@ -1,15 +1,16 @@ -import { browser, es2022, es2023, node } from "globals" +import * as globals from "globals"; export const browserLanguageOptionsConfig = { languageOptions: { // Same as the ones we use in our tsconfig compilerOptions.lib for browser ecmaVersion: 2022, globals: { - ...browser, - ...es2022, + ...globals.browser, + ...globals.es2022, }, sourceType: "module", parserOptions: { + projectService: true, ecmaFeatures: { impliedStrict: true, }, @@ -22,11 +23,12 @@ export const nodeLanguageOptionsConfig = { // Same as the ones we use in our tsconfig compilerOptions.lib for node ecmaVersion: 2023, globals: { - ...node, - ...es2023, + ...globals.node, + ...globals.es2023, }, sourceType: "module", parserOptions: { + projectService: true, ecmaFeatures: { impliedStrict: true, }, diff --git a/packages/eslint-config/reactConfigs.ts b/packages/eslint-config/src/reactConfigs.ts similarity index 100% rename from packages/eslint-config/reactConfigs.ts rename to packages/eslint-config/src/reactConfigs.ts diff --git a/packages/eslint-config/stylisticConfigs.ts b/packages/eslint-config/src/stylisticConfigs.ts similarity index 89% rename from packages/eslint-config/stylisticConfigs.ts rename to packages/eslint-config/src/stylisticConfigs.ts index cc4f7de67..0c2918974 100644 --- a/packages/eslint-config/stylisticConfigs.ts +++ b/packages/eslint-config/src/stylisticConfigs.ts @@ -1,4 +1,5 @@ import stylistic from "@stylistic/eslint-plugin"; +import { type Linter } from "eslint"; export const stylisticConfigs = [ stylistic.configs.customize({ @@ -20,5 +21,5 @@ export const stylisticConfigs = [ { code: 120, ignoreUrls: true, ignoreStrings: true }, ], }, - }, + } satisfies Linter.Config, ]; diff --git a/packages/eslint-config/src/tsConfigs.ts b/packages/eslint-config/src/tsConfigs.ts new file mode 100644 index 000000000..9688eeafd --- /dev/null +++ b/packages/eslint-config/src/tsConfigs.ts @@ -0,0 +1,23 @@ +import { type Linter } from "eslint"; +import tseslint from "typescript-eslint"; + +export const tsConfigs = [ + ...tseslint.configs.recommendedTypeChecked, + { + files: ["**/*.{ts,tsx}"], + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { + args: "all", + argsIgnorePattern: "^_", + caughtErrors: "all", + caughtErrorsIgnorePattern: "^_", + destructuredArrayIgnorePattern: "^_", + varsIgnorePattern: "^_", + ignoreRestSiblings: true, + }, + ], + }, + } satisfies Linter.Config, +]; diff --git a/packages/eslint-config/tsConfigs.ts b/packages/eslint-config/tsConfigs.ts deleted file mode 100644 index 867dfb3a8..000000000 --- a/packages/eslint-config/tsConfigs.ts +++ /dev/null @@ -1,22 +0,0 @@ -import tseslint from "typescript-eslint"; - -export const tsConfigs = [ - ...tseslint.configs.recommendedTypeChecked, - { - files: ["**/*.{ts,tsx}"], - rules: { - "@typescript-eslint/no-unused-vars": [ - "error", - { - "args": "all", - "argsIgnorePattern": "^_", - "caughtErrors": "all", - "caughtErrorsIgnorePattern": "^_", - "destructuredArrayIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "ignoreRestSiblings": true - }, - ], - }, - }, -]; diff --git a/packages/eslint-config/tsconfig.json b/packages/eslint-config/tsconfig.json index b74f1124c..bab18e3d3 100644 --- a/packages/eslint-config/tsconfig.json +++ b/packages/eslint-config/tsconfig.json @@ -1,9 +1,10 @@ { - "complierOptions": { + "compilerOptions": { "lib": ["ES2023"], + "allowJs": true, "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "nodenext", + "module": "NodeNext", + "moduleResolution": "NodeNext", "moduleDetection": "force", "verbatimModuleSyntax": true, "noEmit": true, @@ -14,5 +15,5 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "include": ["./*.ts"] + "include": ["src/**/*.ts", "eslint.config.js"] }