eslint-plugin-nextfriday 3.2.1 → 4.0.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,21 @@
1
1
  # eslint-plugin-nextfriday
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#122](https://github.com/next-friday/eslint-plugin-nextfriday/pull/122) [`a9c5237`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/a9c52375dfe7d7064ead37c611c680e4c8d6be9f) Thanks [@joetakara](https://github.com/joetakara)! - Breaking changes
8
+ - Remove `enforce-curly-newline` rule. Use ESLint's built-in `curly: "all"` instead. Consumers should add `"curly": ["error", "all"]` to their ESLint config.
9
+ - Narrow `no-inline-nested-object` to truly nested structures only. Flat collections (objects of primitive properties, arrays of identifiers/member expressions/primitives) are now allowed inline because Prettier already controls their wrapping via `printWidth`. Eliminates the fix loop with Prettier on cases like `{ allow: [target.utils, target.types, target.constants] }`.
10
+
11
+ New rules
12
+ - Add `prefer-props-with-children`. Reports `children: ReactNode` declarations and recommends `PropsWithChildren<T>`. Included in `react` and `nextjs` presets.
13
+ - Add `prefer-interface-for-component-props`. Auto-fixes `type FooProps = {...}` to `interface FooProps {...}` in `.tsx` and `.jsx` files. Included in `react` and `nextjs` presets.
14
+
15
+ Fixes
16
+ - `prefer-import-type` skips imports with inline `type` markers, deferring to `no-inline-type-import`.
17
+ - `prefer-named-param-types` skips React component functions with single non-destructured `Identifier` param, deferring to `prefer-interface-over-inline-types`.
18
+
3
19
  ## 3.2.1
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -50,7 +50,7 @@ export default [nextfriday.configs["nextjs/recommended"]];
50
50
 
51
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
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:
53
+ For example, start with the `react/recommended` preset (which runs `nextfriday/enforce-camel-case` and `nextfriday/enforce-props-suffix` as errors) and add a rule override on top:
54
54
 
55
55
  ```js
56
56
  import nextfriday from "eslint-plugin-nextfriday";
@@ -60,7 +60,6 @@ export default [
60
60
 
61
61
  {
62
62
  rules: {
63
- "nextfriday/jsx-pascal-case": "error",
64
63
  "nextfriday/enforce-props-suffix": "error",
65
64
  "nextfriday/sort-imports": "warn",
66
65
  },
@@ -68,7 +67,7 @@ export default [
68
67
  ];
69
68
  ```
70
69
 
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.
70
+ The first object enables every rule in `react/recommended`. The second object reaffirms `enforce-props-suffix` (already enforced — useful when you want it loud and explicit) and downgrades `sort-imports` from error to warning.
72
71
 
73
72
  ### Manual Configuration
74
73
 
@@ -108,10 +107,6 @@ export default [
108
107
  "nextfriday/enforce-property-case": "error",
109
108
  "nextfriday/no-misleading-constant-case": "error",
110
109
 
111
- // File Naming
112
- "nextfriday/file-kebab-case": "error",
113
- "nextfriday/jsx-pascal-case": "error",
114
-
115
110
  // Code Style
116
111
  "nextfriday/no-emoji": "error",
117
112
  "nextfriday/prefer-destructuring-params": "error",
@@ -130,7 +125,6 @@ export default [
130
125
  "nextfriday/no-inline-nested-object": "error",
131
126
  "nextfriday/no-inline-return-properties": "error",
132
127
  "nextfriday/prefer-async-await": "error",
133
- "nextfriday/enforce-curly-newline": "error",
134
128
  "nextfriday/no-nested-ternary": "error",
135
129
  "nextfriday/prefer-guard-clause": "error",
136
130
 
@@ -166,9 +160,6 @@ export default [
166
160
  "nextfriday/react-props-destructure": "error",
167
161
  "nextfriday/enforce-props-suffix": "error",
168
162
  "nextfriday/enforce-readonly-component-props": "error",
169
-
170
- // Next.js
171
- "nextfriday/nextjs-require-public-env": "error",
172
163
  },
173
164
  },
174
165
  ];
@@ -388,11 +379,11 @@ Once a directory hits zero, lock it in (step 4). Once the warn-level count hits
388
379
 
