@exadev/eslint-config 2.9.1 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,6 +101,63 @@ export default tseslint.config(
101
101
 
102
102
  **`plugin.configs.recommended`/`plugin.configs.barrel` carry no `files`/`ignores` and are safe unscoped** -- `no-side-effects-in-index` and `no-non-barrel-reexport` each check `context.filename` themselves (self-scoping). For a barrel not at `src/index.ts`, or a project-specific exception, layer an override on top (e.g. `{ files: ['lib/other.ts'], rules: { 'exadev/no-non-barrel-reexport': 'off' } }`) rather than wiring all four rules individually.
103
103
 
104
+ ## Optional React and Next.js support
105
+
106
+ `import exadev from '@exadev/eslint-config'` keeps working unchanged -- it's now literally `exadevConfig()` called with no arguments, no migration required. React/hooks/a11y and Next.js rule blocks are folded in automatically, with no separate import or config needed, gated on two independent, always-both-required conditions:
107
+
108
+ 1. **The corresponding package must actually be resolvable.** `eslint-plugin-react`, `eslint-plugin-react-hooks`, `eslint-plugin-jsx-a11y`, and `@next/eslint-plugin-next` are all *optional* peer dependencies (`peerDependenciesMeta.<pkg>.optional: true`) -- install only whichever your project actually needs:
109
+ ```sh
110
+ pnpm add -D eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y # React support
111
+ pnpm add -D @next/eslint-plugin-next # Next.js support
112
+ ```
113
+ If none of these resolve, `@exadev/eslint-config`'s default export is byte-for-byte identical to the plain TypeScript ruleset -- nothing about the base package changes.
114
+ 2. **For React specifically, the file must actually be `.jsx`/`.tsx`.** The React/hooks/a11y rule block is scoped to `files: ['**/*.jsx', '**/*.tsx']`, so even if `eslint-plugin-react` is resolvable only incidentally (e.g. hoisted as a transitive dependency of something unrelated in a monorepo, with zero real JSX anywhere in the linted project), its rules are never matched against a file that isn't JSX -- ESLint's flat-config `files` matching happens per linted file, at lint time, not at config-build time. `@next/eslint-plugin-next`'s block carries no such glob: its own presence is already an unambiguous signal on its own (nothing installs it except a real Next.js project).
115
+
116
+ ### Explicit control
117
+
118
+ Two ways to override the automatic behaviour, for anyone who doesn't want to rely on it:
119
+
120
+ **`plugin.configs.react`/`plugin.configs.nextjs`** -- explicit tier selection, mirroring `plugin.configs.recommended`/`.barrel`. Unlike those two, which only ever reference this package's own always-present rules, selecting `configs.react`/`.nextjs` is itself an explicit request: it **throws** a clear, actionable error if the underlying peer isn't installed, rather than silently returning nothing.
121
+ ```ts
122
+ import { plugin } from '@exadev/eslint-config';
123
+ import tseslint from 'typescript-eslint';
124
+
125
+ export default tseslint.config(
126
+ // ...your own config...
127
+ {
128
+ files: ['**/*.tsx'],
129
+ plugins: { exadev: plugin },
130
+ extends: [plugin.configs.react], // throws if eslint-plugin-react isn't installed
131
+ },
132
+ );
133
+ ```
134
+
135
+ **`exadevConfig(options, ...userConfigs)`** -- the named factory export, for fine-grained tri-state control per feature:
136
+
137
+ ```ts
138
+ // eslint.config.ts
139
+ import { exadevConfig } from '@exadev/eslint-config';
140
+ import tseslint from 'typescript-eslint';
141
+
142
+ export default tseslint.config(
143
+ {
144
+ languageOptions: {
145
+ parserOptions: { project: './tsconfig.json', tsconfigRootDir: import.meta.dirname },
146
+ },
147
+ },
148
+ ...exadevConfig({ react: true, nextjs: false }),
149
+ // ...your own config...
150
+ );
151
+ ```
152
+
153
+ | Value | React (`options.react`) | Next.js (`options.nextjs`) |
154
+ | --- | --- | --- |
155
+ | `true` | Force on -- throws if `eslint-plugin-react` isn't resolvable | Force on -- throws if `@next/eslint-plugin-next` isn't resolvable |
156
+ | `false` | Force off -- always `[]`, no resolution attempted | Force off -- always `[]`, no resolution attempted |
157
+ | `undefined` / omitted | Auto-detect (the default) | Auto-detect (the default) |
158
+
159
+ Trailing arguments are arbitrary flat-config objects, appended in order after everything else -- `exadevConfig({}, { rules: { 'no-console': 'warn' } })` is equivalent to spreading the default export plus one more config object.
160
+
104
161
  ## Rules
105
162
 
106
163
  | Rule | Fixable | Description |
@@ -155,17 +212,23 @@ The `lint`/`typecheck`/`test`/`build` npm scripts wrap turbo tasks named `_lint`
155
212
 
156
213
  ## Architecture
157
214
 
158
- `src/plugin.ts` builds an `ESLint.Plugin` (ESLint's own type) combining `src/rules/` into a flat `rules` map. `configs.recommended` and `configs.barrel` are getters in the object literal -- each references the fully-built `plugin` (`plugins: { exadev: plugin }`), which a plain property initializer can't do mid-construction. `recommended` ships `barrel-policy` at `mode: 'banned'`; `barrel` at `mode: 'single'`.
215
+ `src/plugin.ts` builds a `TSESLint.FlatConfig.Plugin` (`@typescript-eslint/utils`'s own type -- not ESLint's own `ESLint.Plugin`, which can't hold a rule built with `ESLintUtils.RuleCreator`) combining `src/rules/` into a flat `rules` map. `configs.recommended`, `.barrel`, `.react`, and `.nextjs` are getters in the object literal -- each references the fully-built `plugin` (`plugins: { exadev: plugin }`), which a plain property initializer can't do mid-construction. `recommended` ships `barrel-policy` at `mode: 'banned'`; `barrel` at `mode: 'single'`; `.react`/`.nextjs` call `buildReactConfig`/`buildNextjsConfig` with `enabled: true` (see below).
216
+
217
+ `src/config-types.ts` holds `ConfigValue`/`ConfigArrayValue` (`ConfigArrayValue = Extract<ConfigValue, unknown[]>`, the array-only member of ESLint's own config-value union), shared by every file below rather than redefined per file -- annotating a config array with the wider `ConfigValue` union directly broke `...exadev` with `TS2488` ("must have a Symbol.iterator method").
218
+
219
+ `src/optional-plugin.ts` is the lazy-resolution helper behind React/Next.js support: `tryRequire` wraps `createRequire(import.meta.url)` in try/catch, returning `unknown` (never a cast) so every call site narrows explicitly before use; `readFlatConfig` walks a property path through that `unknown` value via a real type guard, normalizing a stray legacy top-level `parserOptions` key into `languageOptions.parserOptions` along the way (confirmed necessary: `eslint-plugin-jsx-a11y`'s own `configs.recommended` export carries exactly this legacy shape, which flat config's schema rejects outright rather than ignores).
220
+
221
+ `src/react.ts`/`src/nextjs.ts` each export a `build*Config(options)` function: resolve the relevant optional peer(s) via `tryRequire`, extract their real flat config via `readFlatConfig`, and return an array of 0-or-more config blocks -- `[]` if unresolvable and not explicitly forced on, a thrown `Error` if explicitly forced on (`enabled: true`) and still unresolvable. `react.ts`'s blocks are scoped to `files: ['**/*.jsx', '**/*.tsx']`; `nextjs.ts`'s is not (see [Optional React and Next.js support](#optional-react-and-nextjs-support) for why).
159
222
 
160
- `src/recommended-type-checked.ts` bundles typescript-eslint's `strictTypeChecked` + `stylisticTypeChecked` alongside this plugin's rules into a flat config array. Its value is typed as `ConfigArrayValue = Extract<ConfigValue, unknown[]>` (the array-only member of ESLint's own config-value union), because annotating with the wider union broke `...exadev` with `TS2488`.
223
+ `src/create-config.ts` is config assembly's single source of truth: `exadevConfig(options, ...userConfigs)` concatenates `recommendedTypeChecked` with both builders' output (each fed the matching tri-state option) and any trailing user configs; `defaultConfig` is `exadevConfig()` evaluated once, eagerly, at module load.
161
224
 
162
- `src/index.ts` is the entry point: `export { default } from './recommended-type-checked'; export { default as plugin } from './plugin';`. Both exports share one root module, so importing `{ plugin }` alone still resolves `typescript-eslint` via the sibling re-export -- an accepted trade-off (an earlier separate-subpath split proved more awkward in practice).
225
+ `src/index.ts` is the entry point, still a pure re-export barrel (required by `no-side-effects-in-index`/`no-non-barrel-reexport`, both of which assume this file contains nothing but `export ... from ...`): `export { defaultConfig as default, exadevConfig } from './create-config'; export { default as plugin } from './plugin';`. All exports share one root module, so importing `{ plugin }` alone still resolves `typescript-eslint` via the sibling re-export -- an accepted trade-off (an earlier separate-subpath split proved more awkward in practice). React/Next.js support never adds to this cost: none of the four optional packages are ever statically imported, only passed as a runtime string to `createRequire`'s resolver, so their absence never affects module evaluation for a consumer who doesn't use them.
163
226
 
164
227
  `pnpm-workspace.yaml` declares an empty `packages: []` -- not a real workspace, just giving turbo a root for local task caching.
165
228
 
166
229
  ## Conventions
167
230
 
168
- `eslint.config.ts` dogfoods the default export on itself (`import exadev from './src/index'`), spreading it exactly as a real consumer would. `no-side-effects-in-index` and `no-non-barrel-reexport` self-scope to `src/index.ts` internally, so no `files`/`ignores` wiring is needed here. Plugin construction lives in `src/plugin.ts` specifically so `src/index.ts` stays a pure re-export point.
231
+ `eslint.config.ts` dogfoods this package's own factory export on itself (`import { exadevConfig } from './src/index'`), spreading `exadevConfig({ react: false, nextjs: false })` -- forced off explicitly, not the plain auto-detecting default, since `eslint-plugin-react`/`@next/eslint-plugin-next` are real devDependencies of *this* repo (needed to test `src/react.ts`/`src/nextjs.ts`'s own "package is resolvable" branch) even though this repo is neither a React nor a Next.js project. `no-side-effects-in-index` and `no-non-barrel-reexport` self-scope to `src/index.ts` internally, so no `files`/`ignores` wiring is needed here. Plugin construction lives in `src/plugin.ts` specifically so `src/index.ts` stays a pure re-export point.
169
232
 
170
233
  `tsconfig.json` enables `verbatimModuleSyntax` (`import type`/`export type` required for type-only imports -- also enforced by `consistent-type-imports`) and `noUncheckedIndexedAccess` (narrow indexed access before use rather than asserting).
171
234
 
@@ -177,6 +240,7 @@ Conventional commits are enforced by commitlint, restricted to the type-enum def
177
240
  - `src/index.ts` mixing a default export with a named one triggers rolldown's `MIXED_EXPORTS` warning: a raw CommonJS `require()` would see the raw exports object instead of the default. ESM `import` (the actual consumer path) resolves both correctly; `attw --pack` and `publint` report no problems, so the warning is accepted (see `tsdown.config.ts`).
178
241
  - Husky hooks: `pre-commit` runs lint-staged (`eslint --fix` on staged `*.ts`), `commit-msg` runs commitlint, `pre-push` runs typecheck + test + build.
179
242
  - The CI release job sets `HUSKY=0` (commit-msg hook skips the automated release commit) and blanks `NPM_TOKEN`/`NODE_AUTH_TOKEN` explicitly so an inherited token can't win over OIDC trusted publishing.
243
+ - A consumer who already has `eslint-plugin-react`/`@next/eslint-plugin-next` resolvable for unrelated reasons (e.g. hoisted in a monorepo) and writes `.jsx`/`.tsx` files may see new rule activity the moment they upgrade to a version of this package that ships React/Next.js support -- with zero action on their part. This is the normal, widely-accepted ESLint-ecosystem convention that adding rules to a shared/recommended config is a minor bump even though it can newly trip an existing `--max-warnings 0` gate, not a breaking change; see [Optional React and Next.js support](#optional-react-and-nextjs-support) for the `react`/`nextjs` options to force it off explicitly if needed.
180
244
 
181
245
  ## Contributing
182
246
 
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
+ let node_module = require("node:module");
27
28
  let typescript_eslint = require("typescript-eslint");
28
29
  typescript_eslint = __toESM(typescript_eslint, 1);
29
30
  let node_path = require("node:path");
@@ -31,8 +32,85 @@ let _typescript_eslint_utils = require("@typescript-eslint/utils");
31
32
  let typescript = require("typescript");
32
33
  typescript = __toESM(typescript, 1);
33
34
  let ts_api_utils = require("ts-api-utils");
35
+ //#region src/optional-plugin.ts
36
+ const nodeRequire = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
37
+ function tryRequire(specifier, requireFn = nodeRequire) {
38
+ try {
39
+ return requireFn(specifier);
40
+ } catch {
41
+ return;
42
+ }
43
+ }
44
+ function isRecord(value) {
45
+ return typeof value === "object" && value !== null;
46
+ }
47
+ function isFlatConfig$1(value) {
48
+ return isRecord(value);
49
+ }
50
+ function normalizeLegacyParserOptions(record) {
51
+ if (!("parserOptions" in record)) return record;
52
+ const { parserOptions, languageOptions, ...rest } = record;
53
+ const existingLanguageOptions = isRecord(languageOptions) ? languageOptions : {};
54
+ return {
55
+ ...rest,
56
+ languageOptions: {
57
+ ...existingLanguageOptions,
58
+ parserOptions
59
+ }
60
+ };
61
+ }
62
+ function readFlatConfig(module, path) {
63
+ let current = module;
64
+ for (const key of path) {
65
+ if (!isRecord(current)) return void 0;
66
+ current = current[key];
67
+ }
68
+ if (!isRecord(current)) return void 0;
69
+ const normalized = normalizeLegacyParserOptions(current);
70
+ return isFlatConfig$1(normalized) ? normalized : void 0;
71
+ }
72
+ //#endregion
73
+ //#region src/nextjs.ts
74
+ const INSTALL_COMMAND$1 = "pnpm add -D @next/eslint-plugin-next";
75
+ function buildNextjsConfig(options = {}) {
76
+ if (options.enabled === false) return [];
77
+ const nextConfig = readFlatConfig(tryRequire("@next/eslint-plugin-next", options.requireFn), ["configs", "core-web-vitals"]);
78
+ if (options.enabled === true && nextConfig === void 0) throw new Error(`@exadev/eslint-config: Next.js support was explicitly requested but '@next/eslint-plugin-next' could not be resolved. Install it with: ${INSTALL_COMMAND$1}`);
79
+ return nextConfig === void 0 ? [] : [nextConfig];
80
+ }
81
+ //#endregion
82
+ //#region src/react.ts
83
+ const JSX_FILE_PATTERNS = ["**/*.jsx", "**/*.tsx"];
84
+ const INSTALL_COMMAND = "pnpm add -D eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y";
85
+ function isFlatConfig(value) {
86
+ return value !== void 0;
87
+ }
88
+ function buildReactConfig(options = {}) {
89
+ if (options.enabled === false) return [];
90
+ const reactConfig = readFlatConfig(tryRequire("eslint-plugin-react", options.requireFn), [
91
+ "configs",
92
+ "flat",
93
+ "recommended"
94
+ ]);
95
+ if (options.enabled === true && reactConfig === void 0) throw new Error(`@exadev/eslint-config: React support was explicitly requested but 'eslint-plugin-react' could not be resolved. Install it with: ${INSTALL_COMMAND}`);
96
+ if (reactConfig === void 0) return [];
97
+ const hooksModule = tryRequire("eslint-plugin-react-hooks", options.requireFn);
98
+ return [
99
+ reactConfig,
100
+ readFlatConfig(hooksModule, [
101
+ "configs",
102
+ "flat",
103
+ "recommended-latest"
104
+ ]) ?? readFlatConfig(hooksModule, ["configs", "recommended-latest"]),
105
+ readFlatConfig(tryRequire("eslint-plugin-jsx-a11y", options.requireFn), ["flatConfigs", "recommended"])
106
+ ].filter(isFlatConfig).map((config) => ({
107
+ ...config,
108
+ files: [...JSX_FILE_PATTERNS]
109
+ }));
110
+ }
111
+ //#endregion
34
112
  //#region package.json
35
- var version = "2.9.1";
113
+ var version = "2.10.0";
36
114
  //#endregion
37
115
  //#region src/rules/barrel-helpers.ts
38
116
  const INDEX_BASENAME$1 = /^index\.[cm]?[tj]sx?$/;
@@ -1248,6 +1326,12 @@ const plugin = {
1248
1326
  plugins: { exadev: plugin },
1249
1327
  rules: { "exadev/barrel-policy": ["error", { mode: "single" }] }
1250
1328
  };
1329
+ },
1330
+ get react() {
1331
+ return buildReactConfig({ enabled: true });
1332
+ },
1333
+ get nextjs() {
1334
+ return buildNextjsConfig({ enabled: true });
1251
1335
  }
1252
1336
  }
1253
1337
  };
@@ -1312,5 +1396,17 @@ const recommendedTypeChecked = [
1312
1396
  }
1313
1397
  ];
1314
1398
  //#endregion
1315
- exports.default = recommendedTypeChecked;
1399
+ //#region src/create-config.ts
1400
+ function exadevConfig(options = {}, ...userConfigs) {
1401
+ return [
1402
+ ...recommendedTypeChecked,
1403
+ ...buildReactConfig({ enabled: options.react }),
1404
+ ...buildNextjsConfig({ enabled: options.nextjs }),
1405
+ ...userConfigs
1406
+ ];
1407
+ }
1408
+ const defaultConfig = exadevConfig();
1409
+ //#endregion
1410
+ exports.default = defaultConfig;
1411
+ exports.exadevConfig = exadevConfig;
1316
1412
  exports.plugin = plugin;
package/dist/index.d.cts CHANGED
@@ -1,10 +1,17 @@
1
1
  import { TSESLint } from "@typescript-eslint/utils";
2
- //#region src/recommended-type-checked.d.ts
2
+ //#region src/config-types.d.ts
3
3
  type ConfigValue = NonNullable<TSESLint.FlatConfig.Plugin['configs']>[string];
4
4
  type ConfigArrayValue = Extract<ConfigValue, unknown[]>;
5
- declare const recommendedTypeChecked: ConfigArrayValue;
5
+ //#endregion
6
+ //#region src/create-config.d.ts
7
+ interface ExadevConfigOptions {
8
+ readonly react?: boolean;
9
+ readonly nextjs?: boolean;
10
+ }
11
+ declare function exadevConfig(options?: ExadevConfigOptions, ...userConfigs: readonly TSESLint.FlatConfig.Config[]): ConfigArrayValue;
12
+ declare const defaultConfig: ConfigArrayValue;
6
13
  //#endregion
7
14
  //#region src/plugin.d.ts
8
15
  declare const plugin: TSESLint.FlatConfig.Plugin;
9
16
  //#endregion
10
- export { recommendedTypeChecked as default, plugin };
17
+ export { defaultConfig as default, exadevConfig, plugin };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,17 @@
1
1
  import { TSESLint } from "@typescript-eslint/utils";
2
- //#region src/recommended-type-checked.d.ts
2
+ //#region src/config-types.d.ts
3
3
  type ConfigValue = NonNullable<TSESLint.FlatConfig.Plugin['configs']>[string];
4
4
  type ConfigArrayValue = Extract<ConfigValue, unknown[]>;
5
- declare const recommendedTypeChecked: ConfigArrayValue;
5
+ //#endregion
6
+ //#region src/create-config.d.ts
7
+ interface ExadevConfigOptions {
8
+ readonly react?: boolean;
9
+ readonly nextjs?: boolean;
10
+ }
11
+ declare function exadevConfig(options?: ExadevConfigOptions, ...userConfigs: readonly TSESLint.FlatConfig.Config[]): ConfigArrayValue;
12
+ declare const defaultConfig: ConfigArrayValue;
6
13
  //#endregion
7
14
  //#region src/plugin.d.ts
8
15
  declare const plugin: TSESLint.FlatConfig.Plugin;
9
16
  //#endregion
10
- export { recommendedTypeChecked as default, plugin };
17
+ export { defaultConfig as default, exadevConfig, plugin };
package/dist/index.js CHANGED
@@ -1,10 +1,88 @@
1
+ import { createRequire } from "node:module";
1
2
  import tseslint from "typescript-eslint";
2
3
  import { posix } from "node:path";
3
4
  import { AST_NODE_TYPES, ESLintUtils, TSESLint } from "@typescript-eslint/utils";
4
5
  import * as ts from "typescript";
5
6
  import { isPropertyReadonlyInType, isTypeReference } from "ts-api-utils";
7
+ //#region src/optional-plugin.ts
8
+ const nodeRequire = createRequire(import.meta.url);
9
+ function tryRequire(specifier, requireFn = nodeRequire) {
10
+ try {
11
+ return requireFn(specifier);
12
+ } catch {
13
+ return;
14
+ }
15
+ }
16
+ function isRecord(value) {
17
+ return typeof value === "object" && value !== null;
18
+ }
19
+ function isFlatConfig$1(value) {
20
+ return isRecord(value);
21
+ }
22
+ function normalizeLegacyParserOptions(record) {
23
+ if (!("parserOptions" in record)) return record;
24
+ const { parserOptions, languageOptions, ...rest } = record;
25
+ const existingLanguageOptions = isRecord(languageOptions) ? languageOptions : {};
26
+ return {
27
+ ...rest,
28
+ languageOptions: {
29
+ ...existingLanguageOptions,
30
+ parserOptions
31
+ }
32
+ };
33
+ }
34
+ function readFlatConfig(module, path) {
35
+ let current = module;
36
+ for (const key of path) {
37
+ if (!isRecord(current)) return void 0;
38
+ current = current[key];
39
+ }
40
+ if (!isRecord(current)) return void 0;
41
+ const normalized = normalizeLegacyParserOptions(current);
42
+ return isFlatConfig$1(normalized) ? normalized : void 0;
43
+ }
44
+ //#endregion
45
+ //#region src/nextjs.ts
46
+ const INSTALL_COMMAND$1 = "pnpm add -D @next/eslint-plugin-next";
47
+ function buildNextjsConfig(options = {}) {
48
+ if (options.enabled === false) return [];
49
+ const nextConfig = readFlatConfig(tryRequire("@next/eslint-plugin-next", options.requireFn), ["configs", "core-web-vitals"]);
50
+ if (options.enabled === true && nextConfig === void 0) throw new Error(`@exadev/eslint-config: Next.js support was explicitly requested but '@next/eslint-plugin-next' could not be resolved. Install it with: ${INSTALL_COMMAND$1}`);
51
+ return nextConfig === void 0 ? [] : [nextConfig];
52
+ }
53
+ //#endregion
54
+ //#region src/react.ts
55
+ const JSX_FILE_PATTERNS = ["**/*.jsx", "**/*.tsx"];
56
+ const INSTALL_COMMAND = "pnpm add -D eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y";
57
+ function isFlatConfig(value) {
58
+ return value !== void 0;
59
+ }
60
+ function buildReactConfig(options = {}) {
61
+ if (options.enabled === false) return [];
62
+ const reactConfig = readFlatConfig(tryRequire("eslint-plugin-react", options.requireFn), [
63
+ "configs",
64
+ "flat",
65
+ "recommended"
66
+ ]);
67
+ if (options.enabled === true && reactConfig === void 0) throw new Error(`@exadev/eslint-config: React support was explicitly requested but 'eslint-plugin-react' could not be resolved. Install it with: ${INSTALL_COMMAND}`);
68
+ if (reactConfig === void 0) return [];
69
+ const hooksModule = tryRequire("eslint-plugin-react-hooks", options.requireFn);
70
+ return [
71
+ reactConfig,
72
+ readFlatConfig(hooksModule, [
73
+ "configs",
74
+ "flat",
75
+ "recommended-latest"
76
+ ]) ?? readFlatConfig(hooksModule, ["configs", "recommended-latest"]),
77
+ readFlatConfig(tryRequire("eslint-plugin-jsx-a11y", options.requireFn), ["flatConfigs", "recommended"])
78
+ ].filter(isFlatConfig).map((config) => ({
79
+ ...config,
80
+ files: [...JSX_FILE_PATTERNS]
81
+ }));
82
+ }
83
+ //#endregion
6
84
  //#region package.json
7
- var version = "2.9.1";
85
+ var version = "2.10.0";
8
86
  //#endregion
9
87
  //#region src/rules/barrel-helpers.ts
10
88
  const INDEX_BASENAME$1 = /^index\.[cm]?[tj]sx?$/;
@@ -1220,6 +1298,12 @@ const plugin = {
1220
1298
  plugins: { exadev: plugin },
1221
1299
  rules: { "exadev/barrel-policy": ["error", { mode: "single" }] }
1222
1300
  };
1301
+ },
1302
+ get react() {
1303
+ return buildReactConfig({ enabled: true });
1304
+ },
1305
+ get nextjs() {
1306
+ return buildNextjsConfig({ enabled: true });
1223
1307
  }
1224
1308
  }
1225
1309
  };
