eslint-plugin-nextfriday 3.2.1 → 4.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 +26 -0
- package/README.md +36 -70
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +22 -0
- package/docs/agents/triage-labels.md +15 -0
- package/docs/rules/NO_GHOST_WRAPPER.md +75 -0
- package/docs/rules/NO_INLINE_NESTED_OBJECT.md +45 -27
- package/docs/rules/NO_REDUNDANT_FRAGMENT.md +56 -0
- package/docs/rules/PREFER_GUARD_CLAUSE.md +2 -0
- package/docs/rules/PREFER_IMPORT_TYPE.md +5 -0
- package/docs/rules/PREFER_INTERFACE_FOR_COMPONENT_PROPS.md +53 -0
- package/docs/rules/PREFER_NAMED_PARAM_TYPES.md +5 -1
- package/docs/rules/PREFER_PROPS_WITH_CHILDREN.md +112 -0
- package/lib/index.cjs +748 -679
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +176 -200
- package/lib/index.d.ts +176 -200
- package/lib/index.js +685 -616
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/docs/rules/ENFORCE_CURLY_NEWLINE.md +0 -55
- package/docs/rules/FILE_KEBAB_CASE.md +0 -70
- package/docs/rules/JSX_PASCAL_CASE.md +0 -71
- package/docs/rules/NEXTJS_REQUIRE_PUBLIC_ENV.md +0 -44
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# eslint-plugin-nextfriday
|
|
2
2
|
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#124](https://github.com/next-friday/eslint-plugin-nextfriday/pull/124) [`be12809`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/be128098e2af102a153c3d01546a2921518f4b96) Thanks [@joetakara](https://github.com/joetakara)! - add two JSX rules targeting unnecessary wrapper noise (the "Divitis" anti-pattern):
|
|
8
|
+
- `no-ghost-wrapper` flags bare `<div>` / `<span>` elements that carry no meaningful attributes. Self-closing variants are checked the same way. The `key` prop alone does not silence the rule, since `key` carries no structural intent. Any other attribute — `className`, `style`, `id`, `ref`, `role`, `aria-*`, `data-*`, `tabIndex`, event handlers, or spread attributes — is considered meaningful and lets the element pass.
|
|
9
|
+
- `no-redundant-fragment` flags Fragments (`<>...</>`, `<Fragment>`, `<React.Fragment>`) that wrap zero or one child. JSX text consisting only of whitespace is not counted. Long-form Fragments carrying a `key` attribute are exempt, since `key` is the canonical reason long-form Fragment exists (the shorthand `<>...</>` cannot accept attributes).
|
|
10
|
+
|
|
11
|
+
Both rules are report-only — no autofix is provided so authors retain control over which structural alternative (Fragment, semantic element, unwrapping the children) best fits the surrounding code. Both are added to the JSX rule tier and ship in the `react`, `react/recommended`, `nextjs`, and `nextjs/recommended` presets at `warn` and `error` severity respectively.
|
|
12
|
+
|
|
13
|
+
## 4.0.0
|
|
14
|
+
|
|
15
|
+
### Major Changes
|
|
16
|
+
|
|
17
|
+
- [#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
|
|
18
|
+
- Remove `enforce-curly-newline` rule. Use ESLint's built-in `curly: "all"` instead. Consumers should add `"curly": ["error", "all"]` to their ESLint config.
|
|
19
|
+
- 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] }`.
|
|
20
|
+
|
|
21
|
+
New rules
|
|
22
|
+
- Add `prefer-props-with-children`. Reports `children: ReactNode` declarations and recommends `PropsWithChildren<T>`. Included in `react` and `nextjs` presets.
|
|
23
|
+
- Add `prefer-interface-for-component-props`. Auto-fixes `type FooProps = {...}` to `interface FooProps {...}` in `.tsx` and `.jsx` files. Included in `react` and `nextjs` presets.
|
|
24
|
+
|
|
25
|
+
Fixes
|
|
26
|
+
- `prefer-import-type` skips imports with inline `type` markers, deferring to `no-inline-type-import`.
|
|
27
|
+
- `prefer-named-param-types` skips React component functions with single non-destructured `Identifier` param, deferring to `prefer-interface-over-inline-types`.
|
|
28
|
+
|
|
3
29
|
## 3.2.1
|
|
4
30
|
|
|
5
31
|
### 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,
|
|
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 `
|
|
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
|
|
392
|
-
| ------------------------------------- |
|
|
393
|
-
| High — correctness and runtime safety | `no-direct-date`, `no-env-fallback`, `
|
|
394
|
-
| Medium — structure and naming | `boolean-naming-prefix`, `enforce-camel-case`, `enforce-constant-case`, `
|
|
395
|
-
| Low — formatting and ordering | `sort-imports`, `sort-exports`, `sort-type-alphabetically`, `jsx-sort-props`, `newline-before-return`, `newline-after-multiline-block`, `no-emoji`
|
|
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
|
|
462
|
-
|
|
|
463
|
-
| [enforce-type-declaration-order](docs/rules/ENFORCE_TYPE_DECLARATION_ORDER.md)
|
|
464
|
-
| [no-nested-interface-declaration](docs/rules/NO_NESTED_INTERFACE_DECLARATION.md)
|
|
465
|
-
| [prefer-named-param-types](docs/rules/PREFER_NAMED_PARAM_TYPES.md)
|
|
466
|
-
| [prefer-inline-literal-union](docs/rules/PREFER_INLINE_LITERAL_UNION.md)
|
|
467
|
-
| [prefer-inline-type-export](docs/rules/PREFER_INLINE_TYPE_EXPORT.md)
|
|
468
|
-
| [prefer-interface-
|
|
469
|
-
| [
|
|
470
|
-
| [sort-type-
|
|
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
|
|
|
@@ -483,46 +467,41 @@ In practice: turn the high tier on as `"error"` first, leave the medium tier as
|
|
|
483
467
|
| [jsx-simple-props](docs/rules/JSX_SIMPLE_PROPS.md) | Enforce simple prop values (strings, variables, callbacks, ReactNode) | ❌ |
|
|
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 | ❌ |
|
|
470
|
+
| [no-ghost-wrapper](docs/rules/NO_GHOST_WRAPPER.md) | Disallow bare `<div>`/`<span>` with no meaningful attributes | ❌ |
|
|
471
|
+
| [no-redundant-fragment](docs/rules/NO_REDUNDANT_FRAGMENT.md) | Disallow Fragments wrapping zero or one child | ❌ |
|
|
486
472
|
| [prefer-jsx-template-literals](docs/rules/PREFER_JSX_TEMPLATE_LITERALS.md) | Enforce template literals instead of mixing text and JSX expressions | ✅ |
|
|
473
|
+
| [prefer-props-with-children](docs/rules/PREFER_PROPS_WITH_CHILDREN.md) | Prefer PropsWithChildren over manually declaring children: ReactNode | ❌ |
|
|
487
474
|
| [react-props-destructure](docs/rules/REACT_PROPS_DESTRUCTURE.md) | Enforce destructuring props inside React component body | ❌ |
|
|
488
475
|
| [enforce-props-suffix](docs/rules/ENFORCE_PROPS_SUFFIX.md) | Enforce 'Props' suffix for interfaces and types in \*.tsx files | ❌ |
|
|
489
476
|
| [enforce-readonly-component-props](docs/rules/ENFORCE_READONLY_COMPONENT_PROPS.md) | Enforce Readonly wrapper for React component props | ✅ |
|
|
490
477
|
|
|
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
478
|
## Configurations
|
|
498
479
|
|
|
499
480
|
### Configuration Presets Overview
|
|
500
481
|
|
|
501
|
-
| Preset | Severity | Base Rules | JSX Rules |
|
|
502
|
-
| -------------------- | -------- | ---------- | --------- |
|
|
503
|
-
| `base` | warn |
|
|
504
|
-
| `base/recommended` | error |
|
|
505
|
-
| `react` | warn |
|
|
506
|
-
| `react/recommended` | error |
|
|
507
|
-
| `nextjs` | warn |
|
|
508
|
-
| `nextjs/recommended` | error |
|
|
482
|
+
| Preset | Severity | Base Rules | JSX Rules | Total Rules |
|
|
483
|
+
| -------------------- | -------- | ---------- | --------- | ----------- |
|
|
484
|
+
| `base` | warn | 40 | 0 | 40 |
|
|
485
|
+
| `base/recommended` | error | 40 | 0 | 40 |
|
|
486
|
+
| `react` | warn | 40 | 19 | 59 |
|
|
487
|
+
| `react/recommended` | error | 40 | 19 | 59 |
|
|
488
|
+
| `nextjs` | warn | 40 | 19 | 59 |
|
|
489
|
+
| `nextjs/recommended` | error | 40 | 19 | 59 |
|
|
509
490
|
|
|
510
|
-
The `nextjs` and `nextjs/recommended` presets
|
|
491
|
+
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
492
|
|
|
512
|
-
### Base Configuration Rules (
|
|
493
|
+
### Base Configuration Rules (40 rules)
|
|
513
494
|
|
|
514
495
|
Included in `base`, `base/recommended`, and all other presets:
|
|
515
496
|
|
|
516
497
|
- `nextfriday/boolean-naming-prefix`
|
|
517
498
|
- `nextfriday/enforce-camel-case`
|
|
518
499
|
- `nextfriday/enforce-constant-case`
|
|
519
|
-
- `nextfriday/enforce-curly-newline`
|
|
520
500
|
- `nextfriday/enforce-hook-naming`
|
|
521
501
|
- `nextfriday/enforce-property-case`
|
|
522
502
|
- `nextfriday/enforce-service-naming`
|
|
523
503
|
- `nextfriday/enforce-sorted-destructuring`
|
|
524
504
|
- `nextfriday/enforce-type-declaration-order`
|
|
525
|
-
- `nextfriday/file-kebab-case`
|
|
526
505
|
- `nextfriday/index-export-only`
|
|
527
506
|
- `nextfriday/newline-after-multiline-block`
|
|
528
507
|
- `nextfriday/newline-before-return`
|
|
@@ -556,7 +535,7 @@ Included in `base`, `base/recommended`, and all other presets:
|
|
|
556
535
|
- `nextfriday/sort-type-alphabetically`
|
|
557
536
|
- `nextfriday/sort-type-required-first`
|
|
558
537
|
|
|
559
|
-
### JSX Rules (
|
|
538
|
+
### JSX Rules (19 rules)
|
|
560
539
|
|
|
561
540
|
Additionally included in `react`, `react/recommended`, `nextjs`, `nextjs/recommended`:
|
|
562
541
|
|
|
@@ -568,21 +547,18 @@ Additionally included in `react`, `react/recommended`, `nextjs`, `nextjs/recomme
|
|
|
568
547
|
- `nextfriday/jsx-no-non-component-function`
|
|
569
548
|
- `nextfriday/jsx-no-ternary-null`
|
|
570
549
|
- `nextfriday/jsx-no-variable-in-callback`
|
|
571
|
-
- `nextfriday/jsx-pascal-case`
|
|
572
550
|
- `nextfriday/jsx-require-suspense`
|
|
573
551
|
- `nextfriday/jsx-simple-props`
|
|
574
552
|
- `nextfriday/jsx-sort-props`
|
|
575
553
|
- `nextfriday/jsx-spread-props-last`
|
|
554
|
+
- `nextfriday/no-ghost-wrapper`
|
|
555
|
+
- `nextfriday/no-redundant-fragment`
|
|
556
|
+
- `nextfriday/prefer-interface-for-component-props`
|
|
576
557
|
- `nextfriday/prefer-interface-over-inline-types`
|
|
577
558
|
- `nextfriday/prefer-jsx-template-literals`
|
|
559
|
+
- `nextfriday/prefer-props-with-children`
|
|
578
560
|
- `nextfriday/react-props-destructure`
|
|
579
561
|
|
|
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
562
|
### Severity Levels
|
|
587
563
|
|
|
588
564
|
- **`base` / `react` / `nextjs`**: All rules set to `"warn"`
|
|
@@ -599,16 +575,6 @@ Additionally included in `nextjs`, `nextjs/recommended` only:
|
|
|
599
575
|
- **Clean code practices**: Prevents emoji usage, enforces parameter destructuring, and more
|
|
600
576
|
- **Formatting rules**: Enforces consistent blank lines around multi-line blocks and return statements
|
|
601
577
|
|
|
602
|
-
## Agent Skill
|
|
603
|
-
|
|
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.
|
|
605
|
-
|
|
606
|
-
```bash
|
|
607
|
-
npx skills add next-friday/eslint-plugin-nextfriday --skill eslint-plugin-nextfriday
|
|
608
|
-
```
|
|
609
|
-
|
|
610
|
-
Once installed, AI assistants will automatically follow the naming, code style, type, JSX, import, and formatting patterns enforced by this plugin — reducing lint errors to zero.
|
|
611
|
-
|
|
612
578
|
## Need Help?
|
|
613
579
|
|
|
614
580
|
If you encounter any issues or have questions:
|
|
@@ -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.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# no-ghost-wrapper
|
|
2
|
+
|
|
3
|
+
Disallow bare `<div>` and `<span>` elements that have no meaningful attributes.
|
|
4
|
+
|
|
5
|
+
## Rule Details
|
|
6
|
+
|
|
7
|
+
This rule flags `<div>` and `<span>` elements that carry zero meaningful attributes — the "ghost wrapper" anti-pattern (also called "Divitis"). These elements add depth to the DOM tree without contributing semantics, styling, behavior, accessibility hooks, or test hooks.
|
|
8
|
+
|
|
9
|
+
The `key` prop alone does not count as meaningful: `key` is a React-only signal for list reconciliation and carries no structural intent.
|
|
10
|
+
|
|
11
|
+
### Why?
|
|
12
|
+
|
|
13
|
+
- **Semantic clarity**: A wrapper without attributes signals that the author has not decided whether the element is structural, presentational, or semantic.
|
|
14
|
+
- **DOM bloat**: Empty wrappers extend the DOM tree, increasing layout work and accessibility-tree noise.
|
|
15
|
+
- **Cognitive load**: Reviewers must guess whether a bare wrapper is intentional or a leftover from refactoring.
|
|
16
|
+
- **Better alternatives exist**: Fragments (`<>...</>`), semantic elements (`<section>`, `<article>`, `<header>`, `<nav>`), or simply unwrapping the children.
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Incorrect
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<div>x</div>
|
|
24
|
+
<span>text</span>
|
|
25
|
+
<div></div>
|
|
26
|
+
<div> </div>
|
|
27
|
+
<div />
|
|
28
|
+
<span />
|
|
29
|
+
<div key={item.id}>x</div>
|
|
30
|
+
<div>{children}</div>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Correct
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
<div className="container">x</div>
|
|
37
|
+
<div data-testid="root">x</div>
|
|
38
|
+
<div role="button">x</div>
|
|
39
|
+
<div aria-label="close">x</div>
|
|
40
|
+
<div ref={ref}>x</div>
|
|
41
|
+
<div onClick={handleClick}>x</div>
|
|
42
|
+
<div id="anchor">x</div>
|
|
43
|
+
<div style={{ color: "red" }}>x</div>
|
|
44
|
+
<div {...props}>x</div>
|
|
45
|
+
<div tabIndex={0}>x</div>
|
|
46
|
+
<section>x</section>
|
|
47
|
+
<article>x</article>
|
|
48
|
+
<>x</>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## What This Rule Checks
|
|
52
|
+
|
|
53
|
+
The rule fires on a `<div>` or `<span>` opening element when, after filtering out a lone `key` attribute, it has zero remaining attributes (including spread attributes). Self-closing elements (`<div />`, `<span />`) are checked the same way.
|
|
54
|
+
|
|
55
|
+
Any of the following attributes count as meaningful and silence the rule:
|
|
56
|
+
|
|
57
|
+
- `className`
|
|
58
|
+
- `style`
|
|
59
|
+
- `id`
|
|
60
|
+
- `ref`
|
|
61
|
+
- `role`
|
|
62
|
+
- `aria-*`
|
|
63
|
+
- `data-*`
|
|
64
|
+
- `tabIndex`
|
|
65
|
+
- Event handlers (`onClick`, `onChange`, etc.)
|
|
66
|
+
- Spread attributes (`{...props}`)
|
|
67
|
+
- Any other JSX attribute except `key`
|
|
68
|
+
|
|
69
|
+
## When Not To Use It
|
|
70
|
+
|
|
71
|
+
If your codebase intentionally uses bare `<div>` and `<span>` as structural placeholders, or if you rely on parent CSS selectors that target a fixed wrapper depth.
|
|
72
|
+
|
|
73
|
+
## Related Rules
|
|
74
|
+
|
|
75
|
+
- [no-redundant-fragment](NO_REDUNDANT_FRAGMENT.md)
|
|
@@ -1,62 +1,80 @@
|
|
|
1
1
|
# no-inline-nested-object
|
|
2
2
|
|
|
3
|
-
Require nested objects
|
|
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
|
|
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
|
|
15
|
-
|
|
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
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
30
|
-
|
|
49
|
+
const obj = {
|
|
50
|
+
items: [
|
|
51
|
+
{ a: 1 },
|
|
52
|
+
{ b: 2 },
|
|
53
|
+
],
|
|
31
54
|
};
|
|
32
55
|
```
|
|
33
56
|
|
|
34
57
|
```ts
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
59
|
-
|
|
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
|
|
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
|
|
96
|
+
This rule is auto-fixable. Running ESLint with the `--fix` flag will expand inline collections that contain nested structures onto multiple lines.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# no-redundant-fragment
|
|
2
|
+
|
|
3
|
+
Disallow Fragments that wrap zero or one child.
|
|
4
|
+
|
|
5
|
+
## Rule Details
|
|
6
|
+
|
|
7
|
+
This rule flags Fragments (`<>...</>`, `<Fragment>...</Fragment>`, `<React.Fragment>...</React.Fragment>`) that wrap zero or one child. A Fragment is only justified when grouping multiple sibling nodes, or when a `key` prop is required during list iteration.
|
|
8
|
+
|
|
9
|
+
A Fragment with a single child is structurally equivalent to that child alone. An empty Fragment renders nothing and adds noise to the source.
|
|
10
|
+
|
|
11
|
+
### Why?
|
|
12
|
+
|
|
13
|
+
- **Source clarity**: A Fragment around a single child is dead syntax — it tells the reader nothing.
|
|
14
|
+
- **Less noise in the AST**: Devtools, search, and codemods do not have to step over an extra layer.
|
|
15
|
+
- **Forces a decision**: Either the children are siblings (Fragment is the right tool) or they are not (drop it).
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
### Incorrect
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<></>
|
|
23
|
+
<>{x}</>
|
|
24
|
+
<><A /></>
|
|
25
|
+
<>text</>
|
|
26
|
+
<Fragment>x</Fragment>
|
|
27
|
+
<React.Fragment>{x}</React.Fragment>
|
|
28
|
+
<React.Fragment></React.Fragment>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Correct
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<>{a}{b}</>
|
|
35
|
+
<><A /><B /></>
|
|
36
|
+
<Fragment>{a}{b}</Fragment>
|
|
37
|
+
<React.Fragment>{a}{b}</React.Fragment>
|
|
38
|
+
|
|
39
|
+
// key is the legitimate reason to use long-form Fragment:
|
|
40
|
+
<React.Fragment key={item.id}>{item.label}{item.value}</React.Fragment>
|
|
41
|
+
<Fragment key={k}>{x}</Fragment>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## What This Rule Checks
|
|
45
|
+
|
|
46
|
+
A Fragment is flagged when its meaningful children count is `0` or `1`. JSX text nodes that contain only whitespace are not counted as children.
|
|
47
|
+
|
|
48
|
+
The rule does not fire on a long-form Fragment (`<Fragment>` or `<React.Fragment>`) that carries a `key` attribute — `key` is the canonical reason long-form Fragment exists, since the shorthand `<>...</>` cannot accept attributes.
|
|
49
|
+
|
|
50
|
+
## When Not To Use It
|
|
51
|
+
|
|
52
|
+
If your codebase uses single-child Fragments to keep diffs stable around conditionally-rendered siblings, or if you rely on a build-time transform that depends on the Fragment wrapper.
|
|
53
|
+
|
|
54
|
+
## Related Rules
|
|
55
|
+
|
|
56
|
+
- [no-ghost-wrapper](NO_GHOST_WRAPPER.md)
|
|
@@ -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
|