389
380
  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
381
 
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. |
382
+ | Tier | Examples | Why first |
383
+ | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
384
+ | High — correctness and runtime safety | `no-direct-date`, `no-env-fallback`, `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. |
385
+ | Medium — structure and naming | `boolean-naming-prefix`, `enforce-camel-case`, `enforce-constant-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. |
386
+ | 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
387
 
397
388
  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
389
 
@@ -412,13 +403,6 @@ In practice: turn the high tier on as `"error"` first, leave the medium tier as
412
403
  | [enforce-property-case](docs/rules/ENFORCE_PROPERTY_CASE.md) | Enforce camelCase for unquoted object property keys | ❌ |
413
404
  | [no-misleading-constant-case](docs/rules/NO_MISLEADING_CONSTANT_CASE.md) | Disallow SCREAMING_SNAKE_CASE in local scope and for dynamic values | ❌ |
414
405
 
415
- ### File Naming Rules
416
-
417
- | Rule | Description | Fixable |
418
- | ------------------------------------------------ | ---------------------------------------------------- | ------- |
419
- | [file-kebab-case](docs/rules/FILE_KEBAB_CASE.md) | Enforce kebab-case filenames for .ts and .js files | ❌ |
420
- | [jsx-pascal-case](docs/rules/JSX_PASCAL_CASE.md) | Enforce PascalCase filenames for .jsx and .tsx files | ❌ |
421
-
422
406
  ### Code Style Rules
423
407
 
424
408
  | Rule | Description | Fixable |
@@ -441,7 +425,6 @@ In practice: turn the high tier on as `"error"` first, leave the medium tier as
441
425
  | [no-inline-nested-object](docs/rules/NO_INLINE_NESTED_OBJECT.md) | Require nested objects and arrays to span multiple lines | ✅ |
442
426
  | [no-inline-return-properties](docs/rules/NO_INLINE_RETURN_PROPERTIES.md) | Require return object properties to use shorthand notation | ❌ |
443
427
  | [prefer-async-await](docs/rules/PREFER_ASYNC_AWAIT.md) | Enforce async/await over .then() promise chains | ❌ |
444
- | [enforce-curly-newline](docs/rules/ENFORCE_CURLY_NEWLINE.md) | Enforce curly braces for multi-line if, forbid for single-line | ✅ |
445
428
  | [no-nested-ternary](docs/rules/NO_NESTED_TERNARY.md) | Disallow nested ternary expressions | ❌ |
446
429
  | [prefer-guard-clause](docs/rules/PREFER_GUARD_CLAUSE.md) | Enforce guard clause pattern instead of nested if statements | ❌ |
447
430
 
@@ -458,16 +441,17 @@ In practice: turn the high tier on as `"error"` first, leave the medium tier as
458
441
 
459
442
  ### Type Pattern Rules
460
443
 