@@ -1284,4 +1368,15 @@ const recommendedTypeChecked = [
1284
1368
  }
1285
1369
  ];
1286
1370
  //#endregion
1287
- export { recommendedTypeChecked as default, plugin };
1371
+ //#region src/create-config.ts
1372
+ function exadevConfig(options = {}, ...userConfigs) {
1373
+ return [
1374
+ ...recommendedTypeChecked,
1375
+ ...buildReactConfig({ enabled: options.react }),
1376
+ ...buildNextjsConfig({ enabled: options.nextjs }),
1377
+ ...userConfigs
1378
+ ];
1379
+ }
1380
+ const defaultConfig = exadevConfig();
1381
+ //#endregion
1382
+ export { defaultConfig as default, exadevConfig, plugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exadev/eslint-config",
3
- "version": "2.9.1",
3
+ "version": "2.10.0",
4
4
  "description": "Shared custom ESLint rules and plugin for ExaDev projects",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -38,21 +38,43 @@
38
38
  "url": "git+https://github.com/ExaDev/eslint-config.git"
39
39
  },
40
40
  "peerDependencies": {
41
+ "@next/eslint-plugin-next": "^16.3.2",
41
42
  "eslint": ">=10.0.0",
43
+ "eslint-plugin-jsx-a11y": "^6.10.2",
44
+ "eslint-plugin-react": "^7.37.5",
45
+ "eslint-plugin-react-hooks": "^7.1.1",
42
46
  "typescript": ">=4.8.4",
43
47
  "typescript-eslint": ">=8.0.0"
44
48
  },
