eslint-plugin-code-style 1.11.1 → 1.11.2

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 (3) hide show
  1. package/CHANGELOG.md +20 -23
  2. package/index.js +206 -243
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,19 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
- ## [1.11.1] - 2026-02-03
10
+ ## [1.11.2] - 2026-02-04
11
+
12
+ ### Fixed
11
13
 
12
- **Bug Fix: Single-property type annotations now collapse to single line**
14
+ - **`no-hardcoded-strings`**
15
+ - Skip strings inside style object expressions (CSS values like `radial-gradient(...)`, `rotate(90deg)`, etc.)
16
+ - Skip HTML input types (`text`, `password`, `email`, `number`, etc.)
17
+ - Add CSS function patterns (transform, gradient, animation) to ignore list
18
+ - Simplify error message to unified format: "should be imported from @/data, @/strings, @/constants, or @/enums"
19
+ - Remove forced flagging of status codes, roles, HTTP methods (user intent is ambiguous)
13
20
 
14
- **Version Range:** v1.11.0 → v1.11.1
21
+ - **`ternary-condition-multiline`**
22
+ - Skip collapsing ternaries with JSX branches (JSX ternaries should stay multiline for readability)
23
+
24
+ - **`no-inline-type-definitions`**
25
+ - Skip union types with only built-in/native types (e.g., `Error | null`, `string | null`)
26
+ - Only flag unions with custom inline types like `{ user: string }`
27
+
28
+ ---
29
+
30
+ ## [1.11.1] - 2026-02-03
15
31
 
16
32
  ### Fixed
17
33
 
18
34
  - **`component-props-inline-type`** - Single-property type annotations spanning multiple lines now auto-fix to single line format `{ prop: Type }`
19
35
  - **`function-params-per-line`** - Normalize single-member type annotations to prevent circular fix conflicts
20
36
 
21
- **Full Changelog:** [v1.11.0...v1.11.1](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.0...v1.11.1)
22
-
23
37
  ---
24
38
 
25
39
  ## [1.11.0] - 2026-02-03
@@ -57,25 +71,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
57
71
 
58
72
  ## [1.10.3] - 2026-02-03
59
73
 
60
- **Bug Fixes: className template literals and trailing comma removal**
61
-
62
- **Version Range:** v1.10.2 → v1.10.3
63
-
64
74
  ### Fixed
65
75
 
66
76
  - **`no-hardcoded-strings`** - Skip template literals inside className/style attributes (Tailwind classes in template literals)
67
77
  - **`component-props-inline-type`** - Auto-fix to REMOVE trailing comma for single property (not just skip adding it)
68
78
 
69
- **Full Changelog:** [v1.10.2...v1.10.3](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.10.2...v1.10.3)
70
-
71
79
  ---
72
80
 
73
81
  ## [1.10.2] - 2026-02-03
74
82
 
75
- **Bug Fixes: component-props-inline-type and no-hardcoded-strings**
76
-
77
- **Version Range:** v1.10.1 → v1.10.2
78
-
79
83
  ### Fixed
80
84
 
81
85
  - **`component-props-inline-type`** - Don't require trailing comma for single property in inline type definitions
@@ -85,23 +89,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
85
89
  - **`no-hardcoded-strings`** - Skip CSS property values (cursor: pointer, display: flex, position: absolute, etc.)
86
90
  - **`no-hardcoded-strings`** - Skip SVG filter result identifiers (BackgroundImageFix, SourceGraphic, etc.)
87
91
 
88
- **Full Changelog:** [v1.10.1...v1.10.2](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.10.1...v1.10.2)
89
-
90
92
  ---
91
93
 
92
94
  ## [1.10.1] - 2026-02-03
93
95
 
94
- **Bug Fix: logical-expression-multiline rule improvements**
95
-
96
- **Version Range:** v1.10.0 → v1.10.1
97
-
98
96
  ### Fixed
99
97
 
100
98
  - **`logical-expression-multiline`** - Add collapse to single line for simple expressions (≤3 operands)
