eslint-plugin-nextfriday 2.0.0 → 3.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,66 @@
1
1
  # eslint-plugin-nextfriday
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#116](https://github.com/next-friday/eslint-plugin-nextfriday/pull/116) [`64ced55`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/64ced556b4099a40ca2f4750523c7a079d2c81d8) Thanks [@joetakara](https://github.com/joetakara)! - `nextjs` and `nextjs/recommended` presets now also disable `nextfriday/file-kebab-case` (in addition to `nextfriday/jsx-pascal-case`) for files under `app/**`, `src/app/**`, `pages/**`, and `src/pages/**`. The override globs are expanded from `*.{jsx,tsx}` to `*.{js,jsx,ts,tsx}` so `route.ts`, `middleware.ts`, and other framework-named `.ts`/`.js` files in those directories are no longer flagged by either filename rule. Filename conventions in routing directories are owned by the framework, not by this plugin.
8
+
9
+ The `react` and `react/recommended` presets are unchanged — projects using them still enforce `file-kebab-case` on every `.ts`/`.js` and `jsx-pascal-case` on every `.tsx`/`.jsx`.
10
+
11
+ ### Patch Changes
12
+
13
+ - [#116](https://github.com/next-friday/eslint-plugin-nextfriday/pull/116) [`64ced55`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/64ced556b4099a40ca2f4750523c7a079d2c81d8) Thanks [@joetakara](https://github.com/joetakara)! - Documentation quality improvements targeting Context7 scoring gaps:
14
+ - `README.md` — "Per-Directory Configuration" section now explains how flat config resolves rules (config-array order, `files`/`ignores` precedence, why flat replaces `.eslintrc` overrides), adds a preset-tier-per-directory table, and lists common edge cases (glob ordering, top-level vs scoped `ignores`, spreading array-shaped presets, `--print-config` for debugging).
15
+ - `README.md` — "Migration Strategy" section is restructured around six concrete phases: surveying violations with `eslint --format json | jq`, isolating the auto-fix pass into its own PR, adopting the warn-level preset, ratcheting clean directories to `/recommended`, managing exceptions (severity override → directory override → disable comment, in that order of preference), and tracking violation count over time. The "Prioritize rules by impact" table is unchanged.
16
+ - `docs/rules/ENFORCE_CONSTANT_CASE.md` — Configuration section split into install / enable-just-this-rule / enable-with-related-rules / enable-via-preset / scope-to-directory subsections, plus a "Severity-only — no rule options" callout clarifying that the legacy `["error", { ... }]` array form is not accepted because no rule in this plugin has options.
17
+
18
+ ## 3.0.0
19
+
20
+ ### Major Changes
21
+
22
+ - [#114](https://github.com/next-friday/eslint-plugin-nextfriday/pull/114) [`7933269`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/793326971cbbf15aaf2015f13622b7eff0fd51b2) Thanks [@joetakara](https://github.com/joetakara)! - Remove bundled `configs.sonarjs` and `configs.unicorn`. These configs are no longer exported. Consumers that previously did `...nextfriday.configs.sonarjs` or `...nextfriday.configs.unicorn` must install `eslint-plugin-sonarjs` and/or `eslint-plugin-unicorn` directly and configure them in their own flat config.
23
+
24
+ Migration:
25
+
26
+ ```js
27
+ import sonarjs from "eslint-plugin-sonarjs";
28
+ import unicorn from "eslint-plugin-unicorn";
29
+
30
+ export default [
31
+ {
32
+ plugins: { sonarjs },
33
+ rules: { ...sonarjs.configs.recommended.rules },
34
+ },
35
+ {
36
+ plugins: { unicorn },
37
+ rules: { ...unicorn.configs.recommended.rules },
38
+ },
39
+ ];
40
+ ```
41
+
42
+ The remaining six presets (`base`, `base/recommended`, `react`, `react/recommended`, `nextjs`, `nextjs/recommended`) are unchanged.
43
+
44
+ - [#114](https://github.com/next-friday/eslint-plugin-nextfriday/pull/114) [`7933269`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/793326971cbbf15aaf2015f13622b7eff0fd51b2) Thanks [@joetakara](https://github.com/joetakara)! - `nextjs` and `nextjs/recommended` presets are now arrays of flat-config objects instead of a single object. The first object enables the rules; the second disables `nextfriday/jsx-pascal-case` for files matching `app/**/*.{jsx,tsx}`, `src/app/**/*.{jsx,tsx}`, `pages/**/*.{jsx,tsx}`, and `src/pages/**/*.{jsx,tsx}` — Next.js's official routing directories where filenames are owned by the framework (`page.tsx`, `layout.tsx`, `error.tsx`, etc.).
45
+
46
+ `base`, `base/recommended`, `react`, and `react/recommended` are unchanged — they remain single config objects and `jsx-pascal-case` is enforced everywhere when those presets are used.
47
+
48
+ The `jsx-pascal-case` rule itself no longer special-cases any filename or directory. Scope is expressed in the preset via ESLint's `files` glob, not via rule-internal allowlists. Consumers using `react`/`react/recommended` on a Next.js project must either switch to the `nextjs` preset or add their own `files`-scoped override.
49
+
50
+ Migration:
51
+
52
+ ```js
53
+ import nextfriday from "eslint-plugin-nextfriday";
54
+
55
+ export default [nextfriday.configs["nextjs/recommended"]];
56
+ ```
57
+
58
+ ESLint 9+ flattens nested config arrays automatically, so existing usage continues to work.
59
+
60
+ ### Patch Changes
61
+
62
+ - [#114](https://github.com/next-friday/eslint-plugin-nextfriday/pull/114) [`7933269`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/793326971cbbf15aaf2015f13622b7eff0fd51b2) Thanks [@joetakara](https://github.com/joetakara)! - Fix `enforce-constant-case` flagging conventional names in framework config files. The rule now skips entirely when run on `*.config.{ts,mjs,cjs,js}`, `*.rc.*`, `*.setup.*`, `*.spec.*`, `*.test.*`, `.eslintrc*`, `.babelrc*`, and `.prettierrc*`. Frameworks like Next.js require `nextConfig` in `next.config.ts`, Vite/Tailwind require `config`, etc. — these conventions can no longer be incorrectly flagged as needing `SCREAMING_SNAKE_CASE`.
63
+
3
64
  ## 2.0.0
4
65
 
5
66
  ### Major Changes
package/README.md CHANGED
@@ -46,24 +46,49 @@ export default [nextfriday.configs.nextjs];
46
46
  export default [nextfriday.configs["nextjs/recommended"]];
47
47
  ```
48
48
 
49
- #### Bundled Plugin Configs
49
+ ### Extending a Preset with Rule Overrides
50
50
 
51
- Pre-configured configs for popular plugins. No extra install needed they ship as dependencies. These configs are arrays, so use the spread operator (`...`) to merge them into your flat config.
51
+ To use a preset and adjust individual rules, append a second config object after the preset. Later objects override earlier ones, so you can change severity, swap options, or add rules without re-declaring the entire preset.
52
+
53
+ For example, enforce PascalCase for React components via the `react/recommended` preset (which already runs `nextfriday/jsx-pascal-case` and `nextfriday/enforce-camel-case` as errors), and add a rule override on top:
52
54
 
53
55
  ```js
54
56
  import nextfriday from "eslint-plugin-nextfriday";
55
57
 
56
- export default [nextfriday.configs["react/recommended"], ...nextfriday.configs.sonarjs, ...nextfriday.configs.unicorn];
58
+ export default [
59
+ nextfriday.configs["react/recommended"],
60
+
61
+ {
62
+ rules: {
63
+ "nextfriday/jsx-pascal-case": "error",
64
+ "nextfriday/enforce-props-suffix": "error",
65
+ "nextfriday/sort-imports": "warn",
66
+ },
67
+ },
68
+ ];
57
69
  ```
58
70
 
59
- | Config | Plugin | Description |
60
- | --------- | --------------------- | ---------------------------------------------------------------------------------------------------------- |
61
- | `sonarjs` | eslint-plugin-sonarjs | SonarJS recommended rules for bug detection and code quality |
62
- | `unicorn` | eslint-plugin-unicorn | Unicorn recommended rules (with `filename-case` and `prevent-abbreviations` off, `no-null` off in JSX/TSX) |
71
+ The first object enables every rule in `react/recommended`. The second object reaffirms `jsx-pascal-case` (already enforced — useful when you want it loud and explicit), enables `enforce-props-suffix`, and downgrades `sort-imports` from error to warning.
63
72
 
64
73
  ### Manual Configuration
65
74
 
66
- If you prefer to configure rules manually:
75
+ #### When to use manual configuration vs a preset
76
+
77
+ Reach for a preset (`base`, `react`, `nextjs`, or any `/recommended` variant) by default. Presets are curated, kept in sync with new rules as the plugin grows, and require almost no maintenance on your side.
78
+
79
+ Choose manual configuration when one of these applies:
80
+
81
+ | Scenario | Why manual fits better |
82
+ | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
83
+ | You only need a few specific rules | Presets enable the full set; manual lets you adopt rules one at a time. |
84
+ | You want to opt out of new rules added in future plugin releases | Manual configs are explicit — new rules added to a preset would automatically apply, manual configs don't change without your edit. |
85
+ | You're consolidating multiple ESLint plugins and want fine-grained per-rule control | Avoids preset rules conflicting with rules from other plugins. |
86
+ | You're building a higher-level shared config for your org | You can hand-pick exactly which NextFriday rules to bundle into your own preset. |
87
+ | You're debugging a rule conflict | Manual makes the active rule set unambiguous. |
88
+
89
+ For every other case — new projects, gradual adoption, library code, application code — start from a preset and use [Extending a Preset with Rule Overrides](#extending-a-preset-with-rule-overrides) when you need to adjust specific rules.
90
+
91
+ #### Manual configuration example
67
92
 
68
93
  ```js
69
94
  import nextfriday from "eslint-plugin-nextfriday";
@@ -151,8 +176,230 @@ export default [
151
176
 
152
177
  > **Note:** This plugin requires ESLint 9+ and only supports the flat config format. Legacy `.eslintrc` configurations are not supported.
153
178
 
179
+ ### Per-Directory Configuration
180
+
181
+ #### How ESLint flat config resolves rules
182
+
183
+ Flat config (the only format this plugin supports) is an **array of config objects** evaluated in order. For every file ESLint lints, it walks the array and merges every object whose `files` glob matches and whose `ignores` glob does not. Later objects override earlier ones rule-by-rule, so the **last matching object wins** for any given rule. Objects with no `files` field apply globally; objects with only `ignores` at the top level remove files from the entire run.
184
+
185
+ This is why per-directory configuration works: you stack a global default first, then layer narrower `files`-scoped objects on top. ESLint 9+ also flattens nested arrays automatically, so spreading a preset that ships as an array (like `nextfriday.configs.nextjs`) works the same as spreading a single object.
186
+
187
+ The legacy `.eslintrc` format used `overrides` and an inheritance tree to express the same thing. Flat config replaces that tree with a flat, deterministic array — easier to reason about, no implicit merging across `extends`, and no `parserOptions` plumbing per directory. The plugin does not ship `.eslintrc` shims, so projects on ESLint 8 or below cannot consume it without upgrading.
188
+
189
+ #### Layering strict and loose presets
190
+
191
+ Apply different rule severities to different directories by stacking config objects:
192
+
193
+ ```js
194
+ import nextfriday from "eslint-plugin-nextfriday";
195
+
196
+ export default [
197
+ {
198
+ ignores: ["src/legacy/**", "dist/**", "build/**", "**/*.generated.ts"],
199
+ },
200
+
201
+ nextfriday.configs.react,
202
+
203
+ {
204
+ files: ["src/components/**/*.{ts,tsx}", "src/hooks/**/*.{ts,tsx}"],
205
+ ...nextfriday.configs["react/recommended"],
206
+ },
207
+
208
+ {
209
+ files: ["src/utils/**/*.ts", "src/lib/**/*.ts"],
210
+ rules: {
211
+ "nextfriday/require-explicit-return-type": "error",
212
+ "nextfriday/no-relative-imports": "error",
213
+ },
214
+ },
215
+
216
+ {
217
+ files: ["**/*.{test,spec}.{ts,tsx}"],
218
+ rules: {
219
+ "nextfriday/require-explicit-return-type": "off",
220
+ "nextfriday/no-single-char-variables": "off",
221
+ "nextfriday/no-direct-date": "off",
222
+ },
223
+ },
224
+ ];
225
+ ```
226
+
227
+ Reading top to bottom:
228
+
229
+ 1. **Top-level `ignores`** removes legacy and build artifacts from the entire run. A config object containing _only_ `ignores` is treated as a global ignore — narrower `ignores` inside a `files`-scoped object only affect that object.
230
+ 2. **`nextfriday.configs.react`** applies as the warn-level baseline to every file ESLint sees (no `files` glob).
231
+ 3. **Component and hook directories** get promoted to `react/recommended` (errors). Because this object comes after the baseline, its severities win.
232
+ 4. **Utility directories** keep the warn-level baseline but selectively promote two correctness rules to `error`. Use this pattern when you don't want the full `/recommended` preset but do want specific rules treated as blocking.
233
+ 5. **Test files** keep most rules but turn off rules that conflict with common test patterns (single-letter loop counters, frozen `Date.now()` mocks, void-returning `it()` callbacks).
234
+
235
+ #### Choosing a preset tier per directory
236
+
237
+ | Code area | Suggested preset | Why |
238
+ | --------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------- |
239
+ | Library / SDK code | `base/recommended` or `react/recommended` | Public surface should be tightest. `error` blocks regressions at PR time. |
240
+ | New product code | `react/recommended` or `nextjs/recommended` | New code starts clean; lock the conventions in immediately. |
241
+ | Mature product code mid-migration | `react` or `nextjs` (warn) | Ship while migrating. Switch to `/recommended` after a clean run. |
242
+ | Tests, scripts, fixtures | preset + targeted overrides | Keep the core lint signal; turn off rules that mismatch test/CLI patterns. |
243
+ | Legacy / vendored / generated | top-level `ignores` | No lint signal at all. Don't waste reviewer attention or CI time. |
244
+
245
+ #### Edge cases and troubleshooting
246
+
247
+ - **Glob precedence is order-dependent, not specificity-based.** A more specific glob later in the array wins; a more specific glob _earlier_ in the array does not. If `files: ["src/**"]` appears after `files: ["src/components/**"]`, the broader rule set wins for components. Put broader configs first.
248
+ - **`files` and `ignores` are anchored to the project root** (the directory containing `eslint.config.{js,mjs,ts}`), not the file's directory. Use `**/` prefixes for matches anywhere in the tree (e.g., `**/*.test.ts`).
249
+ - **Top-level `ignores` ≠ `ignores` inside a config object.** A standalone object `{ ignores: [...] }` removes files from the entire run; an `ignores` field next to `files` and `rules` only narrows that one config object's match.
250
+ - **Spreading a preset replaces, not merges, the `plugins` field.** If you spread `...nextfriday.configs.react` and then a different config later, the later object's `plugins` wins. Re-declare `plugins: { nextfriday }` if you add rules in a later object.
251
+ - **`nextfriday.configs.nextjs` is an array, not a single object.** Spreading it inline with `...nextfriday.configs.nextjs` only spreads array indices, not the inner objects. Use it as an array entry (`nextfriday.configs.nextjs,`) instead, so ESLint 9+ flattens it.
252
+ - **Two presets in one project.** Use scoped `files` rather than two unscoped presets — unscoped presets stack and the later one's rule severities win for every file, which is rarely what you want.
253
+ - **Verifying the resolved config for a file:** `pnpm eslint --print-config path/to/file.tsx` prints the merged config ESLint would actually use. Reach for this when a rule fires (or doesn't) and you can't tell why.
254
+
255
+ ### Migration Strategy
256
+
257
+ For an existing codebase with many violations, treat the migration as a phased rollout — survey, fix, lock in, repeat.
258
+
259
+ #### 1. Survey violations before changing anything
260
+
261
+ Install the plugin as a dev dependency, drop a warn-level preset into `eslint.config.mjs`, and run a read-only lint. Don't `--fix` yet — you want to see the raw shape of the codebase first.
262
+
263
+ ```bash
264
+ pnpm add -D eslint-plugin-nextfriday eslint
265
+ pnpm eslint . --no-fix
266
+ ```
267
+
268
+ Group violations by rule so you can plan the work. Most CI dashboards do this for you, but a one-liner works locally:
269
+
270
+ ```bash
271
+ pnpm eslint . --no-fix --format json | jq -r '.[].messages[].ruleId' | sort | uniq -c | sort -rn
272
+ ```
273
+
274
+ The output tells you which rules account for most of the noise. A rule with 3 violations is a 10-minute fix; a rule with 800 is a multi-week project. Plan accordingly.
275
+
276
+ #### 2. Run the auto-fixers in an isolated PR
277
+
278
+ Roughly a third of this plugin's rules are auto-fixable (the `Fixable ✅` column in the [Rules](#rules) table). Run them in a dedicated commit so the diff is purely mechanical and reviewers don't have to read every line:
279
+
280
+ ```bash
281
+ pnpm eslint . --fix
282
+ git add -u && git commit -m "style(lint): autofix nextfriday rules"
283
+ ```
284
+
285
+ Open this as its own PR. Mixing auto-fix output with hand-written changes in the same diff makes review almost impossible.
286
+
287
+ #### 3. Adopt the warn-level preset
288
+
289
+ After the auto-fix pass, drop in the warn-level preset so the remaining violations surface during local dev and CI without breaking the build:
290
+
291
+ ```js
292
+ import nextfriday from "eslint-plugin-nextfriday";
293
+
294
+ export default [nextfriday.configs.react];
295
+ ```
296
+
297
+ Warnings don't fail `eslint --max-warnings=0`, so add that flag in CI only when you're ready to block on warnings. Until then, warnings are visible signal without pressure.
298
+
299
+ #### 4. Lock in clean directories one at a time
300
+
301
+ As individual directories or features reach zero violations, promote them to `/recommended` (errors) so regressions block at PR time. Everything else stays on the warn-level preset.
302
+
303
+ ```js
304
+ import nextfriday from "eslint-plugin-nextfriday";
305
+
306
+ export default [
307
+ nextfriday.configs.react,
308
+
309
+ {
310
+ files: ["src/components/v2/**/*.{ts,tsx}", "src/lib/**/*.ts"],
311
+ ...nextfriday.configs["react/recommended"],
312
+ },
313
+ ];
314
+ ```
315
+
316
+ Repeat per directory. This is how you ratchet without flag-day rewrites.
317
+
318
+ #### 5. Manage exceptions explicitly
319
+
320
+ Three ways to carve out an exception, in order of preference. Pick the narrowest one that solves the problem.
321
+
322
+ **Per-rule severity override** — turn off a single noisy rule globally until you can fix it at the codebase level:
323
+
324
+ ```js
325
+ export default [
326
+ nextfriday.configs["react/recommended"],
327
+
328
+ {
329
+ rules: {
330
+ "nextfriday/require-explicit-return-type": "warn",
331
+ "nextfriday/sort-imports": "off",
332
+ },
333
+ },
334
+ ];
335
+ ```
336
+
337
+ **Per-directory exception** — keep the rule strict everywhere except one stubborn corner:
338
+
339
+ ```js
340
+ export default [
341
+ nextfriday.configs["react/recommended"],
342
+
343
+ {
344
+ files: ["src/legacy/**/*.{ts,tsx}"],
345
+ rules: {
346
+ "nextfriday/no-relative-imports": "off",
347
+ "nextfriday/enforce-camel-case": "off",
348
+ },
349
+ },
350
+ ];
351
+ ```
352
+
353
+ **Per-file or per-line disable comments** — last resort, for genuinely irreducible cases:
354
+
355
+ ```ts
356
+ // eslint-disable-next-line nextfriday/no-direct-date
357
+ const epochAnchor = new Date(0);
358
+
359
+ /* eslint-disable nextfriday/no-emoji */
360
+ export const FLAG_EMOJIS = ["🇹🇭", "🇯🇵", "🇺🇸"];
361
+ /* eslint-enable nextfriday/no-emoji */
362
+ ```
363
+
364
+ Always disable a _named_ rule, never blanket-disable ESLint. A blanket `// eslint-disable` mutes every rule including correctness ones, so legitimate bugs slip through later.
365
+
366
+ **Skip vendored or generated files entirely** with a top-level ignore — these aren't exceptions to manage, they're code you don't lint at all:
367
+
368
+ ```js
369
+ export default [
370
+ {
371
+ ignores: ["dist/**", "build/**", "coverage/**", "**/*.generated.ts"],
372
+ },
373
+
374
+ nextfriday.configs["react/recommended"],
375
+ ];
376
+ ```
377
+
378
+ #### 6. Track progress so the migration actually lands
379
+
380
+ Migrations stall when nobody can see how close you are. Two cheap signals:
381
+
382
+ - **Violation count over time.** Pipe `pnpm eslint . --no-fix --format compact | wc -l` into your CI metrics or a daily Slack post. Trend it weekly. If the number stops dropping, the migration has stalled.
383
+ - **Verify a single file's resolved config.** When a contributor asks "why did this rule fire on my file?", run `pnpm eslint --print-config path/to/file.tsx`. The output shows exactly which severity ESLint resolved for every rule on that file — usually answers the question in seconds.
384
+
385
+ Once a directory hits zero, lock it in (step 4). Once the warn-level count hits zero across the whole repo, switch the global preset to `/recommended` and add `--max-warnings=0` to CI. The migration is done.
386
+
387
+ #### Prioritize rules by impact
388
+
389
+ When the warn-level preset surfaces hundreds of violations, fix them in this order — high-impact rules catch real bugs, while low-impact rules are style preferences that can wait.
390
+
391
+ | Tier | Examples | Why first |
392
+ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
393
+ | High — correctness and runtime safety | `no-direct-date`, `no-env-fallback`, `nextjs-require-public-env`, `enforce-readonly-component-props`, `jsx-no-non-component-function`, `enforce-hook-naming`, `no-logic-in-params` | Each violation can mask a bug, leak config, or break React's rules of hooks. Fix before they ship. |
394
+ | Medium — structure and naming | `boolean-naming-prefix`, `enforce-camel-case`, `enforce-constant-case`, `file-kebab-case`, `jsx-pascal-case`, `enforce-props-suffix`, `prefer-import-type` | No runtime impact, but inconsistent naming compounds review and onboarding cost. Fix once the high tier is clean. |
395
+ | Low — formatting and ordering | `sort-imports`, `sort-exports`, `sort-type-alphabetically`, `jsx-sort-props`, `newline-before-return`, `newline-after-multiline-block`, `no-emoji` | Cosmetic. Most are auto-fixable, so a single `pnpm eslint --fix` pass typically clears the whole codebase. Save these for last. |
396
+
397
+ In practice: turn the high tier on as `"error"` first, leave the medium tier as `"warn"` while you migrate, and run the auto-fixers for the low tier in a single dedicated PR.
398
+
154
399
  ## Rules
155
400
 
401
+ > All rules in this plugin have no configurable options (`schema: []`). The only knob is severity — set each rule to `"error"`, `"warn"`, or `"off"`. Behavior is intentionally fixed so that the same rule means the same thing across every project.
402
+
156
403
  ### Variable Naming Rules
157
404
 
158
405
  | Rule | Description | Fixable |
@@ -253,10 +500,12 @@ export default [
253
500
  | -------------------- | -------- | ---------- | --------- | ------------- | ----------- |
254
501
  | `base` | warn | 40 | 0 | 0 | 40 |
255
502
  | `base/recommended` | error | 40 | 0 | 0 | 40 |
256
- | `react` | warn | 40 | 15 | 0 | 55 |
257
- | `react/recommended` | error | 40 | 15 | 0 | 55 |
258
- | `nextjs` | warn | 40 | 15 | 1 | 56 |
259
- | `nextjs/recommended` | error | 40 | 15 | 1 | 56 |
503
+ | `react` | warn | 40 | 16 | 0 | 56 |
504
+ | `react/recommended` | error | 40 | 16 | 0 | 56 |
505
+ | `nextjs` | warn | 40 | 16 | 1 | 57 |
506
+ | `nextjs/recommended` | error | 40 | 16 | 1 | 57 |
507
+
508
+ The `nextjs` and `nextjs/recommended` presets ship as an array of two flat-config objects: the rule set above, plus a routing override that disables `nextfriday/file-kebab-case` and `nextfriday/jsx-pascal-case` for files matching `app/**/*.{js,jsx,ts,tsx}`, `src/app/**/*.{js,jsx,ts,tsx}`, `pages/**/*.{js,jsx,ts,tsx}`, and `src/pages/**/*.{js,jsx,ts,tsx}`. Next.js owns the filenames in those directories (`page.tsx`, `layout.tsx`, `route.ts`, `middleware.ts`, etc.), so the plugin steps out of the way. ESLint 9+ flattens nested config arrays automatically, so spreading the preset works as expected.
260
509
 
261
510
  ### Base Configuration Rules (40 rules)
262
511
 
@@ -80,6 +80,19 @@ The rule identifies boolean variables and parameters by:
80
80
  | `does` | Action capability | `doesExist`, `doesMatch`, `doesContain` |
81
81
  | `had` | Past possession | `hadError`, `hadAccess`, `hadPrevious` |
82
82
 
83
+ ## Not Checked
84
+
85
+ To keep the rule unobtrusive on patterns that are usually shaped by external APIs or framework conventions, these locations are intentionally ignored:
86
+
87
+ - **Class methods, getters, and setters.** `class Foo { get valid() {...} }` and `class Foo { set valid(v) {...} }` are not checked.
88
+ - **Class fields (property definitions).** `class Foo { valid = true }` is not checked.
89
+ - **Object literal properties.** `{ valid: true }` and `{ open: true, closed: false }` are not checked. Object keys often mirror an external schema (API responses, config payloads, DB rows) where renaming would be incorrect.
90
+ - **Computed property names.** `{ [key]: true }` is not checked because the name is dynamic.
91
+ - **Destructuring patterns.** `const { valid } = result;` is not checked — rename at the source instead, or use a destructuring alias (`const { valid: isValid } = result;`).
92
+ - **Property access.** `if (user.valid) {}` is not checked.
93
+
94
+ If you need stricter coverage of object/class members, pair this rule with [`@typescript-eslint/naming-convention`](https://typescript-eslint.io/rules/naming-convention/), which can target `classProperty`, `objectLiteralProperty`, `accessor`, and other selectors with custom prefix patterns.
95
+
83
96
  ## When Not To Use It
84
97
 
85
98
  - When working with external APIs that return boolean fields with different naming conventions
@@ -8,6 +8,8 @@ This rule ensures that global-scope `const` declarations with static values use
8
8
 
9
9
  Only global scope (top-level of a file) is checked. Local scope constants inside functions are not checked by this rule.
10
10
 
11
+ **Config files are exempt.** Files matching `*.config.{ts,mjs,cjs,js}`, `*.rc.*`, `*.setup.*`, `*.spec.*`, `*.test.*`, `.eslintrc*`, `.babelrc*`, and `.prettierrc*` skip this rule entirely. This avoids conflicts with framework conventions that require specific identifier names — e.g. Next.js expects `nextConfig` (not `NEXT_CONFIG`) in `next.config.ts`, Vite expects `config`, Tailwind expects `config`, etc.
12
+
11
13
  ## Examples
12
14
 
13
15
  ### Incorrect
@@ -54,6 +56,97 @@ function foo() {
54
56
  }
55
57
  ```
56
58
 
59
+ ## Configuration
60
+
61
+ This rule has no options — only severity is configurable (`"error"`, `"warn"`, `"off"`). It pairs with [`no-misleading-constant-case`](./NO_MISLEADING_CONSTANT_CASE.md) so that static globals use `SCREAMING_SNAKE_CASE` while local scopes and dynamic values keep `camelCase`.
62
+
63
+ ### Install
64
+
65
+ ```bash
66
+ pnpm add -D eslint-plugin-nextfriday eslint
67
+ # npm install --save-dev eslint-plugin-nextfriday eslint
68
+ # yarn add --dev eslint-plugin-nextfriday eslint
69
+ ```
70
+
71
+ ### Enable just this rule
72
+
73
+ Use this when you want the rule but not the rest of the preset (e.g., adopting one rule at a time during a migration). The `plugins` field registers the plugin under the `nextfriday` namespace; the `rules` field turns on the rule by name:
74
+
75
+ ```js
76
+ import nextfriday from "eslint-plugin-nextfriday";
77
+
78
+ export default [
79
+ {
80
+ plugins: { nextfriday },
81
+ rules: {
82
+ "nextfriday/enforce-constant-case": "error",
83
+ },
84
+ },
85
+ ];
86
+ ```
87
+
88
+ ### Enable with related rules
89
+
90
+ Constants, locals, and dynamic values each need a different naming convention. Enable the trio together so violations from any direction surface:
91
+
92
+ ```js
93
+ import nextfriday from "eslint-plugin-nextfriday";
94
+
95
+ export default [
96
+ {
97
+ plugins: { nextfriday },
98
+ rules: {
99
+ "nextfriday/enforce-constant-case": "error",
100
+ "nextfriday/no-misleading-constant-case": "error",
101
+ "nextfriday/enforce-camel-case": "error",
102
+ },
103
+ },
104
+ ];
105
+ ```
106
+
107
+ ### Enable via a preset
108
+
109
+ Every preset includes this rule at the preset's severity (`warn` or `error`). This is the simplest setup and the recommended one for most projects:
110
+
111
+ ```js
112
+ import nextfriday from "eslint-plugin-nextfriday";
113
+
114
+ export default [nextfriday.configs["base/recommended"]];
115
+ ```
116
+
117
+ ### Scope to a directory
118
+
119
+ If you're migrating an existing codebase, scope the rule to a clean directory first and leave the rest of the project on `"warn"` until you've fixed the violations there. ESLint 9+ flat config layers configs in array order; the later object's severity wins for any file matching its `files` glob:
120
+
121
+ ```js
122
+ import nextfriday from "eslint-plugin-nextfriday";
123
+
124
+ export default [
125
+ nextfriday.configs.base,
126
+
127
+ {
128
+ files: ["src/config/**/*.ts", "src/lib/**/*.ts"],
129
+ rules: {
130
+ "nextfriday/enforce-constant-case": "error",
131
+ },
132
+ },
133
+ ];
134
+ ```
135
+
136
+ ### Severity-only — no rule options
137
+
138
+ Each rule in this plugin uses `schema: []` and `defaultOptions: []` (no options). The flat-config value is **only** the severity string — `"error"`, `"warn"`, or `"off"`. The legacy `["error", { ... }]` array form is not accepted because there are no options to pass.
139
+
140
+ ```js
141
+ // Correct
142
+ "nextfriday/enforce-constant-case": "error"
143
+
144
+ // Won't apply — there are no options to override
145
+ "nextfriday/enforce-constant-case": ["error", { allowCamelCase: true }]
146
+ ```
147
+
148
+ This plugin only supports ESLint 9+ flat config — legacy `.eslintrc` is not supported. Projects on ESLint 8 or below cannot consume it without upgrading.
149
+
57
150
  ## When Not To Use It
58
151
 
59
152
  - If your project uses different naming conventions for constants
@@ -28,6 +28,43 @@ This rule enforces that all TypeScript (.ts) and JavaScript (.js) files use keba
28
28
  - `user-service.ts`
29
29
  - `api-utils.ts`
30
30
 
31
+ ## Allowed Patterns and Edge Cases
32
+
33
+ - **Only `.ts` and `.js` are checked.** `.tsx` and `.jsx` files are ignored — use [`jsx-pascal-case`](./JSX_PASCAL_CASE.md) for React component filenames.
34
+ - **Compound extensions are allowed when each segment is kebab-case.** Both the basename and the trailing token before the file extension are validated independently. This pattern is useful for config, setup, spec, test, and rc files:
35
+ - `next.config.ts` ✓
36
+ - `vitest.setup.ts` ✓
37
+ - `user-service.test.ts` ✓
38
+ - `auth.spec.ts` ✓
39
+ - `eslint.rc.ts` ✓
40
+ - **Numbers are allowed inside segments.** `file-with-numbers-123.ts` ✓
41
+ - **Single-word filenames are valid.** `single.ts` ✓ (no hyphens needed)
42
+
43
+ ## Scoping the Rule
44
+
45
+ This rule has **no built-in framework detection**. It checks every `.ts`/`.js` file it sees against kebab-case. In Next.js projects, routing directories (`app/`, `pages/`) contain framework-named files (`route.ts`, `middleware.ts`) that already happen to be kebab-case — but if you colocate helpers there with non-kebab names (`useThing.ts`, `MyHelper.ts`), the rule will flag them.
46
+
47
+ The `nextjs` and `nextjs/recommended` presets ship with an override that **automatically disables this rule** for files under `app/**`, `src/app/**`, `pages/**`, and `src/pages/**` (matched against `*.{js,jsx,ts,tsx}`). Filename conventions in those directories are owned by the framework, not by this plugin. The `base` and `react` presets do **not** include this override — they enforce `file-kebab-case` on every `.ts`/`.js` regardless of directory.
48
+
49
+ ## Disabling the Rule
50
+
51
+ To opt out for additional directories or file patterns, add an override in your flat config:
52
+
53
+ ```js
54
+ import nextfriday from "eslint-plugin-nextfriday";
55
+
56
+ export default [
57
+ nextfriday.configs["base/recommended"],
58
+
59
+ {
60
+ files: ["src/legacy/**/*.ts", "src/vendor/**/*.js"],
61
+ rules: {
62
+ "nextfriday/file-kebab-case": "off",
63
+ },
64
+ },
65
+ ];
66
+ ```
67
+
31
68
  ## When Not To Use It
32
69
 
33
70
  If your project has established naming conventions that conflict with kebab-case, or if you're working with frameworks that require specific filename patterns, you may want to disable this rule.
@@ -28,6 +28,40 @@ This rule enforces that JSX and TSX files use PascalCase naming convention for t
28
28
  - `LoginForm.tsx`
29
29
  - `UserProfile2.jsx` (PascalCase with numbers)
30
30
 
31
+ ## Scoping the Rule
32
+
33
+ This rule has **no built-in framework detection** and no allowlist of "known" filenames. It checks every `.jsx`/`.tsx` it sees against PascalCase.
34
+
35
+ The `nextjs` and `nextjs/recommended` presets ship with an override that **automatically disables this rule** for files under `app/**`, `src/app/**`, `pages/**`, and `src/pages/**` — Next.js owns those filenames (`page.tsx`, `layout.tsx`, `error.tsx`, etc.). If you use a `nextjs` preset, you do not need to add a routing override yourself.
36
+
37
+ The `base` and `react` presets do **not** include this override. If you use `react` on a Next.js project (or any project that mixes PascalCase component files with lowercase framework routing files), scope the rule explicitly via ESLint's `files` glob:
38
+
39
+ ```js
40
+ import nextfriday from "eslint-plugin-nextfriday";
41
+
42
+ export default [
43
+ nextfriday.configs.react,
44
+
45
+ {
46
+ files: ["src/components/**/*.{jsx,tsx}", "components/**/*.{jsx,tsx}"],
47
+ rules: {
48
+ "nextfriday/jsx-pascal-case": "error",
49
+ },
50
+ },
51
+
52
+ {
53
+ files: ["src/app/**/*.{jsx,tsx}", "app/**/*.{jsx,tsx}", "src/pages/**/*.{jsx,tsx}", "pages/**/*.{jsx,tsx}"],
54
+ rules: {
55
+ "nextfriday/jsx-pascal-case": "off",
56
+ },
57
+ },
58
+ ];
59
+ ```
60
+
61
+ The first override turns the rule on only inside component directories where PascalCase is the convention. The second override explicitly disables the rule for Next.js routing directories where the framework owns the filename.
62
+
63
+ The plugin deliberately does not try to detect Next.js, Remix, or other framework conventions outside of the `nextjs` preset — folder structures vary across projects (monorepos, custom `app` locations, hybrid Pages + App Router setups), and a built-in allowlist would inevitably go stale. ESLint's `files` glob is the deterministic way to express the scope you actually want.
64
+
31
65
  ## When Not To Use It
32
66
 
33
67
  If your project uses different naming conventions for JSX/TSX files, you can disable this rule.