eslint-plugin-nextfriday 1.19.0 → 1.21.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,28 @@
1
1
  # eslint-plugin-nextfriday
2
2
 
3
+ ## 1.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#87](https://github.com/next-friday/eslint-plugin-nextfriday/pull/87) [`ed401e3`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/ed401e39db04857995ddcc29d7444298181ee60b) Thanks [@joetakara](https://github.com/joetakara)! - Add `no-inline-return-properties` rule that enforces shorthand-only properties in return objects
8
+
9
+ - [#90](https://github.com/next-friday/eslint-plugin-nextfriday/pull/90) [`d5e9db5`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/d5e9db562cb9e38beadd59da39f8618b6f5825ed) Thanks [@joetakara](https://github.com/joetakara)! - Add comprehensive naming convention rules and fix prefer-import-type JSX prop bug
10
+ - Add `enforce-camel-case` rule: ban snake_case variables/functions, restrict PascalCase to React components
11
+ - Add `enforce-property-case` rule: enforce camelCase for unquoted object property keys
12
+ - Refactor `enforce-constant-case` to only check global scope, support RegExp/objects/arrays/as const
13
+ - Refactor `no-misleading-constant-case` to flag SCREAMING_SNAKE_CASE in local scope
14
+ - Fix `prefer-import-type` incorrectly converting imports used as JSX prop values to type imports
15
+
16
+ ## 1.20.0
17
+
18
+ ### Minor Changes
19
+
20
+ - [#85](https://github.com/next-friday/eslint-plugin-nextfriday/pull/85) [`280a864`](https://github.com/next-friday/eslint-plugin-nextfriday/commit/280a864f31d32433856d6914f742861d3bb43ec9) Thanks [@joetakara](https://github.com/joetakara)! - feat(rules): add no-misleading-constant-case rule and improve enforce-constant-case
21
+ - Add `no-misleading-constant-case` rule that disallows SCREAMING_SNAKE_CASE for mutable bindings (let/var) and non-static values (function calls, objects, arrays, dynamic templates, computed expressions)
22
+ - Add `prefer-inline-type-export` rule that requires type/interface declarations to be exported inline
23
+ - Update `enforce-constant-case` to exempt booleans with standard prefixes (is, has, should, etc.) to resolve conflict with `boolean-naming-prefix`
24
+ - Update existing rules: enforce-constant-case, jsx-newline-between-elements, no-lazy-identifiers
25
+
3
26
  ## 1.19.0
4
27
 
5
28
  ### Minor Changes
package/README.md CHANGED
@@ -117,6 +117,7 @@ export default [
117
117
  "nextfriday/no-nested-interface-declaration": "error",
118
118
  "nextfriday/prefer-named-param-types": "error",
119
119
  "nextfriday/prefer-inline-literal-union": "error",
120
+ "nextfriday/prefer-inline-type-export": "error",
120
121
  "nextfriday/prefer-interface-over-inline-types": "error",
121
122
  "nextfriday/sort-type-alphabetically": "error",
122
123
  "nextfriday/sort-type-required-first": "error",
@@ -149,12 +150,15 @@ export default [
149
150
 
150
151
  ### Variable Naming Rules
151
152
 
152
- | Rule | Description | Fixable |
153
- | ------------------------------------------------------------------ | --------------------------------------------------------------------- | ------- |
154
- | [no-single-char-variables](docs/rules/NO_SINGLE_CHAR_VARIABLES.md) | Disallow single character variable names (e.g., `d`, `u`, `l`) | ❌ |
155
- | [no-lazy-identifiers](docs/rules/NO_LAZY_IDENTIFIERS.md) | Disallow lazy identifiers like `xxx`, `asdf`, `qwerty` | ❌ |
156
- | [boolean-naming-prefix](docs/rules/BOOLEAN_NAMING_PREFIX.md) | Enforce boolean variables to have prefix (is, has, should, can, etc.) | ❌ |
157
- | [enforce-constant-case](docs/rules/ENFORCE_CONSTANT_CASE.md) | Enforce SCREAMING_SNAKE_CASE for constant primitive values | ❌ |
153
+ | Rule | Description | Fixable |
154
+ | ------------------------------------------------------------------------ | --------------------------------------------------------------------- | ------- |
155
+ | [no-single-char-variables](docs/rules/NO_SINGLE_CHAR_VARIABLES.md) | Disallow single character variable names (e.g., `d`, `u`, `l`) | ❌ |
156
+ | [no-lazy-identifiers](docs/rules/NO_LAZY_IDENTIFIERS.md) | Disallow lazy identifiers like `xxx`, `asdf`, `qwerty` | ❌ |
157
+ | [boolean-naming-prefix](docs/rules/BOOLEAN_NAMING_PREFIX.md) | Enforce boolean variables to have prefix (is, has, should, can, etc.) | ❌ |
158
+ | [enforce-camel-case](docs/rules/ENFORCE_CAMEL_CASE.md) | Ban snake_case and restrict PascalCase to React components | ❌ |
159
+ | [enforce-constant-case](docs/rules/ENFORCE_CONSTANT_CASE.md) | Enforce SCREAMING_SNAKE_CASE for global static constant values | ❌ |
160
+ | [enforce-property-case](docs/rules/ENFORCE_PROPERTY_CASE.md) | Enforce camelCase for unquoted object property keys | ❌ |
161
+ | [no-misleading-constant-case](docs/rules/NO_MISLEADING_CONSTANT_CASE.md) | Disallow SCREAMING_SNAKE_CASE in local scope and for dynamic values | ❌ |
158
162
 
159
163
  ### File Naming Rules
160
164
 
@@ -183,6 +187,7 @@ export default [
183
187
  | [newline-after-multiline-block](docs/rules/NEWLINE_AFTER_MULTILINE_BLOCK.md) | Require a blank line after multi-line statements | ✅ |
184
188
  | [newline-before-return](docs/rules/NEWLINE_BEFORE_RETURN.md) | Require a blank line before return statements | ✅ |
185
189
  | [no-inline-nested-object](docs/rules/NO_INLINE_NESTED_OBJECT.md) | Require nested objects and arrays to span multiple lines | ✅ |
190
+ | [no-inline-return-properties](docs/rules/NO_INLINE_RETURN_PROPERTIES.md) | Require return object properties to use shorthand notation | ❌ |
186
191
  | [prefer-async-await](docs/rules/PREFER_ASYNC_AWAIT.md) | Enforce async/await over .then() promise chains | ❌ |
187
192
  | [enforce-curly-newline](docs/rules/ENFORCE_CURLY_NEWLINE.md) | Enforce curly braces for multi-line if, forbid for single-line | ✅ |
188
193
  | [no-nested-ternary](docs/rules/NO_NESTED_TERNARY.md) | Disallow nested ternary expressions | ❌ |
@@ -206,6 +211,7 @@ export default [
206
211
  | [no-nested-interface-declaration](docs/rules/NO_NESTED_INTERFACE_DECLARATION.md) | Disallow inline object types in interface/type properties | ❌ |
207
212
  | [prefer-named-param-types](docs/rules/PREFER_NAMED_PARAM_TYPES.md) | Enforce named types for function parameters with object types | ❌ |
208
213
  | [prefer-inline-literal-union](docs/rules/PREFER_INLINE_LITERAL_UNION.md) | Enforce inlining literal union types for better IDE hover info | ✅ |
214
+ | [prefer-inline-type-export](docs/rules/PREFER_INLINE_TYPE_EXPORT.md) | Require type/interface exports inline at the declaration | ✅ |
209
215
  | [prefer-interface-over-inline-types](docs/rules/PREFER_INTERFACE_OVER_INLINE_TYPES.md) | Enforce interface declarations over inline types for React props | ❌ |
210
216
  | [sort-type-alphabetically](docs/rules/SORT_TYPE_ALPHABETICALLY.md) | Enforce A-Z sorting of properties within type groups | ✅ |
211
217
  | [sort-type-required-first](docs/rules/SORT_TYPE_REQUIRED_FIRST.md) | Enforce required properties before optional in types/interfaces | ✅ |
@@ -214,7 +220,7 @@ export default [
214
220
 
215
221
  | Rule | Description | Fixable |
216
222
  | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------- |
217
- | [jsx-newline-between-elements](docs/rules/JSX_NEWLINE_BETWEEN_ELEMENTS.md) | Require empty lines between sibling multi-line JSX elements | ✅ |
223
+ | [jsx-newline-between-elements](docs/rules/JSX_NEWLINE_BETWEEN_ELEMENTS.md) | Require empty lines between sibling multi-line JSX children | ✅ |
218
224
  | [jsx-no-inline-object-prop](docs/rules/JSX_NO_INLINE_OBJECT_PROP.md) | Disallow inline object literals in JSX props | ❌ |
219
225
  | [jsx-no-newline-single-line-elements](docs/rules/JSX_NO_NEWLINE_SINGLE_LINE_ELEMENTS.md) | Disallow empty lines between single-line sibling JSX elements | ✅ |
220
226
  | [jsx-no-non-component-function](docs/rules/JSX_NO_NON_COMPONENT_FUNCTION.md) | Disallow non-component functions at top level in .tsx/.jsx files | ❌ |
@@ -240,14 +246,14 @@ export default [
240
246
 
241
247
  | Preset | Severity | Base Rules | JSX Rules | Next.js Rules | Total Rules |
242
248
  | -------------------- | -------- | ---------- | --------- | ------------- | ----------- |
243
- | `base` | warn | 36 | 0 | 0 | 36 |
244
- | `base/recommended` | error | 36 | 0 | 0 | 36 |
245
- | `react` | warn | 36 | 15 | 0 | 51 |
246
- | `react/recommended` | error | 36 | 15 | 0 | 51 |
247
- | `nextjs` | warn | 36 | 15 | 1 | 52 |
248
- | `nextjs/recommended` | error | 36 | 15 | 1 | 52 |
249
+ | `base` | warn | 37 | 0 | 0 | 37 |
250
+ | `base/recommended` | error | 37 | 0 | 0 | 37 |
251
+ | `react` | warn | 37 | 15 | 0 | 52 |
252
+ | `react/recommended` | error | 37 | 15 | 0 | 52 |
253
+ | `nextjs` | warn | 37 | 15 | 1 | 53 |
254
+ | `nextjs/recommended` | error | 37 | 15 | 1 | 53 |
249
255
 
250
- ### Base Configuration Rules (36 rules)
256
+ ### Base Configuration Rules (37 rules)
251
257
 
252
258
  Included in `base`, `base/recommended`, and all other presets:
253
259
 
@@ -280,6 +286,7 @@ Included in `base`, `base/recommended`, and all other presets:
280
286
  - `nextfriday/prefer-guard-clause`
281
287
  - `nextfriday/prefer-import-type`
282
288
  - `nextfriday/prefer-inline-literal-union`
289
+ - `nextfriday/prefer-inline-type-export`
283
290
  - `nextfriday/prefer-named-param-types`
284
291
  - `nextfriday/prefer-react-import-types`
285
292
  - `nextfriday/require-explicit-return-type`
@@ -332,7 +339,7 @@ Additionally included in `nextjs`, `nextjs/recommended` only:
332
339
 
333
340
  ## Agent Skill
334
341
 
335
- This plugin ships with an [Agent Skill](https://github.com/anthropics/skills) that teaches AI coding assistants (Claude Code, Cursor, etc.) all 52 rules so they generate compliant code from the start.
342
+ This plugin ships with an [Agent Skill](https://github.com/anthropics/skills) that teaches AI coding assistants (Claude Code, Cursor, etc.) all 53 rules so they generate compliant code from the start.
336
343
 
337
344
  ```bash
338
345
  npx skills add next-friday/eslint-plugin-nextfriday --skill eslint-plugin-nextfriday
@@ -0,0 +1,68 @@
1
+ # enforce-camel-case
2
+
3
+ Enforce camelCase for variables and functions, ban snake_case, and restrict PascalCase to React components.
4
+
5
+ ## Rule Details
6
+
7
+ This rule enforces consistent naming conventions across your codebase:
8
+
9
+ - Variables and functions must use camelCase
10
+ - snake_case is banned for all variable and function declarations
11
+ - PascalCase is reserved exclusively for React components (functions that return JSX)
12
+
13
+ Global static constants are not checked by this rule. Use `enforce-constant-case` for those.
14
+
15
+ ### Why?
16
+
17
+ Consistent naming conventions reduce cognitive load. When PascalCase is reserved for components and camelCase is used for everything else, the name alone tells you what something is.
18
+
19
+ ## Examples
20
+
21
+ ### Incorrect
22
+
23
+ ```ts
24
+ // snake_case variables
25
+ let current_index = 0;
26
+ const first_name = "John";
27
+
28
+ // snake_case functions
29
+ function calculate_total() {
30
+ return 0;
31
+ }
32
+
33
+ // PascalCase for non-component functions
34
+ const CalculateTotal = () => 0;
35
+ const FormatDate = (date: string) => date;
36
+ ```
37
+
38
+ ### Correct
39
+
40
+ ```ts
41
+ // camelCase variables
42
+ let currentIndex = 0;
43
+ const firstName = "John";
44
+
45
+ // camelCase functions
46
+ function calculateTotal() { return 0; }
47
+ const formatDate = (date: string) => date;
48
+
49
+ // PascalCase for React components
50
+ const ArticleCard = () => <div />;
51
+ const WrappedComponent = memo(() => <div />);
52
+ const LazyPage = lazy(() => import("./Page"));
53
+ ```
54
+
55
+ ## Exceptions
56
+
57
+ - Global static constants (handled by `enforce-constant-case`)
58
+ - SCREAMING_SNAKE_CASE variables (handled by `no-misleading-constant-case`)
59
+ - React components wrapped with `memo`, `forwardRef`, or `lazy`
60
+
61
+ ## When Not To Use It
62
+
63
+ If your project does not follow the convention that PascalCase is reserved for React components.
64
+
65
+ ## Related Rules
66
+
67
+ - [enforce-constant-case](ENFORCE_CONSTANT_CASE.md) - Enforces SCREAMING_SNAKE_CASE for global static constants
68
+ - [no-misleading-constant-case](NO_MISLEADING_CONSTANT_CASE.md) - Disallows SCREAMING_SNAKE_CASE in local scope
@@ -1,10 +1,12 @@
1
1
  # enforce-constant-case
2
2
 
3
- Enforce SCREAMING_SNAKE_CASE for constant primitive values.
3
+ Enforce SCREAMING_SNAKE_CASE for global constant static values.
4
4
 
5
5
  ## Rule Details
6
6
 
7
- This rule ensures that `const` declarations with primitive values (strings, numbers, booleans, template literals) use SCREAMING_SNAKE_CASE naming convention. Objects, arrays, and functions are not checked. Only `const` declarations are checked; `let` and `var` are ignored.
7
+ This rule ensures that global-scope `const` declarations with static values use SCREAMING_SNAKE_CASE naming convention. Static values include: string/number/boolean literals, RegExp, static template literals, `as const` assertions, and objects/arrays containing only literal values.
8
+
9
+ Only global scope (top-level of a file) is checked. Local scope constants inside functions are not checked by this rule.
8
10
 
9
11
  ## Examples
10
12
 
@@ -13,9 +15,11 @@ This rule ensures that `const` declarations with primitive values (strings, numb
13
15
  ```ts
14
16
  const defaultCover = "/images/default.jpg";
15
17
  const pageLimit = 10;
16
- const isEnabled = true;
17
18
  const apiBaseUrl = "https://api.example.com";
18
19
  const template = `hello world`;
20
+ const phoneRegex = /^[0-9]{10}$/;
21
+ const default_theme = "dark";
22
+ export const categories = [{ id: "1" }] as const;
19
23
  ```
20
24
 
21
25
  ### Correct
@@ -23,21 +27,39 @@ const template = `hello world`;
23
27
  ```ts
24
28
  const DEFAULT_COVER = "/images/default.jpg";
25
29
  const PAGE_LIMIT = 10;
26
- const IS_ENABLED = true;
27
30
  const API_BASE_URL = "https://api.example.com";
28
31
  const TEMPLATE = `hello world`;
32
+ const PHONE_REGEX = /^[0-9]{10}$/;
33
+ const DEFAULT_THEME = "dark";
34
+ export const CATEGORIES = [{ id: "1" }] as const;
35
+
36
+ const SKELETON_ITEMS = [1, 2, 3, 4, 5];
37
+ const MAP_STYLE = { height: "320px", width: "100%" };
38
+ const STATUS_MAP = { ACTIVE: "active" } as const;
39
+
40
+ // Booleans with standard prefixes (is, has, should, etc.) are exempt
41
+ const isProduction = true;
42
+ const hasAccess = false;
29
43
 
30
- // Objects, arrays, and functions can use camelCase
31
- const config = { key: "value" };
32
- const items = [1, 2, 3];
44
+ // Template literals with expressions are dynamic, camelCase is fine
45
+ const pendingHref = `/branch/${branch.branchNumber}`;
46
+
47
+ // Functions and components are not checked
33
48
  const handleClick = () => {};
49
+ const MyComponent = () => {};
34
50
 
35
- // Let and var declarations are not checked
36
- let defaultCover = "/images/default.jpg";
37
- var pageLimit = 10;
51
+ // Local scope is not checked
52
+ function foo() {
53
+ const maxRetry = 3;
54
+ }
38
55
  ```
39
56
 
40
57
  ## When Not To Use It
41
58
 
42
59
  - If your project uses different naming conventions for constants
43
60
  - If you prefer camelCase for all variable declarations
61
+
62
+ ## Related Rules
63
+
64
+ - [no-misleading-constant-case](NO_MISLEADING_CONSTANT_CASE.md) - Disallows SCREAMING_SNAKE_CASE in local scope and for dynamic values
65
+ - [enforce-camel-case](ENFORCE_CAMEL_CASE.md) - Enforces camelCase for variables and functions
@@ -0,0 +1,63 @@
1
+ # enforce-property-case
2
+
3
+ Enforce camelCase for unquoted object property keys.
4
+
5
+ ## Rule Details
6
+
7
+ This rule bans snake_case and SCREAMING_SNAKE_CASE for unquoted object property keys. Quoted keys (string literals) are allowed for API boundaries where external systems require different conventions.
8
+
9
+ ### Why?
10
+
11
+ Object properties should follow the same camelCase convention as variables. When an external API requires snake_case keys, wrapping them in quotes makes it explicit that the naming is intentional and driven by an external contract.
12
+
13
+ ## Examples
14
+
15
+ ### Incorrect
16
+
17
+ ```ts
18
+ const userPayload = {
19
+ first_name: "John",
20
+ last_name: "Doe",
21
+ };
22
+
23
+ const item = {
24
+ ITEM_ID: 1,
25
+ };
26
+ ```
27
+
28
+ ### Correct
29
+
30
+ ```ts
31
+ // camelCase properties
32
+ const userPayload = {
33
+ firstName: "John",
34
+ lastName: "Doe",
35
+ };
36
+
37
+ // Quoted keys for API boundaries
38
+ const apiPayload = {
39
+ first_name: "John",
40
+ last_name: "Doe",
41
+ };
42
+
43
+ // as const objects are exempt (enum-like pattern)
44
+ const STATUS_MAP = {
45
+ ACTIVE: "active",
46
+ INACTIVE: "inactive",
47
+ } as const;
48
+ ```
49
+
50
+ ## Exceptions
51
+
52
+ - Quoted property keys (`"first_name"`) are always allowed
53
+ - Computed property keys (`[key]`) are not checked
54
+ - Properties inside `as const` objects and arrays are exempt
55
+
56
+ ## When Not To Use It
57
+
58
+ If your project frequently uses snake_case or SCREAMING_SNAKE_CASE for object properties without quoting.
59
+
60
+ ## Related Rules
61
+
62
+ - [enforce-camel-case](ENFORCE_CAMEL_CASE.md) - Enforces camelCase for variables and functions
63
+ - [enforce-constant-case](ENFORCE_CONSTANT_CASE.md) - Enforces SCREAMING_SNAKE_CASE for global constants
@@ -1,10 +1,10 @@
1
1
  # jsx-newline-between-elements
2
2
 
3
- Require empty lines between sibling JSX elements when at least one spans multiple lines.
3
+ Require empty lines between sibling JSX elements and expression containers when at least one spans multiple lines.
4
4
 
5
5
  ## Rule Details
6
6
 
7
- This rule enforces empty lines between sibling JSX elements when either element spans multiple lines. This improves visual separation and readability of complex component structures. Single-line elements do not require empty lines between them.
7
+ This rule enforces empty lines between sibling JSX elements, fragments, and expression containers (e.g., `{condition && (...)}`) when either sibling spans multiple lines. This improves visual separation and readability of complex component structures. Single-line siblings do not require empty lines between them.
8
8
 
9
9
  ## Examples
10
10
 
@@ -70,6 +70,28 @@ function UserProfile() {
70
70
  }
71
71
  ```
72
72
 
73
+ Expression containers also need empty lines:
74
+
75
+ ```tsx
76
+ function StudentInfo({ studentId, name }) {
77
+ return (
78
+ <div>
79
+ {studentId && (
80
+ <div className="flex items-center gap-x-1">
81
+ <Text size="lg">{studentId}</Text>
82
+ </div>
83
+ )}
84
+
85
+ {name && (
86
+ <div className="flex items-center gap-x-1">
87
+ <Text size="lg">{name}</Text>
88
+ </div>
89
+ )}
90
+ </div>
91
+ );
92
+ }
93
+ ```
94
+
73
95
  Single-line elements do not require empty lines:
74
96
 
75
97
  ```jsx
@@ -0,0 +1,83 @@
1
+ # no-inline-return-properties
2
+
3
+ Require return object properties to use shorthand notation by extracting non-shorthand values to const variables.
4
+
5
+ ## Rule Details
6
+
7
+ This rule enforces that all properties in a returned object literal use shorthand notation. When a return statement contains an object with renamed or computed properties, the values should be extracted into const variables before the return statement.
8
+
9
+ ### Why?
10
+
11
+ Return objects with a mix of shorthand and non-shorthand properties are harder to scan. Extracting values into named constants before returning keeps the return statement clean and makes the data flow easier to follow.
12
+
13
+ ## Examples
14
+
15
+ ### Incorrect
16
+
17
+ ```ts
18
+ function useBranch() {
19
+ return {
20
+ admins,
21
+ branch,
22
+ coursesCurrentPage: coursePage,
23
+ coursesTotalPages: Math.ceil(sortedLearning.length / COURSES_PER_PAGE) || 1,
24
+ members,
25
+ membersHref: `/branch/${branchNumber}/members`,
26
+ news,
27
+ newsCurrentPage: newsData?.currentPage ?? 1,
28
+ newsTotalPages: newsData?.pages ?? 1,
29
+ stats,
30
+ };
31
+ }
32
+ ```
33
+
34
+ ```ts
35
+ function useJoin() {
36
+ return {
37
+ error,
38
+ isSubmitting,
39
+ onJoin: handleJoin,
40
+ };
41
+ }
42
+ ```
43
+
44
+ ### Correct
45
+
46
+ ```ts
47
+ function useBranch() {
48
+ const coursesCurrentPage = coursePage;
49
+ const coursesTotalPages = Math.ceil(sortedLearning.length / COURSES_PER_PAGE) || 1;
50
+ const membersHref = `/branch/${branchNumber}/members`;
51
+ const newsCurrentPage = newsData?.currentPage ?? 1;
52
+ const newsTotalPages = newsData?.pages ?? 1;
53
+
54
+ return {
55
+ admins,
56
+ branch,
57
+ coursesCurrentPage,
58
+ coursesTotalPages,
59
+ members,
60
+ membersHref,
61
+ news,
62
+ newsCurrentPage,
63
+ newsTotalPages,
64
+ stats,
65
+ };
66
+ }
67
+ ```
68
+
69
+ ```ts
70
+ function useJoin() {
71
+ const onJoin = handleJoin;
72
+
73
+ return {
74
+ error,
75
+ isSubmitting,
76
+ onJoin,
77
+ };
78
+ }
79
+ ```
80
+
81
+ ## When Not To Use It
82
+
83
+ If your project prefers inline property values in return statements for brevity.
@@ -60,10 +60,12 @@ The rule detects two types of lazy patterns:
60
60
 
61
61
  ### Repeated Characters (3+)
62
62
 
63
- Variables with 3 or more consecutive identical characters:
63
+ Variables with 3 or more consecutive identical characters (case-sensitive):
64
64
 
65
65
  - `xxx`, `aaa`, `zzz`, `qqqq`, `aaaa`
66
66
 
67
+ Compound names where repeated characters span word boundaries are not flagged (e.g., `ProfileProgressSkeleton` is allowed because `ss` and `S` are different cases).
68
+
67
69
  ### Keyboard Sequences (4+)
68
70
 
69
71
  Variables containing 4 or more consecutive keyboard row characters:
@@ -0,0 +1,76 @@
1
+ # no-misleading-constant-case
2
+
3
+ Disallow SCREAMING_SNAKE_CASE for non-constant or non-static values.
4
+
5
+ ## Rule Details
6
+
7
+ SCREAMING_SNAKE_CASE is reserved for global static constants. This rule flags it when used incorrectly:
8
+
9
+ - **Mutable bindings**: `let` or `var` with SCREAMING_SNAKE_CASE
10
+ - **Dynamic values**: `const` with function calls, await, dynamic templates, objects with dynamic values
11
+ - **Local scope**: SCREAMING_SNAKE_CASE inside functions, even for static values
12
+
13
+ ### Why?
14
+
15
+ SCREAMING_SNAKE_CASE signals a compile-time constant at the module level. Using it for local variables or dynamic values is misleading. Local constants should use camelCase regardless of whether the value is static.
16
+
17
+ ## Examples
18
+
19
+ ### Incorrect
20
+
21
+ ```ts
22
+ // Mutable bindings
23
+ let API_URL = "https://api.example.com";
24
+ var MAX_COUNT = 10;
25
+
26
+ // Dynamic values at global scope
27
+ const API_URL = getUrl();
28
+ const USER_ID = await fetchId();
29
+ const PATHNAME = `/news/${slug}`;
30
+ const CONFIG = { key: getValue() };
31
+
32
+ // SCREAMING_SNAKE_CASE in local scope (even static values)
33
+ function foo() {
34
+ const MAX_RETRY = 3;
35
+ const TOTAL_COUNT = items.length;
36
+ }
37
+
38
+ const MyComponent = () => {
39
+ const ACTIVE_ITEMS = ITEMS.filter((i) => i.active);
40
+ };
41
+ ```
42
+
43
+ ### Correct
44
+
45
+ ```ts
46
+ // Global static constants use SCREAMING_SNAKE_CASE
47
+ const API_URL = "https://api.example.com";
48
+ const MAX_COUNT = 10;
49
+ const ITEMS = [1, 2, 3];
50
+ const CONFIG = { key: "value" };
51
+ const VARIANTS = ["a", "b"] as const;
52
+
53
+ // Dynamic values use camelCase
54
+ const apiUrl = getUrl();
55
+ const userId = await fetchId();
56
+ const pathname = `/news/${slug}`;
57
+
58
+ // Local scope uses camelCase
59
+ function foo() {
60
+ const maxRetry = 3;
61
+ const totalCount = items.length;
62
+ }
63
+
64
+ // Mutable bindings use camelCase
65
+ let count = 10;
66
+ var name = "foo";
67
+ ```
68
+
69
+ ## When Not To Use It
70
+
71
+ If your project does not follow the convention that SCREAMING_SNAKE_CASE is reserved for global static constants.
72
+
73
+ ## Related Rules
74
+
75
+ - [enforce-constant-case](ENFORCE_CONSTANT_CASE.md) - The complementary rule that enforces SCREAMING_SNAKE_CASE for global static constants
76
+ - [enforce-camel-case](ENFORCE_CAMEL_CASE.md) - Enforces camelCase for variables and functions
@@ -0,0 +1,64 @@
1
+ # prefer-inline-type-export
2
+
3
+ Require type and interface declarations to be exported inline rather than via a separate export statement.
4
+
5
+ > This rule is auto-fixable using `--fix`.
6
+
7
+ ## Rule Details
8
+
9
+ This rule enforces that `interface` and `type` declarations are exported directly at the declaration site using the `export` keyword, rather than being exported separately via `export type { ... }` or `export { ... }` at the bottom of the file.
10
+
11
+ ### Why?
12
+
13
+ Inline exports make it immediately clear at the declaration site that a type is part of the module's public API. Separate export statements create a disconnect between the declaration and its visibility, making it harder to understand the module's surface area at a glance.
14
+
15
+ ## Examples
16
+
17
+ ### Incorrect
18
+
19
+ ```ts
20
+ interface ButtonProps {
21
+ label: string;
22
+ disabled: boolean;
23
+ }
24
+
25
+ type Theme = "light" | "dark";
26
+
27
+ export type { ButtonProps, Theme };
28
+ ```
29
+
30
+ ```ts
31
+ interface Config {
32
+ port: number;
33
+ }
34
+
35
+ export { Config };
36
+ ```
37
+
38
+ ### Correct
39
+
40
+ ```ts
41
+ export interface ButtonProps {
42
+ label: string;
43
+ disabled: boolean;
44
+ }
45
+
46
+ export type Theme = "light" | "dark";
47
+ ```
48
+
49
+ ```ts
50
+ export interface Config {
51
+ port: number;
52
+ }
53
+ ```
54
+
55
+ ## Exceptions
56
+
57
+ This rule only applies to `interface` and `type` declarations defined in the same file. It does not flag:
58
+
59
+ - Re-exports from other modules (`export type { Foo } from './types'`)
60
+ - Separate exports of non-type declarations (variables, functions, classes)
61
+
62
+ ## When Not To Use It
63
+
64
+ If your project prefers grouping all exports at the bottom of the file for consistency.