eslint-plugin-code-style 1.9.1 → 1.9.3

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +36 -6
  2. package/README.md +16 -8
  3. package/index.js +209 -43
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,15 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
- ## [1.9.1] - 2026-02-03
10
+ ## [1.9.3] - 2026-02-03
11
+
12
+ ### Enhanced
13
+
14
+ - **`no-hardcoded-strings`** - Remove single-word string length limitations and add more special cases:
15
+ - Removed length restrictions (now detects all single-word hardcoded strings)
16
+ - Added validation strings: `empty`, `invalid`, `missing`, `optional`, `required`, `valid`
17
+ - Added auth state strings: `anonymous`, `authenticated`, `authed`, `authorized`, `denied`, `forbidden`, `granted`, `locked`, `loggedin`, `loggedout`, `revoked`, `unauthenticated`, `unauthorized`, `unlocked`, `unverified`, `verified`
18
+ - Added more status strings: `done`, `finished`, `inprogress`, `queued`, `ready`, `running`, `started`, `stopped`, `successful`, `waiting`
19
+ - Made technical patterns stricter to avoid false negatives (camelCase requires uppercase in middle, snake_case requires underscore, kebab-case requires hyphen)
20
+
21
+ ---
22
+
23
+ ## [1.9.2] - 2026-02-03
11
24
 
12
25
  ### Enhanced
13
26
 
14
- - **`no-hardcoded-strings`** - Now detects additional hardcoded values:
15
- - HTTP status codes (4xx, 5xx like "404", "422", "500") with descriptive error message
16
- - Role/permission names ("admin", "user", "moderator", etc.) with descriptive error message
17
- - Added `@/data` as preferred import source for enums/objects
18
- - Improved error messages to mention all valid import sources
27
+ - **`no-hardcoded-strings`** - Now detects special strings that should be enums:
28
+ - HTTP status codes (2xx, 4xx, 5xx like "200", "404", "500")
29
+ - HTTP methods ("GET", "POST", "PUT", "DELETE", etc.)
30
+ - Role/permission names ("admin", "user", "moderator", etc.)
31
+ - Environment names ("production", "development", "staging", etc.)
32
+ - Log levels ("debug", "info", "warn", "error", etc.)
33
+ - Status strings ("active", "pending", "approved", "rejected", etc.)
34
+ - Priority levels ("high", "medium", "low", "critical", etc.)
35
+ - All above → import from `@/enums` or `@/data`
36
+ - Regular strings → import from `@/data or @/strings or @/constants or @/@constants or @/@strings`
19
37
 
20
38
  ### Fixed
21
39
 
@@ -23,6 +41,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
41
 
24
42
  ---
25
43
 
44
+ ## [1.9.1] - 2026-02-03
45
+
46
+ ### Enhanced
47
+
48
+ - **`no-hardcoded-strings`** - Initial special string detection:
49
+ - HTTP status codes (4xx, 5xx)
50
+ - Role/permission names
51
+
52
+ ---
53
+
26
54
  ## [1.9.0] - 2026-02-03
27
55
 
28
56
  **New Rule: class-method-definition-format + Enhanced Spacing Rules**
@@ -1260,6 +1288,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1260
1288
 
1261
1289
  ---
1262
1290
 