101
99
  - **`logical-expression-multiline`** - Skip collapsing when any operand is multiline (e.g., JSX elements)
102
100
 
103
- **Full Changelog:** [v1.10.0...v1.10.1](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.10.0...v1.10.1)
104
-
105
101
  ---
106
102
 
107
103
  ## [1.10.0] - 2026-02-03
@@ -1476,6 +1472,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1476
1472
 
1477
1473
  ---
1478
1474
 
1475
+ [1.11.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.1...v1.11.2
1479
1476
  [1.11.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.0...v1.11.1
1480
1477
  [1.11.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.10.3...v1.11.0
1481
1478
  [1.10.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.10.2...v1.10.3
package/index.js CHANGED
@@ -4734,6 +4734,25 @@ const ternaryConditionMultiline = {
4734
4734
  return lineText.match(/^\s*/)[0].length;
4735
4735
  };
4736
4736
 
4737
+ // Check if a node contains JSX elements (recursively)
4738
+ const containsJsxHandler = (n) => {
4739
+ if (!n) return false;
4740
+
4741
+ if (n.type === "JSXElement" || n.type === "JSXFragment") return true;
4742
+
4743
+ if (n.type === "ParenthesizedExpression") return containsJsxHandler(n.expression);
4744
+
4745
+ if (n.type === "ConditionalExpression") {
4746
+ return containsJsxHandler(n.consequent) || containsJsxHandler(n.alternate);
4747
+ }
4748
+
4749
+ if (n.type === "LogicalExpression") {
4750
+ return containsJsxHandler(n.left) || containsJsxHandler(n.right);
4751
+ }
4752
+
4753
+ return false;
4754
+ };
4755
+
4737
4756
  // Check if branches have complex objects (should stay multiline)
4738
4757
  const hasComplexObjectHandler = (n) => {
4739
4758
  if (n.type === "ObjectExpression" && n.properties.length >= 2) return true;
@@ -4743,6 +4762,9 @@ const ternaryConditionMultiline = {
4743
4762
  return false;
4744
4763
  };
4745
4764
 
4765
+ // Check if branches contain JSX (should stay multiline)
4766
+ const hasJsxBranchesHandler = (node) => containsJsxHandler(node.consequent) || containsJsxHandler(node.alternate);
4767
+
4746
4768
  // Check if a nested ternary has complex condition (>maxOperands)
4747
4769
  const hasComplexNestedTernaryHandler = (node) => {
4748
4770
  const checkBranch = (branch) => {
@@ -4794,6 +4816,11 @@ const ternaryConditionMultiline = {
4794
4816
  return false;
4795
4817
  }
4796
4818
 
4819
+ // Skip ternaries with JSX branches (should stay multiline for readability)
4820
+ if (hasJsxBranchesHandler(node)) {
4821
+ return false;
4822
+ }
4823
+
4797
4824
  // Skip parenthesized nested ternaries with complex condition (>maxOperands)
4798
4825
  // These should be formatted manually or stay as-is
4799
4826
  if (hasComplexNestedTernaryHandler(node)) {
@@ -4968,9 +4995,14 @@ const ternaryConditionMultiline = {
4968
4995
  return; // Don't collapse - nested group needs multiline
4969
4996
  }
4970
4997
 
4971
- // Skip if branches have complex objects
4998
+ // Skip if branches have complex objects or JSX elements
4972
4999
  const hasComplexBranches = hasComplexObjectHandler(node.consequent) || hasComplexObjectHandler(node.alternate);
4973
5000
 
5001
+ // Skip ternaries with JSX branches (should stay multiline for readability)
5002
+ if (hasJsxBranchesHandler(node)) {
5003
+ return;
5004
+ }
5005
+
4974
5006
  // Skip unparenthesized nested ternaries (parenthesized ones count as 1 operand)
4975
5007
  const hasUnparenthesizedNestedTernary = (node.consequent.type === "ConditionalExpression" && !isParenthesizedHandler(node.consequent))
4976
5008
  || (node.alternate.type === "ConditionalExpression" && !isParenthesizedHandler(node.alternate));
@@ -14455,6 +14487,24 @@ const noHardcodedStrings = {
14455
14487
  /^&[a-z]+;$/,
14456
14488
  // Punctuation only
14457
14489
  /^[.!?,;:'"()\[\]{}]+$/,
14490
+ // CSS transform functions: rotate(), translate(), scale(), skew(), matrix(), etc.
14491
+ /^(rotate|translate|translateX|translateY|translateZ|translate3d|scale|scaleX|scaleY|scaleZ|scale3d|skew|skewX|skewY|matrix|matrix3d|perspective)\(.+\)$/,
14492
+ // CSS transform values with multiple functions: "rotate(90deg) scaleX(-1)"
14493
+ /^(rotate|translate|translateX|translateY|scale|scaleX|scaleY|skew|skewX|skewY|matrix)\([^)]+\)(\s+(rotate|translate|translateX|translateY|scale|scaleX|scaleY|skew|skewX|skewY|matrix)\([^)]+\))+$/,
14494
+ // CSS gradient functions
14495
+ /^(linear-gradient|radial-gradient|conic-gradient|repeating-linear-gradient|repeating-radial-gradient)\(.+\)$/,
14496
+ // CSS animation shorthand: "spin 2s linear infinite"
14497
+ /^[a-zA-Z][\w-]*\s+[\d.]+m?s\s+[\w-]+(\s+[\w-]+)*$/,
14498
+ // CSS transform-origin values: "50% 50%", "center center", "top left"
14499
+ /^(\d+%|center|top|bottom|left|right)(\s+(\d+%|center|top|bottom|left|right))?$/,
14500
+ // CSS calc() function
14501
+ /^calc\(.+\)$/,
14502
+ // CSS var() function
14503
+ /^var\(.+\)$/,
14504
+ // CSS clamp() function
14505
+ /^clamp\(.+\)$/,
14506
+ // CSS min/max functions
14507
+ /^(min|max)\(.+\)$/,
14458
14508
  ];
14459
14509
 
14460
14510
  const extraIgnorePatterns = (options.ignorePatterns || []).map((p) => {
@@ -14528,223 +14578,73 @@ const noHardcodedStrings = {
14528
14578
  // UI component patterns - only ignored in JSX attributes, not in logic
14529
14579
  const uiComponentPattern = /^(primary|secondary|tertiary|ghost|outline|link|muted|danger|warning|info|success|error|default|subtle|solid|soft|plain|flat|elevated|filled|tonal|text|contained|standard|xs|sm|md|lg|xl|2xl|3xl|4xl|5xl|xxs|xxl|small|medium|large|tiny|huge|compact|comfortable|spacious|left|right|center|top|bottom|start|end|middle|baseline|stretch|between|around|evenly|horizontal|vertical|row|column|inline|block|flex|grid|auto|none|hidden|visible|static|relative|absolute|fixed|sticky|on|off|hover|focus|click|blur|always|never)$/;
14530
14580
 
14531
- // HTTP status codes that should NOT be hardcoded (2xx, 4xx, and 5xx)
14532
- const httpStatusCodePattern = /^[245]\d{2}$/;
14533
-
14534
- // Common role/permission names that should be imported from enums/data
14535
- const rolePermissionNames = new Set([
14536
- "admin",
14537
- "administrator",
14538
- "editor",
14539
- "guest",
14540
- "manager",
14541
- "member",
14542
- "moderator",
14543
- "operator",
14544
- "owner",
14545
- "reviewer",
14546
- "subscriber",
14547
- "superadmin",
14548
- "supervisor",
14549
- "user",
14550
- "viewer",
14581
+ // HTML input types - standard browser input types, not hardcoded strings
14582
+ const htmlInputTypes = new Set([
14583
+ "button",
14584
+ "checkbox",
14585
+ "color",
14586
+ "date",
14587
+ "datetime-local",
14588
+ "email",
14589
+ "file",
14590
+ "hidden",
14591
+ "image",
14592
+ "month",
14593
+ "number",
14594
+ "password",
14595
+ "radio",
14596
+ "range",
14597
+ "reset",
14598
+ "search",
14599
+ "submit",
14600
+ "tel",
14601
+ "text",
14602
+ "time",
14603
+ "url",
14604
+ "week",
14551
14605
  ]);
14552
14606
 
14553
- // HTTP methods that should be imported from enums/data
14554
- const httpMethods = new Set([
14555
- "CONNECT",
14556
- "DELETE",
14557
- "GET",
14558
- "HEAD",
14559
- "OPTIONS",
14560
- "PATCH",
14561
- "POST",
14562
- "PUT",
14563
- "TRACE",
14564
- ]);
14607
+ // Check if string is an HTML input type
14608
+ const isHtmlInputTypeHandler = (str) => htmlInputTypes.has(str.toLowerCase());
14565
14609
 
14566
- // Environment names that should be imported from enums/data
14567
- const environmentNames = new Set([
14568
- "dev",
14569
- "development",
14570
- "local",
14571
- "prod",
14572
- "production",
14573
- "qa",
14574
- "sandbox",
14575
- "staging",
14576
- "test",
14577
- "testing",
14578
- "uat",
14579
- ]);
14610
+ // Check if node is inside a style object expression (style={{ ... }})
14611
+ const isInsideStyleObjectHandler = (node) => {
14612
+ let current = node.parent;
14580
14613
 
14581
- // Log levels that should be imported from enums/data
14582
- const logLevels = new Set([
14583
- "debug",
14584
- "error",
14585
- "fatal",
14586
- "info",
14587
- "log",
14588
- "trace",
14589
- "warn",
14590
- "warning",
14591
- ]);
14614
+ while (current) {
14615
+ // Check if we're in a Property inside an ObjectExpression inside a JSXAttribute named "style"
14616
+ if (current.type === "Property" && current.parent && current.parent.type === "ObjectExpression") {
14617
+ const objExpr = current.parent;
14592
14618
 
14593
- // Status/state strings that should be imported from enums/data
14594
- const statusStrings = new Set([
14595
- "accepted",
14596
- "active",
14597
- "approved",
14598
- "archived",
14599
- "blocked",
14600
- "cancelled",
14601
- "closed",
14602
- "completed",
14603
- "declined",
14604
- "deleted",
14605
- "disabled",
14606
- "done",
14607
- "draft",
14608
- "enabled",
14609
- "expired",
14610
- "failed",
14611
- "finished",
14612
- "inactive",
14613
- "inprogress",
14614
- "open",
14615
- "paused",
14616
- "pending",
14617
- "processing",
14618
- "published",
14619
- "queued",
14620
- "ready",
14621
- "rejected",
14622
- "resolved",
14623
- "running",
14624
- "scheduled",
14625
- "started",
14626
- "stopped",
14627
- "submitted",
14628
- "success",
14629
- "successful",
14630
- "suspended",
14631
- "verified",
14632
- "waiting",
14633
- ]);
14619
+ if (objExpr.parent && objExpr.parent.type === "JSXExpressionContainer") {
14620
+ const jsxExprContainer = objExpr.parent;
14634
14621
 
14635
- // Validation/form strings that should be imported from enums/data
14636
- const validationStrings = new Set([
14637
- "empty",
14638
- "invalid",
14639
- "missing",
14640
- "optional",
14641
- "required",
14642
- "valid",
14643
- ]);
14622
+ if (jsxExprContainer.parent && jsxExprContainer.parent.type === "JSXAttribute") {
14623
+ const attrName = jsxExprContainer.parent.name && jsxExprContainer.parent.name.name;
14644
14624
 
14645
- // Auth/permission state strings that should be imported from enums/data
14646
- const authStrings = new Set([
14647
- "anonymous",
14648
- "authenticated",
14649
- "authed",
14650
- "authorized",
14651
- "denied",
14652
- "expired",
14653
- "forbidden",
14654
- "granted",
14655
- "locked",
14656
- "loggedin",
14657
- "loggedout",
14658
- "revoked",
14659
- "unauthenticated",
14660
- "unauthorized",
14661
- "unlocked",
14662
- "unverified",
14663
- "verified",
14664
- ]);
14625
+ if (attrName === "style") return true;
14626
+ }
14627
+ }
14628
+ }
14665
14629
 
14666
- // Priority levels that should be imported from enums/data
14667
- const priorityLevels = new Set([
14668
- "critical",
14669
- "high",
14670
- "highest",
14671
- "low",
14672
- "lowest",
14673
- "medium",
14674
- "normal",
14675
- "urgent",
14676
- ]);
14630
+ current = current.parent;
14631
+ }
14677
14632
 
14678
- // Check functions for each category
14679
- const isHttpStatusCodeHandler = (str) => httpStatusCodePattern.test(str);
14680
- const isRoleNameHandler = (str) => rolePermissionNames.has(str.toLowerCase());
14681
- const isHttpMethodHandler = (str) => httpMethods.has(str.toUpperCase());
14682
- const isEnvironmentNameHandler = (str) => environmentNames.has(str.toLowerCase());
14683
- const isLogLevelHandler = (str) => logLevels.has(str.toLowerCase());
14684
- const isStatusStringHandler = (str) => statusStrings.has(str.toLowerCase());
14685
- const isPriorityLevelHandler = (str) => priorityLevels.has(str.toLowerCase());
14686
- const isValidationStringHandler = (str) => validationStrings.has(str.toLowerCase());
14687
- const isAuthStringHandler = (str) => authStrings.has(str.toLowerCase());
14688
-
14689
- // Check if string should be flagged even if it matches technical patterns
14690
- const isFlaggedSpecialStringHandler = (str) => isHttpStatusCodeHandler(str)
14691
- || isRoleNameHandler(str)
14692
- || isHttpMethodHandler(str)
14693
- || isEnvironmentNameHandler(str)
14694
- || isLogLevelHandler(str)
14695
- || isStatusStringHandler(str)
14696
- || isPriorityLevelHandler(str)
14697
- || isValidationStringHandler(str)
14698
- || isAuthStringHandler(str);
14699
-
14700
- // Get descriptive error message based on string type
14633
+ return false;
14634
+ };
14635
+
14636
+ // Get descriptive error message - unified message for all hardcoded strings
14701
14637
  const getErrorMessageHandler = (str, context = "") => {
14702
14638
  const truncatedStr = str.length > 30 ? `${str.substring(0, 30)}...` : str;
14703
14639
  const contextPart = context ? ` in ${context}` : "";
14704
14640
 
14705
- if (isHttpStatusCodeHandler(str)) {
14706
- return `Hardcoded HTTP status code "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14707
- }
14708
-
14709
- if (isRoleNameHandler(str)) {
14710
- return `Hardcoded role/permission "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14711
- }
14712
-
14713
- if (isHttpMethodHandler(str)) {
14714
- return `Hardcoded HTTP method "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14715
- }
14716
-
14717
- if (isEnvironmentNameHandler(str)) {
14718
- return `Hardcoded environment name "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14719
- }
14720
-
14721
- if (isLogLevelHandler(str)) {
14722
- return `Hardcoded log level "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14723
- }
14724
-
14725
- if (isStatusStringHandler(str)) {
14726
- return `Hardcoded status "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14727
- }
14728
-
14729
- if (isPriorityLevelHandler(str)) {
14730
- return `Hardcoded priority level "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14731
- }
14732
-
14733
- if (isValidationStringHandler(str)) {
14734
- return `Hardcoded validation string "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14735
- }
14736
-
14737
- if (isAuthStringHandler(str)) {
14738
- return `Hardcoded auth state "${truncatedStr}"${contextPart} should be imported from @/enums or @/data`;
14739
- }
14740
-
14741
- return `Hardcoded string "${truncatedStr}"${contextPart} should be imported from @/data or @/strings or @/constants or @/@constants or @/@strings`;
14641
+ return `Hardcoded string "${truncatedStr}"${contextPart} should be imported from @/data, @/strings, @/constants, or @/enums`;
14742
14642
  };
14743
14643
 
14744
- // Check if a string matches any ignore pattern (but not if it's a flagged special string)
14644
+ // Check if a string matches any ignore pattern
14745
14645
  const shouldIgnoreStringHandler = (str) => {
14746
- // Always flag HTTP status codes and role names
14747
- if (isFlaggedSpecialStringHandler(str)) return false;
14646
+ // Skip HTML input types (text, password, email, etc.)
14647
+ if (isHtmlInputTypeHandler(str)) return true;
14748
14648
 
14749
14649
  // Skip Tailwind/CSS class strings
14750
14650
  if (isTailwindClassStringHandler(str)) return true;
@@ -14931,13 +14831,10 @@ const noHardcodedStrings = {
14931
14831
 
14932
14832
  if (!text) return;
14933
14833
 
14934
- // Check if it's a flagged special string (status code, role name)
14935
- const isSpecialString = isFlaggedSpecialStringHandler(text);
14834
+ if (shouldIgnoreStringHandler(text)) return;
14936
14835
 
14937
- if (!isSpecialString && shouldIgnoreStringHandler(text)) return;
14938
-
14939
- // Check if it looks like user-facing text (contains letters) - skip for special strings
14940
- if (!isSpecialString && !/[a-zA-Z]/.test(text)) return;
14836
+ // Check if it looks like user-facing text (contains letters)
14837
+ if (!/[a-zA-Z]/.test(text)) return;
14941
14838
 
14942
14839
  context.report({
14943
14840
  message: getErrorMessageHandler(text, "JSX"),
@@ -14968,13 +14865,10 @@ const noHardcodedStrings = {
14968
14865
  if (expression.type === "Literal" && typeof expression.value === "string") {
14969
14866
  const str = expression.value;
14970
14867
 
14971
- // Check if it's a flagged special string (status code, role name)
14972
- const isSpecialString = isFlaggedSpecialStringHandler(str);
14973
-
14974
- if (!isSpecialString && shouldIgnoreStringHandler(str)) return;
14868
+ if (shouldIgnoreStringHandler(str)) return;
14975
14869
 
14976
- // Check if it looks like user-facing text - skip for special strings
14977
- if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14870
+ // Check if it looks like user-facing text
14871
+ if (!/[a-zA-Z]/.test(str)) return;
14978
14872
 
14979
14873
  context.report({
14980
14874
  message: getErrorMessageHandler(str, "JSX expression"),
@@ -14987,13 +14881,10 @@ const noHardcodedStrings = {
14987
14881
  expression.quasis.forEach((quasi) => {
14988
14882
  const str = quasi.value.cooked || quasi.value.raw;
14989
14883
 
14990
- // Check if it's a flagged special string (status code, role name)
14991
- const isSpecialString = isFlaggedSpecialStringHandler(str);
14992
-
14993
- if (!isSpecialString && shouldIgnoreStringHandler(str)) return;
14884
+ if (shouldIgnoreStringHandler(str)) return;
14994
14885
 
14995
- // Check if it contains user-facing text - skip for special strings
14996
- if (!isSpecialString && !/[a-zA-Z]{2,}/.test(str)) return;
14886
+ // Check if it contains user-facing text
14887
+ if (!/[a-zA-Z]{2,}/.test(str)) return;
14997
14888
 
14998
14889
  // Skip if it looks like a path or URL pattern
14999
14890
  if (/^[/.]|https?:\/\//.test(str)) return;
@@ -15029,13 +14920,10 @@ const noHardcodedStrings = {
15029
14920
  // Skip UI component patterns in JSX attributes (variant, size, position props)
15030
14921
  if (uiComponentPattern.test(str)) return;
15031
14922
 
15032
- // Check if it's a flagged special string (status code, role name)
15033
- const isSpecialString = isFlaggedSpecialStringHandler(str);
14923
+ if (shouldIgnoreStringHandler(str)) return;
15034
14924
 
15035
- if (!isSpecialString && shouldIgnoreStringHandler(str)) return;
15036
-
15037
- // Check if it looks like user-facing text - skip for special strings
15038
- if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14925
+ // Check if it looks like user-facing text
14926
+ if (!/[a-zA-Z]/.test(str)) return;
15039
14927
 
15040
14928
  context.report({
15041
14929
  message: getErrorMessageHandler(str, `attribute "${attrName}"`),
@@ -15056,12 +14944,9 @@ const noHardcodedStrings = {
15056
14944
  // Skip UI component patterns in JSX attributes (variant, size, position props)
15057
14945
  if (uiComponentPattern.test(str)) return;
15058
14946
 
15059
- // Check if it's a flagged special string (status code, role name)
15060
- const isSpecialString = isFlaggedSpecialStringHandler(str);
15061
-
15062
- if (!isSpecialString && shouldIgnoreStringHandler(str)) return;
14947
+ if (shouldIgnoreStringHandler(str)) return;
15063
14948
 
15064
- if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14949
+ if (!/[a-zA-Z]/.test(str)) return;
15065
14950
 
15066
14951
  context.report({
15067
14952
  message: getErrorMessageHandler(str, `attribute "${attrName}"`),
@@ -15078,11 +14963,11 @@ const noHardcodedStrings = {
15078
14963
 
15079
14964
  const str = node.value;
15080
14965
 
15081
- // Check if it's a flagged special string (status code, role name)
15082
- const isSpecialString = isFlaggedSpecialStringHandler(str);
14966
+ // Skip if it matches ignore patterns
14967
+ if (shouldIgnoreStringHandler(str)) return;
15083
14968
 
15084
- // Skip if it matches ignore patterns (but not special strings)
15085
- if (!isSpecialString && shouldIgnoreStringHandler(str)) return;
14969
+ // Skip if inside a style object (style={{ transform: "..." }})
14970
+ if (isInsideStyleObjectHandler(node)) return;
15086
14971
 
15087
14972
  // Skip if not in relevant context
15088
14973
  if (!isInRelevantContextHandler(node)) return;
@@ -15099,8 +14984,8 @@ const noHardcodedStrings = {
15099
14984
  // Skip object property keys
15100
14985
  if (node.parent.type === "Property" && node.parent.key === node) return;
15101
14986
 
15102
- // Skip if it doesn't look like user-facing text - but not for special strings
15103
- if (!isSpecialString && !/[a-zA-Z]/.test(str)) return;
14987
+ // Skip if it doesn't look like user-facing text
14988
+ if (!/[a-zA-Z]/.test(str)) return;
15104
14989
 
15105
14990
  context.report({
15106
14991
  message: getErrorMessageHandler(str),
@@ -15113,6 +14998,9 @@ const noHardcodedStrings = {
15113
14998
  // Skip if in JSX (handled separately)
15114
14999
  if (node.parent.type === "JSXExpressionContainer") return;
15115
15000
 
15001
+ // Skip if inside a style object (style={{ background: `...` }})
15002
+ if (isInsideStyleObjectHandler(node)) return;
15003
+
15116
15004
  // Skip if not in relevant context
15117
15005
  if (!isInRelevantContextHandler(node)) return;
15118
15006
 
@@ -15123,13 +15011,10 @@ const noHardcodedStrings = {
15123
15011
  node.quasis.forEach((quasi) => {
15124
15012
  const str = quasi.value.cooked || quasi.value.raw;
15125
15013
 
15126
- // Check if it's a flagged special string (status code, role name)
15127
- const isSpecialString = isFlaggedSpecialStringHandler(str);
15014
+ if (shouldIgnoreStringHandler(str)) return;
15128
15015
 
15129
- if (!isSpecialString && shouldIgnoreStringHandler(str)) return;
15130
-
15131
- // Check if it contains substantial user-facing text - but not for special strings
15132
- if (!isSpecialString && !/[a-zA-Z]{3,}/.test(str)) return;
15016
+ // Check if it contains substantial user-facing text
15017
+ if (!/[a-zA-Z]{3,}/.test(str)) return;
15133
15018
 
15134
15019
  // Skip if it looks like a path, URL, or query
15135
15020
  if (/^[/.]|^https?:\/\/|^[?&]/.test(str)) return;
@@ -17763,6 +17648,81 @@ const noInlineTypeDefinitions = {
17763
17648
  const maxUnionMembers = options.maxUnionMembers ?? 2;
17764
17649
  const maxLength = options.maxLength ?? 50;
17765
17650
 
17651
+ // Built-in type keywords that don't need to be extracted
17652
+ const builtInTypeKeywords = new Set([
17653
+ "any",
17654
+ "bigint",
17655
+ "boolean",
17656
+ "never",
17657
+ "null",
17658
+ "number",
17659
+ "object",
17660
+ "string",
17661
+ "symbol",
17662
+ "undefined",
17663
+ "unknown",
17664
+ "void",
17665
+ ]);
17666
+
17667
+ // Built-in type references (classes/interfaces that are built-in)
17668
+ const builtInTypeReferences = new Set([
17669
+ "Array",
17670
+ "BigInt",
17671
+ "Boolean",
17672
+ "Date",
17673
+ "Error",
17674
+ "Function",
17675
+ "Map",
17676
+ "Number",
17677
+ "Object",
17678
+ "Promise",
17679
+ "ReadonlyArray",
17680
+ "RegExp",
17681
+ "Set",
17682
+ "String",
17683
+ "Symbol",
17684
+ "WeakMap",
17685
+ "WeakSet",
17686
+ ]);
17687
+
17688
+ // Check if a type node is a built-in type
17689
+ const isBuiltInTypeHandler = (node) => {
17690
+ if (!node) return false;
17691
+
17692
+ // Keyword types: string, number, boolean, null, undefined, etc.
17693
+ if (node.type === "TSStringKeyword" || node.type === "TSNumberKeyword"
17694
+ || node.type === "TSBooleanKeyword" || node.type === "TSNullKeyword"
17695
+ || node.type === "TSUndefinedKeyword" || node.type === "TSVoidKeyword"
17696
+ || node.type === "TSAnyKeyword" || node.type === "TSUnknownKeyword"
17697
+ || node.type === "TSNeverKeyword" || node.type === "TSObjectKeyword"
17698
+ || node.type === "TSSymbolKeyword" || node.type === "TSBigIntKeyword") {
17699
+ return true;
17700
+ }
17701
+
17702
+ // Type reference: Error, Promise, Array, etc.
17703
+ if (node.type === "TSTypeReference" && node.typeName) {
17704
+ const typeName = node.typeName.name || (node.typeName.type === "Identifier" && node.typeName.name);
17705
+
17706
+ if (typeName && builtInTypeReferences.has(typeName)) {
17707
+ return true;
17708
+ }
17709
+ }
17710
+
17711
+ // Literal types: true, false, specific strings/numbers
17712
+ if (node.type === "TSLiteralType") {
17713
+ return true;
17714
+ }
17715
+
17716
+ return false;
17717
+ };
17718
+
17719
+ // Check if all members of a union are built-in types
17720
+ const isBuiltInUnionHandler = (unionNode) => {
17721
+ if (unionNode.type !== "TSUnionType") return false;
17722
+
17723
+ return unionNode.types.every((type) => isBuiltInTypeHandler(type));
17724
+ };
17725
+
17766
17726
  // Count union type members
17767
17727
  const countUnionMembersHandler = (node) => {
17768
17728
  if (node.type !== "TSUnionType") return 1;
@@ -17782,6 +17742,9 @@ const noInlineTypeDefinitions = {
17782
17742
 
17783
17743
  // Handle union types directly
17784
17744
  if (typeNode.type === "TSUnionType") {
17745
+ // Skip union types with only built-in types (e.g., string | null, Error | null)
17746
+ if (isBuiltInUnionHandler(typeNode)) return;
17747
+
17785
17748
  const memberCount = countUnionMembersHandler(typeNode);
17786
17749
  const typeText = sourceCode.getText(typeNode);
17787
17750
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.11.1",
3
+ "version": "1.11.2",
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",