49
+ "peerDependenciesMeta": {
50
+ "@next/eslint-plugin-next": {
51
+ "optional": true
52
+ },
53
+ "eslint-plugin-jsx-a11y": {
54
+ "optional": true
55
+ },
56
+ "eslint-plugin-react": {
57
+ "optional": true
58
+ },
59
+ "eslint-plugin-react-hooks": {
60
+ "optional": true
61
+ }
62
+ },
45
63
  "devDependencies": {
46
64
  "@arethetypeswrong/cli": "^0.18.5",
47
65
  "@commitlint/cli": "^21.2.1",
48
66
  "@commitlint/config-conventional": "^21.2.0",
49
67
  "@eslint/js": "^10.0.1",
68
+ "@next/eslint-plugin-next": "^16.3.2",
50
69
  "@semantic-release/changelog": "^7.0.0",
51
70
  "@semantic-release/git": "^11.0.1",
52
71
  "@types/node": "^24.9.2",
53
72
  "@typescript-eslint/rule-tester": "^8.67.0",
54
73
  "@vitest/coverage-v8": "^4.1.10",
55
74
  "eslint": "^10.8.0",
75
+ "eslint-plugin-jsx-a11y": "^6.10.2",
76
+ "eslint-plugin-react": "^7.37.5",
77
+ "eslint-plugin-react-hooks": "^7.1.1",
56
78
  "husky": "^9.1.7",
57
79
  "lint-staged": "^17.2.0",
58
80
  "publint": "^0.3.21",