1291
+ [1.9.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.2...v1.9.3
1292
+ [1.9.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.1...v1.9.2
1263
1293
  [1.9.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.0...v1.9.1
1264
1294
  [1.9.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.8.4...v1.9.0
1265
1295
  [1.8.4]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.8.3...v1.8.4
package/README.md CHANGED
@@ -3427,9 +3427,14 @@ const YetAnotherBad = ({ title }) => {
3427
3427
 
3428
3428
  **Why use it:** Hardcoded strings scattered throughout your codebase are hard to maintain, translate, and keep consistent. Centralizing strings in constants makes them easy to find, update, and potentially translate.
3429
3429
 
3430
- **Special detection:**
3431
- - **HTTP status codes** (4xx, 5xx like "404", "500") — should be imported from `@/data` as enums/objects
3432
- - **Role/permission names** ("admin", "user", "moderator", etc.) — should be imported from `@/data` as enums/objects
3430
+ **Special detection (should be imported from `@/enums` or `@/data`):**
3431
+ - **HTTP status codes** — 2xx, 4xx, 5xx like "200", "404", "500"
3432
+ - **HTTP methods** "GET", "POST", "PUT", "DELETE", "PATCH", etc.
3433
+ - **Role/permission names** — "admin", "user", "moderator", "editor", etc.
3434
+ - **Environment names** — "production", "development", "staging", "test", etc.
3435
+ - **Log levels** — "debug", "info", "warn", "error", "fatal", etc.
3436
+ - **Status strings** — "active", "pending", "approved", "rejected", "completed", etc.
3437
+ - **Priority levels** — "high", "medium", "low", "critical", "urgent", etc.
3433
3438
 
3434
3439
  **Options:**
3435
3440
 
@@ -3447,7 +3452,7 @@ const YetAnotherBad = ({ title }) => {
3447
3452
  // ✅ Good — strings imported from constants
3448
3453
  import { BUTTON_LABEL, ERROR_MESSAGE, welcomeText } from "@/constants";
3449
3454
  import { FORM_LABELS } from "@/strings";
3450
- import { HttpStatus, UserRole } from "@/data";
3455
+ import { HttpStatus, UserRole } from "@/enums";
3451
3456
 
3452
3457
  const ComponentHandler = () => (
3453
3458
  <div>
@@ -3490,11 +3495,14 @@ if (role === "admin") { ... }
3490
3495
  }]
3491
3496
  ```
3492
3497
 
3493
- **Valid import paths for constants:**
3494
- - `@/data` (preferred for enums/objects like HttpStatus, UserRole)
3495
- - `@/constants` or `@/@constants`
3498
+ **Valid import paths for strings:**
3499
+ - `@/data`
3496
3500
  - `@/strings` or `@/@strings`
3497
- - `@/data/constants` or `@/data/strings`
3501
+ - `@/constants` or `@/@constants`
3502
+
3503
+ **Valid import paths for enums (status codes, roles):**
3504
+ - `@/enums`
3505
+ - `@/data`
3498
3506
 
3499
3507
  ---
3500
3508
 
package/index.js CHANGED
@@ -13826,17 +13826,26 @@ const stringPropertySpacing = {
13826
13826
  * This promotes maintainability, consistency, and enables
13827
13827
  * easier internationalization.
13828
13828
  *
13829
- * The rule also detects:
13830
- * - HTTP status codes (4xx, 5xx) that should be imported from @/data or enums
13831
- * - Role/permission names (admin, user, moderator, etc.) that should be from @/data
13832
- *
13833
- * Valid import sources:
13834
- * - @/data (enums, objects)
13829
+ * The rule also detects special strings that should be enums:
13830
+ * - HTTP status codes (2xx, 4xx, 5xx like "200", "404", "500")
13831
+ * - HTTP methods ("GET", "POST", "PUT", "DELETE", etc.)
13832
+ * - Role/permission names ("admin", "user", "moderator", etc.)
13833
+ * - Environment names ("production", "development", "staging", etc.)
13834
+ * - Log levels ("debug", "info", "warn", "error", etc.)
13835
+ * - Status strings ("active", "pending", "approved", "rejected", etc.)
13836
+ * - Priority levels ("high", "medium", "low", "critical", etc.)
13837
+ *
13838
+ * Valid import sources for strings:
13839
+ * - @/data
13835
13840
  * - @/strings
13836
13841
  * - @/constants
13837
13842
  * - @/@strings
13838
13843
  * - @/@constants
13839
13844
  *
13845
+ * Valid import sources for enums:
13846
+ * - @/enums
13847
+ * - @/data
13848
+ *
13840
13849
  * Options:
13841
13850
  * { ignoreAttributes: ["className", "id", ...] } - JSX attributes to ignore (replaces defaults)
13842
13851
  * { extraIgnoreAttributes: ["tooltip", ...] } - Additional JSX attributes to ignore (extends defaults)
@@ -13845,7 +13854,7 @@ const stringPropertySpacing = {
13845
13854
  * ✓ Good:
13846
13855
  * import { BUTTON_LABEL, ERROR_MESSAGE } from "@/constants";
13847
13856
  * import { welcomeText } from "@/strings";
13848
- * import { HttpStatus, UserRole } from "@/data";
13857
+ * import { HttpStatus, UserRole } from "@/enums";
13849
13858
  *
13850
13859
  * <button>{BUTTON_LABEL}</button>
13851
13860
  * <span>{ERROR_MESSAGE}</span>
@@ -14046,22 +14055,21 @@ const noHardcodedStrings = {
14046
14055
  /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?/,
14047
14056
  // Time formats
14048
14057
  /^\d{1,2}:\d{2}(:\d{2})?(\s?(AM|PM|am|pm))?$/,
14049
- // JSON keys (camelCase, snake_case, SCREAMING_SNAKE_CASE)
14050
- /^[a-z][a-zA-Z0-9]*$/,
14051
- /^[a-z][a-z0-9_]*$/,
14052
- /^[A-Z][A-Z0-9_]*$/,
14058
+ // JSON keys - require actual naming convention markers (underscore/uppercase in middle)
14059
+ // camelCase: must have uppercase letter in middle (e.g., userId, firstName)
14060
+ /^[a-z]+[A-Z][a-zA-Z0-9]*$/,
14061
+ // snake_case: must have underscore (e.g., user_id, first_name)
14062
+ /^[a-z][a-z0-9]*_[a-z0-9_]*$/,
14063
+ // SCREAMING_SNAKE_CASE: must have underscore (e.g., MAX_VALUE, API_URL)
14064
+ /^[A-Z][A-Z0-9]*_[A-Z0-9_]+$/,
14053
14065
  // Common technical strings
14054
14066
  /^(true|false|null|undefined|NaN|Infinity)$/,
14055
- // HTTP methods
14056
- /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|CONNECT|TRACE)$/,
14057
14067
  // Content types
14058
14068
  /^application\//,
14059
- // Environment variables pattern
14060
- /^[A-Z][A-Z0-9_]*$/,
14061
14069
  // Query parameters
14062
14070
  /^[a-z][a-zA-Z0-9_]*=/,
14063
- // CSS property-like
14064
- /^[a-z]+(-[a-z]+)*$/,
14071
+ // CSS property-like (kebab-case): must have hyphen (e.g., font-size, background-color)
14072
+ /^[a-z]+-[a-z]+(-[a-z]+)*$/,
14065
14073
  // Numbers with separators
14066
14074
  /^[\d,._]+$/,
14067
14075
  // Semantic version
@@ -14082,10 +14090,10 @@ const noHardcodedStrings = {
14082
14090
 
14083
14091
  const allIgnorePatterns = [...technicalPatterns, ...extraIgnorePatterns];
14084
14092
 
14085
- // HTTP status codes that should NOT be hardcoded (4xx and 5xx error codes)
14086
- const httpStatusCodePattern = /^[45]\d{2}$/;
14093
+ // HTTP status codes that should NOT be hardcoded (2xx, 4xx, and 5xx)
14094
+ const httpStatusCodePattern = /^[245]\d{2}$/;
14087
14095
 
14088
- // Common role/permission names that should be imported from data/enums
14096
+ // Common role/permission names that should be imported from enums/data
14089
14097
  const rolePermissionNames = new Set([
14090
14098
  "admin",
14091
14099
  "administrator",
@@ -14104,14 +14112,152 @@ const noHardcodedStrings = {
14104
14112
  "viewer",
14105
14113
  ]);
14106
14114
 
14107
- // Check if string looks like an HTTP status code
14108
- const isHttpStatusCodeHandler = (str) => httpStatusCodePattern.test(str);
14115
+ // HTTP methods that should be imported from enums/data
14116
+ const httpMethods = new Set([
14117
+ "CONNECT",
14118
+ "DELETE",
14119
+ "GET",
14120
+ "HEAD",
14121
+ "OPTIONS",
14122
+ "PATCH",
14123
+ "POST",
14124
+ "PUT",
14125
+ "TRACE",
14126
+ ]);
14127
+
14128
+ // Environment names that should be imported from enums/data
14129
+ const environmentNames = new Set([
14130
+ "dev",
14131
+ "development",
14132
+ "local",
14133
+ "prod",
14134
+ "production",
14135
+ "qa",
14136
+ "sandbox",
14137
+ "staging",
14138
+ "test",
14139
+ "testing",
14140
+ "uat",
14141
+ ]);
14142
+
14143
+ // Log levels that should be imported from enums/data
14144
+ const logLevels = new Set([
14145
+ "debug",
14146
+ "error",
14147
+ "fatal",
14148
+ "info",
14149
+ "log",
14150
+ "trace",
14151
+ "warn",
14152
+ "warning",
14153
+ ]);
14154
+
14155
+ // Status/state strings that should be imported from enums/data
14156
+ const statusStrings = new Set([
14157
+ "accepted",
14158
+ "active",
14159
+ "approved",
14160
+ "archived",
14161
+ "blocked",
14162
+ "cancelled",
14163
+ "closed",
14164
+ "completed",
14165
+ "declined",
14166
+ "deleted",
14167
+ "disabled",
14168
+ "done",
14169
+ "draft",
14170
+ "enabled",
14171
+ "expired",
14172
+ "failed",
14173
+ "finished",
14174
+ "inactive",
14175
+ "inprogress",
14176
+ "open",
14177
+ "paused",
14178
+ "pending",
14179
+ "processing",
14180
+ "published",
14181
+ "queued",
14182
+ "ready",
14183
+ "rejected",
14184
+ "resolved",
14185
+ "running",
14186
+ "scheduled",
14187
+ "started",
14188
+ "stopped",
14189
+ "submitted",
14190
+ "success",
14191
+ "successful",
14192
+ "suspended",
14193
+ "verified",
14194
+ "waiting",
14195
+ ]);
14196
+
14197
+ // Validation/form strings that should be imported from enums/data
14198
+ const validationStrings = new Set([
14199
+ "empty",
14200
+ "invalid",
14201
+ "missing",
14202
+ "optional",
14203
+ "required",
14204
+ "valid",
14205
+ ]);
14206
+
14207
+ // Auth/permission state strings that should be imported from enums/data
14208
+ const authStrings = new Set([
14209
+ "anonymous",
14210
+ "authenticated",
14211
+ "authed",
14212
+ "authorized",
14213
+ "denied",
14214
+ "expired",
14215
+ "forbidden",
14216
+ "granted",
14217
+ "locked",
14218
+ "loggedin",
14219
+ "loggedout",
14220
+ "revoked",
14221
+ "unauthenticated",
14222
+ "unauthorized",
14223
+ "unlocked",
14224
+ "unverified",
14225
+ "verified",
14226
+ ]);
14227
+
14228
+ // Priority levels that should be imported from enums/data
14229
+ const priorityLevels = new Set([
14230
+ "critical",
14231
+ "high",
14232
+ "highest",
14233
+ "low",
14234
+ "lowest",
14235
+ "medium",
14236
+ "normal",
14237
+ "urgent",
14238
+ ]);
14109
14239
 
14110
- // Check if string is a role/permission name
14240
+ // Check functions for each category
14241
+ const isHttpStatusCodeHandler = (str) => httpStatusCodePattern.test(str);
14111
14242
  const isRoleNameHandler = (str) => rolePermissionNames.has(str.toLowerCase());
14243
+ const isHttpMethodHandler = (str) => httpMethods.has(str.toUpperCase());
14244
+ const isEnvironmentNameHandler = (str) => environmentNames.has(str.toLowerCase());
14245
+ const isLogLevelHandler = (str) => logLevels.has(str.toLowerCase());
14246
+ const isStatusStringHandler = (str) => statusStrings.has(str.toLowerCase());
14247
+ const isPriorityLevelHandler = (str) => priorityLevels.has(str.toLowerCase());
14248
+ const isValidationStringHandler = (str) => validationStrings.has(str.toLowerCase());
14249
+ const isAuthStringHandler = (str) => authStrings.has(str.toLowerCase());
14112
14250
 
14113
14251
  // Check if string should be flagged even if it matches technical patterns
14114
- const isFlaggedSpecialStringHandler = (str) => isHttpStatusCodeHandler(str) || isRoleNameHandler(str);
14252
+ const isFlaggedSpecialStringHandler = (str) => isHttpStatusCodeHandler(str)
14253
+ || isRoleNameHandler(str)
14254
+ || isHttpMethodHandler(str)
14255
+ || isEnvironmentNameHandler(str)
14256
+ || isLogLevelHandler(str)
14257
+ || isStatusStringHandler(str)
14258
+ || isPriorityLevelHandler(str)
14259
+ || isValidationStringHandler(str)
14260
+ || isAuthStringHandler(str);
14115
14261
 
14116
14262
  // Get descriptive error message based on string type
14117
14263
  const getErrorMessageHandler = (str, context = "") => {
@@ -14119,14 +14265,42 @@ const noHardcodedStrings = {
14119
14265
  const contextPart = context ? ` in ${context}` : "";
14120
14266
 
14121
14267
  if (isHttpStatusCodeHandler(str)) {
14122
- return `Hardcoded HTTP status code "${truncatedStr}"${contextPart} should be imported from @/data (enum/object) or @/constants`;
14268
+ return `Hardcoded HTTP status code "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14123
14269
  }
14124
14270
 
14125
14271
  if (isRoleNameHandler(str)) {
14126
- return `Hardcoded role/permission "${truncatedStr}"${contextPart} should be imported from @/data (enum/object) or @/constants`;
14272
+ return `Hardcoded role/permission "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14273
+ }
14274
+
14275
+ if (isHttpMethodHandler(str)) {
14276
+ return `Hardcoded HTTP method "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14277
+ }
14278
+
14279
+ if (isEnvironmentNameHandler(str)) {
14280
+ return `Hardcoded environment name "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14281
+ }
14282
+
14283
+ if (isLogLevelHandler(str)) {
14284
+ return `Hardcoded log level "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14285
+ }
14286
+
14287
+ if (isStatusStringHandler(str)) {
14288
+ return `Hardcoded status "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14289
+ }
14290
+
14291
+ if (isPriorityLevelHandler(str)) {
14292
+ return `Hardcoded priority level "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14293
+ }
14294
+
14295
+ if (isValidationStringHandler(str)) {
14296
+ return `Hardcoded validation string "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14127
14297
  }
14128
14298
 
14129
- return `Hardcoded string "${truncatedStr}"${contextPart} should be imported from @/data, @/strings, @/constants, or constants module`;
14299
+ if (isAuthStringHandler(str)) {
14300
+ return `Hardcoded auth state "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14301
+ }
14302
+
14303
+ return `Hardcoded string "${truncatedStr}"${contextPart} should be imported from @/data or @/strings or @/constants or @/@constants or @/@strings`;
14130
14304
  };
14131
14305
 
14132
14306
  // Check if a string matches any ignore pattern (but not if it's a flagged special string)
@@ -14137,13 +14311,13 @@ const noHardcodedStrings = {
14137
14311
  return allIgnorePatterns.some((pattern) => pattern.test(str));
14138
14312
  };
14139
14313
 
14140
- // Check if we're inside a constants/strings/data file
14314
+ // Check if we're inside a constants/strings/data/enums file
14141
14315
  const isConstantsFileHandler = () => {
14142
14316
  const filename = context.filename || context.getFilename();
14143
14317
  const normalizedPath = filename.replace(/\\/g, "/").toLowerCase();
14144
14318
 
14145
- // Check if file is in constants/strings/data folders
14146
- return /\/(constants|strings|@constants|@strings|data|@data)(\/|\.)/i.test(normalizedPath)
14319
+ // Check if file is in constants/strings/data/enums folders
14320
+ return /\/(constants|strings|@constants|@strings|data|@data|enums|@enums)(\/|\.)/i.test(normalizedPath)
14147
14321
  || /\/data\/(constants|strings)/i.test(normalizedPath);
14148
14322
  };
14149
14323
 
@@ -14156,8 +14330,8 @@ const noHardcodedStrings = {
14156
14330
 
14157
14331
  if (typeof importPath !== "string") return;
14158
14332
 
14159
- // Check if import is from constants/strings/data
14160
- const isFromConstants = /@?\/?(@?constants|@?strings|@?data|data\/constants|data\/strings)/i
14333
+ // Check if import is from constants/strings/data/enums
14334
+ const isFromConstants = /@?\/?(@?constants|@?strings|@?data|@?enums|data\/constants|data\/strings)/i
14161
14335
  .test(importPath);
14162
14336
 
14163
14337
  if (isFromConstants) {
@@ -14255,8 +14429,9 @@ const noHardcodedStrings = {
14255
14429
  return true;
14256
14430
  }
14257
14431
 
14258
- // Check for keywords that suggest constants (case-insensitive)
14259
- if (/CONSTANTS?|STRINGS?|MESSAGES?|LABELS?|TEXTS?|DATA$/i.test(varName)) {
14432
+ // Check for exact keywords or keywords at word boundaries (not in Handler names)
14433
+ // Match: MESSAGES, Messages, userMessages, but NOT longMessageHandler
14434
+ if (/^(constants?|strings?|messages?|labels?|texts?|data)$/i.test(varName)) {
14260
14435
  return true;
14261
14436
  }
14262
14437
  }
@@ -14378,9 +14553,6 @@ const noHardcodedStrings = {
14378
14553
  // Check if it looks like user-facing text - skip for special strings
14379
14554
  if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14380
14555
 
14381
- // Require multiple words or reasonable length for non-special strings
14382
- if (!isSpecialString && str.split(/\s+/).length < 2 && str.length < 10) return;
14383
-
14384
14556
  context.report({
14385
14557
  message: getErrorMessageHandler(str, `attribute "${attrName}"`),
14386
14558
  node: node.value,
@@ -14404,9 +14576,6 @@ const noHardcodedStrings = {
14404
14576
 
14405
14577
  if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14406
14578
 
14407
- // Require multiple words or reasonable length for non-special strings
14408
- if (!isSpecialString && str.split(/\s+/).length < 2 && str.length < 10) return;
14409
-
14410
14579
  context.report({
14411
14580
  message: getErrorMessageHandler(str, `attribute "${attrName}"`),
14412
14581
  node: expression,
@@ -14446,9 +14615,6 @@ const noHardcodedStrings = {
14446
14615
  // Skip if it doesn't look like user-facing text - but not for special strings
14447
14616
  if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14448
14617
 
14449
- // Require at least 2 words or be reasonably long - but not for special strings
14450
- if (!isSpecialString && str.split(/\s+/).length < 2 && str.length < 15) return;
14451
-
14452
14618
  context.report({
14453
14619
  message: getErrorMessageHandler(str),
14454
14620
  node,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.9.1",
3
+ "version": "1.9.3",
4
4
  "description": "A custom ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",