diff --git a/package.json b/package.json index 428ce49..bf70eea 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@eslint/js": "^9.1.1", + "@next/eslint-plugin-next": "^14.2.3", "@stylistic/eslint-plugin": "^1.7.2", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c658818..b53a06c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@eslint/js': specifier: ^9.1.1 version: 9.1.1 + '@next/eslint-plugin-next': + specifier: ^14.2.3 + version: 14.2.3 '@stylistic/eslint-plugin': specifier: ^1.7.2 version: 1.7.2(eslint@9.1.1)(typescript@5.4.5) @@ -417,6 +420,9 @@ packages: '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@next/eslint-plugin-next@14.2.3': + resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1044,6 +1050,11 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + glob@10.3.12: resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} engines: {node: '>=16 || 14 >=14.17'} @@ -1943,6 +1954,10 @@ snapshots: '@jsdevtools/ono@7.1.3': {} + '@next/eslint-plugin-next@14.2.3': + dependencies: + glob: 10.3.10 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2664,6 +2679,14 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.3.10: + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.4 + minipass: 7.0.4 + path-scurry: 1.10.2 + glob@10.3.12: dependencies: foreground-child: 3.1.1 diff --git a/scripts/typegen.ts b/scripts/typegen.ts index ec9c0ff..adc7a1a 100644 --- a/scripts/typegen.ts +++ b/scripts/typegen.ts @@ -8,6 +8,8 @@ import { javascript } from "#/configs/javascript"; import { typescript } from "#/configs/typescript"; import { stylistic } from "#/configs/stylistic"; import { node } from "#/configs/node"; +import { react } from "#/configs/react"; +import { nextjs } from "#/configs/nextjs"; /** * Combine array and non-array configs into a single array. @@ -23,6 +25,8 @@ const configs = await combine( node(), stylistic(), typescript(), + react(), + nextjs(), ); const configNames = configs.map(i => i.name).filter(Boolean) as string[]; diff --git a/src/configs/nextjs/config.ts b/src/configs/nextjs/config.ts new file mode 100644 index 0000000..68a5061 --- /dev/null +++ b/src/configs/nextjs/config.ts @@ -0,0 +1,22 @@ +/* eslint-disable ts/no-unsafe-member-access */ +/* eslint-disable ts/no-unsafe-assignment */ + +import type { TypedFlatConfigItem } from "#/types/type"; +import { nextjsPlugin } from "#/utils/extension"; +import { configName } from "#/utils/naming"; + +export const nextjs = (): TypedFlatConfigItem[] => { + return [ + { + name: configName("nextjs", "rules"), + files: ["**/*.ts", "**/*.tsx"], + plugins: { + node: nextjsPlugin, + }, + rules: { + ...nextjsPlugin.configs.recommended.rules, + ...nextjsPlugin.configs["core-web-vitals"].rules, + }, + }, + ]; +}; diff --git a/src/configs/nextjs/index.ts b/src/configs/nextjs/index.ts new file mode 100644 index 0000000..5c62e04 --- /dev/null +++ b/src/configs/nextjs/index.ts @@ -0,0 +1 @@ +export * from "./config"; diff --git a/src/configs/react/config.ts b/src/configs/react/config.ts new file mode 100644 index 0000000..e093c1f --- /dev/null +++ b/src/configs/react/config.ts @@ -0,0 +1,22 @@ +/* eslint-disable ts/no-unsafe-assignment */ + +import type { TypedFlatConfigItem } from "#/types/type"; +import { nextjsPlugin } from "#/utils/extension"; +import { configName } from "#/utils/naming"; + +// TODO: https://github.com/antfu/eslint-config/blob/main/src/configs/react.ts +// TODO: https://github.com/we-use/eslint-config/issues/13 + +export const react = (): TypedFlatConfigItem[] => { + return [ + { + name: configName("nextjs", "rules"), + files: ["**/*.ts", "**/*.tsx"], + plugins: { + node: nextjsPlugin, + }, + rules: { + }, + }, + ]; +}; diff --git a/src/configs/react/index.ts b/src/configs/react/index.ts new file mode 100644 index 0000000..5c62e04 --- /dev/null +++ b/src/configs/react/index.ts @@ -0,0 +1 @@ +export * from "./config"; diff --git a/src/configs/typescript/config.ts b/src/configs/typescript/config.ts index 93b7abf..17c9458 100644 --- a/src/configs/typescript/config.ts +++ b/src/configs/typescript/config.ts @@ -58,6 +58,7 @@ export const typescript = ({ tsconfigPath }: ParamsTS = {}): TypedFlatConfigItem "no-return-await": "off", "ts/return-await": "error", "ts/strict-boolean-expressions": "error", + "ts/no-misused-promises": ["error", { checksVoidReturn: { attributes: false } }], // https://github.com/orgs/react-hook-form/discussions/8622 }, }, ]; diff --git a/src/utils/extension.ts b/src/utils/extension.ts index b503aa0..671f7eb 100644 --- a/src/utils/extension.ts +++ b/src/utils/extension.ts @@ -2,5 +2,7 @@ export { default as antfuPlugin } from "eslint-plugin-antfu"; export { default as nodePlugin } from "eslint-plugin-n"; export { default as stylisticPlugin } from "@stylistic/eslint-plugin"; export { default as typescriptPlugin } from "@typescript-eslint/eslint-plugin"; +// @ts-expect-error no types declarations +export { default as nextjsPlugin } from "@next/eslint-plugin-next"; export { default as typescriptParser } from "@typescript-eslint/parser"; diff --git a/src/utils/factory.ts b/src/utils/factory.ts index ec8efbe..7d0024b 100644 --- a/src/utils/factory.ts +++ b/src/utils/factory.ts @@ -8,6 +8,8 @@ import { stylistic } from "#/configs/stylistic/config"; import { node } from "#/configs/node"; import { logger } from "#/utils/logger"; import { ignore } from "#/configs/ignore"; +import { nextjs } from "#/configs/nextjs"; +import { react } from "#/configs/react"; export const eslintConfig = async ( options: OptionsConfig = {}, @@ -17,6 +19,8 @@ export const eslintConfig = async ( const enabled = { typescript: isPackageExists("typescript"), + react: isPackageExists("react"), + nextjs: isPackageExists("next"), }; // Ignore: @@ -41,6 +45,18 @@ export const eslintConfig = async ( logger.info("stylistic - config enabled"); configs.push(stylistic(options.stylistic)); + // React: + if (enabled.react) { + logger.info("react - config enabled"); + configs.push(react()); + } + + // NextJS: + if (enabled.nextjs) { + logger.info("nextjs - config enabled"); + configs.push(nextjs()); + } + // Compose: const composer = new FlatConfigComposer();