From 38402673b7a35f8048843a8a194fc23737c3a406 Mon Sep 17 00:00:00 2001 From: Sebastian Luque Date: Wed, 6 Nov 2024 22:07:04 -0500 Subject: [PATCH] add nextjs optional plugin --- package.json | 1 + pnpm-lock.yaml | 22 ++++++ src/flat-react.ts | 94 +++++++++++++----------- src/types/eslint-plugin-next.d.ts | 7 ++ src/types/eslint-plugin-react-hooks.d.ts | 11 +++ tsconfig.build.json | 4 +- tsconfig.json | 6 +- 7 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 src/types/eslint-plugin-next.d.ts create mode 100644 src/types/eslint-plugin-react-hooks.d.ts diff --git a/package.json b/package.json index 38e64ab..308ca3f 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@eslint/compat": "^1.2.2", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.14.0", + "@next/eslint-plugin-next": "^15.0.2", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b81a5db..ade1bff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@eslint/js': specifier: ^9.14.0 version: 9.14.0 + '@next/eslint-plugin-next': + specifier: ^15.0.2 + version: 15.0.2 eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@9.13.0(jiti@1.21.6)) @@ -364,6 +367,9 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@next/eslint-plugin-next@15.0.2': + resolution: {integrity: sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -949,6 +955,10 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -2274,6 +2284,10 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@next/eslint-plugin-next@15.0.2': + dependencies: + fast-glob: 3.3.1 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2980,6 +2994,14 @@ snapshots: fast-diff@1.3.0: {} + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 diff --git a/src/flat-react.ts b/src/flat-react.ts index e77fb19..6fcdae3 100644 --- a/src/flat-react.ts +++ b/src/flat-react.ts @@ -2,56 +2,66 @@ import type { FlatConfig } from "@typescript-eslint/utils/ts-eslint"; import pluginReact from "eslint-plugin-react"; import jsxA11y from "eslint-plugin-jsx-a11y"; import tseslint from "typescript-eslint"; +import nextPlugin from "@next/eslint-plugin-next"; import pluginTailwindcss from "eslint-plugin-tailwindcss"; import { FlatCompat } from "@eslint/eslintrc"; -// @ts-expect-error - No types import reactHooks from "eslint-plugin-react-hooks"; const compat = new FlatCompat(); -export const react = tseslint.config( - { - files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], - extends: [ - // @ts-expect-error - Types are broken in eslint-plugin-react https://github.com/jsx-eslint/eslint-plugin-react/issues/3838 - pluginReact.configs.flat.recommended, - // @ts-expect-error - See above - pluginReact.configs.flat["jsx-runtime"], - ...compat.config(reactHooks.configs.recommended), - ...pluginTailwindcss.configs["flat/recommended"], - ], - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, +export interface ReactPluginOptions { + framework?: "next"; +} + +console.log(nextPlugin.configs["core-web-vitals"]); + +const eslintNext = compat.config(nextPlugin.configs["core-web-vitals"]); + +export const react = ({ framework }: ReactPluginOptions) => + tseslint.config( + { + files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], + extends: [ + // @ts-expect-error - Types are broken in eslint-plugin-react https://github.com/jsx-eslint/eslint-plugin-react/issues/3838 + pluginReact.configs.flat.recommended, + // @ts-expect-error - See above + pluginReact.configs.flat["jsx-runtime"], + ...compat.config(reactHooks.configs.recommended), + ...pluginTailwindcss.configs["flat/recommended"], + ...(framework === "next" ? eslintNext : []), + ], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, }, }, - }, - settings: { - react: { - version: "detect", + settings: { + react: { + version: "detect", + }, + }, + rules: { + "react/prop-types": "off", + "react/display-name": "warn", + "react/button-has-type": "warn", + "tailwindcss/no-custom-classname": [ + "warn", + { + callees: ["classnames", "clsx", "cva", "twMerge", "cn"], + ignoredKeys: ["color", "variant", "size", "defaultVariants"], + }, + ], }, }, - rules: { - "react/prop-types": "off", - "react/display-name": "warn", - "react/button-has-type": "warn", - "tailwindcss/no-custom-classname": [ - "warn", - { - callees: ["classnames", "clsx", "cva", "twMerge", "cn"], - ignoredKeys: ["color", "variant", "size", "defaultVariants"], - }, + { + files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], + ignores: [ + "**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", + "**/*.test.@(ts|tsx|js|jsx|mjs|cjs)", + "**/*.spec.@(ts|tsx|js|jsx|mjs|cjs)", ], - }, - }, - { - files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], - ignores: [ - "**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", - "**/*.test.@(ts|tsx|js|jsx|mjs|cjs)", - "**/*.spec.@(ts|tsx|js|jsx|mjs|cjs)", - ], - extends: [jsxA11y.flatConfigs.recommended], - } -) satisfies FlatConfig.ConfigArray; + extends: [jsxA11y.flatConfigs.recommended], + } + ) satisfies FlatConfig.ConfigArray; diff --git a/src/types/eslint-plugin-next.d.ts b/src/types/eslint-plugin-next.d.ts new file mode 100644 index 0000000..93128df --- /dev/null +++ b/src/types/eslint-plugin-next.d.ts @@ -0,0 +1,7 @@ +declare module "@next/eslint-plugin-next" { + export const configs: { + recommended: { rules: import("eslint").Linter.RulesRecord }; + "core-web-vitals": { rules: import("eslint").Linter.RulesRecord }; + }; + export const rules: Record; +} diff --git a/src/types/eslint-plugin-react-hooks.d.ts b/src/types/eslint-plugin-react-hooks.d.ts new file mode 100644 index 0000000..15ceafe --- /dev/null +++ b/src/types/eslint-plugin-react-hooks.d.ts @@ -0,0 +1,11 @@ +declare module "eslint-plugin-react-hooks" { + export const configs: { + recommended: { + rules: { + "rules-of-hooks": import("eslint").Linter.RuleEntry; + "exhaustive-deps": import("eslint").Linter.RuleEntry; + }; + }; + }; + export const rules: Record; +} diff --git a/tsconfig.build.json b/tsconfig.build.json index 8086ea5..3ecb4c9 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -20,9 +20,9 @@ "declaration": true, "composite": true, "noEmit": true, - /* If your code doesn't run in the DOM: */ "lib": [ - "es2022" + "es2022", + "DOM" ], }, "include": [ diff --git a/tsconfig.json b/tsconfig.json index 1925111..adaf3fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,10 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": false, - "rootDir": "." + "rootDir": ".", + "typeRoots": [ + "./src/types", + "./node_modules/@types" + ] } } \ No newline at end of file