461
- | Rule | Description | Fixable |
462
- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- |
463
- | [enforce-type-declaration-order](docs/rules/ENFORCE_TYPE_DECLARATION_ORDER.md) | Enforce referenced types are declared after their consumer | ❌ |
464
- | [no-nested-interface-declaration](docs/rules/NO_NESTED_INTERFACE_DECLARATION.md) | Disallow inline object types in interface/type properties | ❌ |
465
- | [prefer-named-param-types](docs/rules/PREFER_NAMED_PARAM_TYPES.md) | Enforce named types for function parameters with object types | ❌ |
466
- | [prefer-inline-literal-union](docs/rules/PREFER_INLINE_LITERAL_UNION.md) | Enforce inlining literal union types for better IDE hover info | ✅ |
467
- | [prefer-inline-type-export](docs/rules/PREFER_INLINE_TYPE_EXPORT.md) | Require type/interface exports inline at the declaration | ✅ |
468
- | [prefer-interface-over-inline-types](docs/rules/PREFER_INTERFACE_OVER_INLINE_TYPES.md) | Enforce interface declarations over inline types for React props | |
469
- | [sort-type-alphabetically](docs/rules/SORT_TYPE_ALPHABETICALLY.md) | Enforce A-Z sorting of properties within type groups | |
470
- | [sort-type-required-first](docs/rules/SORT_TYPE_REQUIRED_FIRST.md) | Enforce required properties before optional in types/interfaces | ✅ |
444
+ | Rule | Description | Fixable |
445
+ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | ------- |
446
+ | [enforce-type-declaration-order](docs/rules/ENFORCE_TYPE_DECLARATION_ORDER.md) | Enforce referenced types are declared after their consumer | ❌ |
447
+ | [no-nested-interface-declaration](docs/rules/NO_NESTED_INTERFACE_DECLARATION.md) | Disallow inline object types in interface/type properties | ❌ |
448
+ | [prefer-named-param-types](docs/rules/PREFER_NAMED_PARAM_TYPES.md) | Enforce named types for function parameters with object types | ❌ |
449
+ | [prefer-inline-literal-union](docs/rules/PREFER_INLINE_LITERAL_UNION.md) | Enforce inlining literal union types for better IDE hover info | ✅ |
450
+ | [prefer-inline-type-export](docs/rules/PREFER_INLINE_TYPE_EXPORT.md) | Require type/interface exports inline at the declaration | ✅ |
451
+ | [prefer-interface-for-component-props](docs/rules/PREFER_INTERFACE_FOR_COMPONENT_PROPS.md) | Enforce interface over type alias for component prop declarations | |
452
+ | [prefer-interface-over-inline-types](docs/rules/PREFER_INTERFACE_OVER_INLINE_TYPES.md) | Enforce interface declarations over inline types for React props | |
453
+ | [sort-type-alphabetically](docs/rules/SORT_TYPE_ALPHABETICALLY.md) | Enforce A-Z sorting of properties within type groups | ✅ |
454
+ | [sort-type-required-first](docs/rules/SORT_TYPE_REQUIRED_FIRST.md) | Enforce required properties before optional in types/interfaces | ✅ |
471
455
 
472
456
  ### React/JSX Rules
473
457
 
@@ -484,45 +468,38 @@ In practice: turn the high tier on as `"error"` first, leave the medium tier as
484
468
  | [jsx-sort-props](docs/rules/JSX_SORT_PROPS.md) | Enforce JSX props are sorted by value type | ✅ |
485
469
  | [jsx-spread-props-last](docs/rules/JSX_SPREAD_PROPS_LAST.md) | Enforce JSX spread attributes appear after all other props | ❌ |
486
470
  | [prefer-jsx-template-literals](docs/rules/PREFER_JSX_TEMPLATE_LITERALS.md) | Enforce template literals instead of mixing text and JSX expressions | ✅ |
471
+ | [prefer-props-with-children](docs/rules/PREFER_PROPS_WITH_CHILDREN.md) | Prefer PropsWithChildren over manually declaring children: ReactNode | ❌ |
487
472
  | [react-props-destructure](docs/rules/REACT_PROPS_DESTRUCTURE.md) | Enforce destructuring props inside React component body | ❌ |
488
473
  | [enforce-props-suffix](docs/rules/ENFORCE_PROPS_SUFFIX.md) | Enforce 'Props' suffix for interfaces and types in \*.tsx files | ❌ |
489
474
  | [enforce-readonly-component-props](docs/rules/ENFORCE_READONLY_COMPONENT_PROPS.md) | Enforce Readonly wrapper for React component props | ✅ |
490
475
 
491
- ### Next.js Rules
492
-
493
- | Rule | Description | Fixable |
494
- | -------------------------------------------------------------------- | ------------------------------------------------------------- | ------- |
495
- | [nextjs-require-public-env](docs/rules/NEXTJS_REQUIRE_PUBLIC_ENV.md) | Require NEXT*PUBLIC* prefix for env vars in client components | ❌ |
496
-
497
476
  ## Configurations
498
477
 
499
478
  ### Configuration Presets Overview
500
479
 
501
- | Preset | Severity | Base Rules | JSX Rules | Next.js Rules | Total Rules |
502
- | -------------------- | -------- | ---------- | --------- | ------------- | ----------- |
503
- | `base` | warn | 42 | 0 | 0 | 42 |
504
- | `base/recommended` | error | 42 | 0 | 0 | 42 |
505
- | `react` | warn | 42 | 16 | 0 | 58 |
506
- | `react/recommended` | error | 42 | 16 | 0 | 58 |
507
- | `nextjs` | warn | 42 | 16 | 1 | 59 |
508
- | `nextjs/recommended` | error | 42 | 16 | 1 | 59 |
480
+ | Preset | Severity | Base Rules | JSX Rules | Total Rules |
481
+ | -------------------- | -------- | ---------- | --------- | ----------- |
482
+ | `base` | warn | 40 | 0 | 40 |
483
+ | `base/recommended` | error | 40 | 0 | 40 |
484
+ | `react` | warn | 40 | 17 | 57 |
485
+ | `react/recommended` | error | 40 | 17 | 57 |
486
+ | `nextjs` | warn | 40 | 17 | 57 |
487
+ | `nextjs/recommended` | error | 40 | 17 | 57 |
509
488
 
510
- 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.
489
+ The `nextjs` and `nextjs/recommended` presets currently share the same rule set as `react` and `react/recommended`; they are kept as named aliases for ergonomics.
511
490
 
512
- ### Base Configuration Rules (42 rules)
491
+ ### Base Configuration Rules (40 rules)
513
492
 
514
493
  Included in `base`, `base/recommended`, and all other presets:
515
494
 
516
495
  - `nextfriday/boolean-naming-prefix`
517
496
  - `nextfriday/enforce-camel-case`
518
497
  - `nextfriday/enforce-constant-case`
519
- - `nextfriday/enforce-curly-newline`
520
498
  - `nextfriday/enforce-hook-naming`
521
499
  - `nextfriday/enforce-property-case`
522
500
  - `nextfriday/enforce-service-naming`
523
501
  - `nextfriday/enforce-sorted-destructuring`
524
502
  - `nextfriday/enforce-type-declaration-order`
525
- - `nextfriday/file-kebab-case`
526
503
  - `nextfriday/index-export-only`
527
504
  - `nextfriday/newline-after-multiline-block`
528
505
  - `nextfriday/newline-before-return`
@@ -556,7 +533,7 @@ Included in `base`, `base/recommended`, and all other presets:
556
533
  - `nextfriday/sort-type-alphabetically`
557
534
  - `nextfriday/sort-type-required-first`
558
535
 
559
- ### JSX Rules (16 rules)
536
+ ### JSX Rules (17 rules)
560
537
 
561
538
  Additionally included in `react`, `react/recommended`, `nextjs`, `nextjs/recommended`:
562
539
 
@@ -568,21 +545,16 @@ Additionally included in `react`, `react/recommended`, `nextjs`, `nextjs/recomme
568
545
  - `nextfriday/jsx-no-non-component-function`
569
546
  - `nextfriday/jsx-no-ternary-null`
570
547
  - `nextfriday/jsx-no-variable-in-callback`
571
- - `nextfriday/jsx-pascal-case`
572
548
  - `nextfriday/jsx-require-suspense`
573
549
  - `nextfriday/jsx-simple-props`
574
550
  - `nextfriday/jsx-sort-props`
575
551
  - `nextfriday/jsx-spread-props-last`
552
+ - `nextfriday/prefer-interface-for-component-props`
576
553
  - `nextfriday/prefer-interface-over-inline-types`
577
554
  - `nextfriday/prefer-jsx-template-literals`
555
+ - `nextfriday/prefer-props-with-children`
578
556
  - `nextfriday/react-props-destructure`
579
557
 
580
- ### Next.js Only Rules (1 rule)
581
-
582
- Additionally included in `nextjs`, `nextjs/recommended` only:
583
-
584
- - `nextfriday/nextjs-require-public-env`
585
-
586
558
  ### Severity Levels
587
559
 
588
560
  - **`base` / `react` / `nextjs`**: All rules set to `"warn"`
@@ -601,7 +573,7 @@ Additionally included in `nextjs`, `nextjs/recommended` only:
601
573
 
602
574
  ## Agent Skill
603
575
 
604
- This plugin ships with an [Agent Skill](https://github.com/anthropics/skills) that teaches AI coding assistants (Claude Code, Cursor, etc.) all 56 rules so they generate compliant code from the start.
576
+ This plugin ships with an [Agent Skill](https://github.com/anthropics/skills) that teaches AI coding assistants (Claude Code, Cursor, etc.) all 57 rules so they generate compliant code from the start.
605
577
 
606
578
  ```bash
607
579
  npx skills add next-friday/eslint-plugin-nextfriday --skill eslint-plugin-nextfriday
@@ -0,0 +1,51 @@
1
+ # Domain Docs
2
+
3
+ How the engineering skills should consume this repo's domain documentation when exploring the codebase.
4
+
5
+ ## Before exploring, read these
6
+
7
+ - **`CONTEXT.md`** at the repo root, or
8
+ - **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
9
+ - **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
10
+
11
+ If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The producer skill (`/grill-with-docs`) creates them lazily when terms or decisions actually get resolved.
12
+
13
+ ## File structure
14
+
15
+ Single-context repo (most repos):
16
+
17
+ ```
18
+ /
19
+ ├── CONTEXT.md
20
+ ├── docs/adr/
21
+ │ ├── 0001-event-sourced-orders.md
22
+ │ └── 0002-postgres-for-write-model.md
23
+ └── src/
24
+ ```
25
+
26
+ Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
27
+
28
+ ```
29
+ /
30
+ ├── CONTEXT-MAP.md
31
+ ├── docs/adr/ ← system-wide decisions
32
+ └── src/
33
+ ├── ordering/
34
+ │ ├── CONTEXT.md
35
+ │ └── docs/adr/ ← context-specific decisions
36
+ └── billing/
37
+ ├── CONTEXT.md
38
+ └── docs/adr/
39
+ ```
40
+
41
+ ## Use the glossary's vocabulary
42
+
43
+ When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
44
+
45
+ If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/grill-with-docs`).
46
+
47
+ ## Flag ADR conflicts
48
+
49
+ If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
50
+
51
+ > _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
@@ -0,0 +1,22 @@
1
+ # Issue tracker: GitHub
2
+
3
+ Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
4
+
5
+ ## Conventions
6
+
7
+ - **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
8
+ - **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
9
+ - **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
10
+ - **Comment on an issue**: `gh issue comment <number> --body "..."`
11
+ - **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
12
+ - **Close**: `gh issue close <number> --comment "..."`
13
+
14
+ Infer the repo from `git remote -v` — `gh` does this automatically when run inside a clone.
15
+
16
+ ## When a skill says "publish to the issue tracker"
17
+
18
+ Create a GitHub issue.
19
+
20
+ ## When a skill says "fetch the relevant ticket"
21
+
22
+ Run `gh issue view <number> --comments`.
@@ -0,0 +1,15 @@
1
+ # Triage Labels
2
+
3
+ The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
4
+
5
+ | Label in mattpocock/skills | Label in our tracker | Meaning |
6
+ | -------------------------- | -------------------- | ---------------------------------------- |
7
+ | `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
8
+ | `needs-info` | `needs-info` | Waiting on reporter for more information |
9
+ | `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
10
+ | `ready-for-human` | `ready-for-human` | Requires human implementation |
11
+ | `wontfix` | `wontfix` | Will not be actioned |
12
+
13
+ When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
14
+
15
+ Edit the right-hand column to match whatever vocabulary you actually use.
@@ -1,62 +1,80 @@
1
1
  # no-inline-nested-object
2
2
 
3
- Require nested objects and arrays with multiple properties to span multiple lines.
3
+ Require object or array values that contain further nested objects or arrays to span multiple lines.
4
4
 
5
5
  ## Rule Details
6
6
 
7
- This rule enforces that when an object property's value is another object or array with more than one element, it should span multiple lines rather than being written inline. Single-property nested objects are allowed inline. This improves readability and makes diffs cleaner when properties are added or removed.
7
+ This rule enforces that when an object property's value is itself an object or array **that contains further nested structures inside**, it must span multiple lines. Flat collections objects of primitive properties or arrays of simple references are allowed inline because Prettier already controls their wrapping via `printWidth`.
8
+
9
+ A "nested structure" is any object or array element/property whose value is another object or array. The rule deliberately ignores depth-1 collections so it does not fight Prettier on simple data and configuration tables.
10
+
11
+ ### Why?
12
+
13
+ Truly nested structures are easy to misread when collapsed onto a single line, and adding or removing an inner element produces noisy diffs. Flat collections do not have the same problem and are best left to Prettier's line-length logic.
8
14
 
9
15
  ## Examples
10
16
 
11
17
  ### Incorrect
12
18
 
19
+ <!-- prettier-ignore -->
13
20
  ```ts
14
- const config = {
15
- database: { host: "localhost", port: 5432, name: "myapp" },
21
+ const obj = {
22
+ items: [{ a: 1 }, { b: 2 }],
23
+ };
24
+ ```
25
+
26
+ <!-- prettier-ignore -->
27
+ ```ts
28
+ const obj = {
29
+ matrix: [[1, 2], [3, 4]],
16
30
  };
17
31
  ```
18
32
 
19
33
  ```ts
20
- const routes = {
21
- api: { users: "/api/users", posts: "/api/posts" },
22
- auth: { login: "/auth/login", logout: "/auth/logout" },
34
+ const obj = {
35
+ layer: { inner: { leaf: 1 } },
36
+ };
37
+ ```
38
+
39
+ ```ts
40
+ const obj = {
41
+ wrap: { items: [1, 2, 3] },
23
42
  };
24
43
  ```
25
44
 
26
45
  ### Correct
27
46
 
47
+ <!-- prettier-ignore -->
28
48
  ```ts
29
- const config = {
30
- database: { host: "localhost" },
49
+ const obj = {
50
+ items: [
51
+ { a: 1 },
52
+ { b: 2 },
53
+ ],
31
54
  };
32
55
  ```
33
56
 
34
57
  ```ts
35
- const config = {
36
- database: {
37
- host: "localhost",
38
- port: 5432,
39
- name: "myapp",
58
+ const obj = {
59
+ layer: {
60
+ inner: { leaf: 1 },
40
61
  },
41
62
  };
42
63
  ```
43
64
 
65
+ Flat values stay inline:
66
+
44
67
  ```ts
45
- const routes = {
46
- api: {
47
- users: "/api/users",
48
- posts: "/api/posts",
49
- },
50
- auth: {
51
- login: "/auth/login",
52
- logout: "/auth/logout",
53
- },
68
+ const obj = {
69
+ config: { enabled: true, timeout: 5000 },
70
+ database: { host: "localhost", port: 5432, name: "myapp" },
54
71
  };
55
72
  ```
56
73
 
57
74
  ```ts
58
- const validationRules = {
59
- required: ["name", "email", "password"],
75
+ const obj = {
76
+ options: ["primary", "foreground", "danger", "outline", "ghost", "link"],
77
+ allow: [target.utils, target.types, target.constants],
60
78
  };
61
79
  ```
62
80
 
@@ -71,8 +89,8 @@ const initialState = {
71
89
 
72
90
  ## When Not To Use It
73
91
 
74
- If you prefer compact inline nested objects for all cases, or if your team has different formatting preferences.
92
+ If your team prefers compact single-line nested structures regardless of depth, or if your project has different formatting preferences.
75
93
 
76
94
  ## Fixable
77
95
 
78
- This rule is auto-fixable. Running ESLint with the `--fix` flag will automatically expand inline nested objects and arrays with multiple properties to multiple lines.
96
+ This rule is auto-fixable. Running ESLint with the `--fix` flag will expand inline collections that contain nested structures onto multiple lines.
@@ -36,12 +36,14 @@ function validate(user) {
36
36
  function process(data) {
37
37
  if (!data) return [];
38
38
  if (!data.items) return [];
39
+
39
40
  return data.items.map(toItem);
40
41
  }
41
42
 
42
43
  function validate(user) {
43
44
  if (!user) return null;
44
45
  if (!user.isAdmin) return null;
46
+
45
47
  return adminDashboard();
46
48
  }
47
49
  ```
@@ -61,6 +61,10 @@ import { TSESTree } from "@typescript-eslint/utils";
61
61
  import type { TSESTree } from "@typescript-eslint/utils";
62
62
  ```
63
63
 
64
+ ## Exceptions
65
+
66
+ This rule defers to [`no-inline-type-import`](./NO_INLINE_TYPE_IMPORT.md) when an import already contains an inline `type` marker (for example, `import { type Foo } from "x"`). Reporting both rules on the same statement produces overlapping messages and conflicting fixes, so `prefer-import-type` skips the import in that case and lets `no-inline-type-import` produce the canonical fix.
67
+
64
68
  ## When Not To Use It
65
69
 
66
70
  - If your project doesn't use TypeScript
@@ -69,4 +73,5 @@ import type { TSESTree } from "@typescript-eslint/utils";
69
73
 
70
74
  ## Related Rules
71
75
 
76
+ - [no-inline-type-import](./NO_INLINE_TYPE_IMPORT.md) - Owns the `import { type Foo }` case
72
77
  - [@typescript-eslint/consistent-type-imports](https://typescript-eslint.io/rules/consistent-type-imports/) - Similar rule from typescript-eslint
@@ -0,0 +1,53 @@
1
+ # prefer-interface-for-component-props
2
+
3
+ Enforce `interface` over `type` alias for component prop declarations in component files (`*.tsx`, `*.jsx`).
4
+
5
+ > This rule is auto-fixable using `--fix`.
6
+
7
+ ## Rule Details
8
+
9
+ In component files, prop declarations are more idiomatic as `interface` than `type` aliases. Interfaces support declaration merging, are easier to extend, and produce clearer error messages from the TypeScript compiler. This rule converts `type` aliases that look like component prop types into `interface` declarations.
10
+
11
+ The rule fires only when all of the following are true:
12
+
13
+ - The file extension is `.tsx` or `.jsx`
14
+ - The declaration is a `type` alias whose name ends with `Props`
15
+ - The body of the type is an object literal (`{ ... }`)
16
+
17
+ Type aliases with intersection types (`type FooProps = Other & { x: number }`), union types (`type Variant = 'a' | 'b'`), or non-object bodies (`type Props = ReactNode`) are left alone — they cannot be expressed as a single interface without restructuring.
18
+
19
+ ## Examples
20
+
21
+ ### Incorrect
22
+
23
+ ```tsx
24
+ type StorePopoverProps = {
25
+ trigger: ReactNode;
26
+ };
27
+
28
+ const StorePopover = (props: Readonly<StorePopoverProps>) => {
29
+ return <div>{props.trigger}</div>;
30
+ };
31
+ ```
32
+
33
+ ### Correct
34
+
35
+ ```tsx
36
+ interface StorePopoverProps {
37
+ trigger: ReactNode;
38
+ }
39
+
40
+ const StorePopover = (props: Readonly<StorePopoverProps>) => {
41
+ return <div>{props.trigger}</div>;
42
+ };
43
+ ```
44
+
45
+ ## When Not To Use It
46
+
47
+ - If your codebase uses `type` aliases for component props by convention
48
+ - If you rely on type alias features that interfaces don't support (for example, intersections or unions in the same declaration)
49
+
50
+ ## Related Rules
51
+
52
+ - [enforce-props-suffix](./ENFORCE_PROPS_SUFFIX.md) - Enforces the `Props` suffix that this rule keys off
53
+ - [prefer-interface-over-inline-types](./PREFER_INTERFACE_OVER_INLINE_TYPES.md) - Sister rule for inline types in component params
@@ -89,6 +89,10 @@ const setup = (config: Config) => {
89
89
  };
90
90
  ```
91
91
 
92
+ ## Exceptions
93
+
94
+ This rule defers to [`prefer-interface-over-inline-types`](./PREFER_INTERFACE_OVER_INLINE_TYPES.md) for React components with a single non-destructured prop parameter (for example, `function Comp(props: { name: string }) { return <div /> }`). Both rules would otherwise report the same parameter with different messages, so `prefer-named-param-types` skips that case and lets the React-specific rule produce the canonical message. Destructured component props (`function Comp({ name }: { name: string })`) and non-component functions are still reported here.
95
+
92
96
  ## When Not To Use It
93
97
 
94
98
  - If you prefer inline types for simple, one-off function parameters
@@ -97,4 +101,4 @@ const setup = (config: Config) => {
97
101
 
98
102
  ## Related Rules
99
103
 
100
- - [prefer-interface-over-inline-types](./PREFER_INTERFACE_OVER_INLINE_TYPES.md) - Similar rule focused on React component props with complexity criteria
104
+ - [prefer-interface-over-inline-types](./PREFER_INTERFACE_OVER_INLINE_TYPES.md) - Owns React component non-destructured prop case