eslint-plugin-code-style 1.9.4 → 1.9.6

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 +28 -0
  2. package/index.js +82 -3
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.9.6] - 2026-02-03
11
+
12
+ ### Enhanced
13
+
14
+ - **`no-hardcoded-strings`** - Skip Tailwind CSS class strings:
15
+ - Multi-class strings like `"px-5 py-3 w-full bg-white"` are now ignored
16
+ - Individual classes: `w-5`, `p-4`, `pr-12`, `text-2xl`, `gap-4`, etc.
17
+ - State modifiers: `hover:bg-primary`, `focus:ring-2`, `disabled:opacity-50`
18
+ - Responsive prefixes: `sm:flex`, `md:hidden`, `lg:grid`
19
+ - Opacity values: `bg-white/50`, `text-black/80`, `placeholder-error/50`
20
+ - Arbitrary values: `w-[100px]`, `bg-[#ff0000]`
21
+ - Negative values: `-translate-y-1/2`, `-rotate-45`
22
+
23
+ ---
24
+
25
+ ## [1.9.5] - 2026-02-03
26
+
27
+ ### Fixed
28
+
29
+ - **`no-hardcoded-strings`** - Fix bug where strings inside exported components were incorrectly skipped:
30
+ - Previously: `export const Component = () => { const name = "ahmed" }` was not detected
31
+ - Now: Strings inside functions are properly detected regardless of export status
32
+ - Only direct constant exports are skipped: `export const MESSAGE = "value"` or `export const DATA = { key: "value" }`
33
+
34
+ ---
35
+
10
36
  ## [1.9.4] - 2026-02-03
11
37
 
12
38
  ### Enhanced
@@ -1299,6 +1325,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1299
1325
 
1300
1326
  ---
1301
1327
 
1328
+ [1.9.6]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.5...v1.9.6
1329
+ [1.9.5]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.4...v1.9.5
1302
1330
  [1.9.4]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.3...v1.9.4
1303
1331
  [1.9.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.2...v1.9.3
1304
1332
  [1.9.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.9.1...v1.9.2
package/index.js CHANGED
@@ -14070,6 +14070,22 @@ const noHardcodedStrings = {
14070
14070
  /^[a-z][a-zA-Z0-9_]*=/,
14071
14071
  // CSS property-like (kebab-case): must have hyphen (e.g., font-size, background-color)
14072
14072
  /^[a-z]+-[a-z]+(-[a-z]+)*$/,
14073
+ // Tailwind CSS utility classes
14074
+ // With numbers: w-5, p-4, pr-12, mt-1, text-2xl, gap-4, h-5, rounded-lg, etc.
14075
+ /^-?[a-z]+-\d+(\.\d+)?(\/\d+)?$/,
14076
+ /^-?[a-z]+-[a-z]+-\d+(\.\d+)?(\/\d+)?$/,
14077
+ // With modifiers: hover:bg-primary, focus:ring-2, sm:flex, disabled:opacity-50, etc.
14078
+ /^[a-z]+:[a-z][-a-z0-9/]*$/,
14079
+ // With opacity: bg-white/50, text-black/80, placeholder-error/50, etc.
14080
+ /^[a-z]+-[a-z]+(-[a-z]+)*\/\d+$/,
14081
+ // Arbitrary values: w-[100px], bg-[#ff0000], translate-x-[50%], etc.
14082
+ /^-?[a-z]+(-[a-z]+)*-\[.+\]$/,
14083
+ // Negative transforms: -translate-y-1/2, -rotate-45, -skew-x-12, etc.
14084
+ /^-[a-z]+-[a-z]+-\d+\/\d+$/,
14085
+ // Common Tailwind patterns with full/auto/screen/none/inherit etc.
14086
+ /^[a-z]+-(full|auto|screen|none|inherit|initial|px|fit|min|max)$/,
14087
+ // Responsive/state prefixes with values: sm:w-full, md:flex, lg:hidden, etc.
14088
+ /^(sm|md|lg|xl|2xl|hover|focus|active|disabled|first|last|odd|even|group-hover|dark|motion-safe|motion-reduce):[a-z][-a-z0-9/[\]]*$/,
14073
14089
  // Numbers with separators
14074
14090
  /^[\d,._]+$/,
14075
14091
  // Semantic version
@@ -14090,6 +14106,38 @@ const noHardcodedStrings = {
14090
14106
 
14091
14107
  const allIgnorePatterns = [...technicalPatterns, ...extraIgnorePatterns];
14092
14108
 
14109
+ // Tailwind/CSS class pattern - matches individual class names
14110
+ const tailwindClassPattern = /^-?[a-z]+(-[a-z0-9]+)*(\/\d+)?$|^-?[a-z]+(-[a-z0-9]+)*-\[.+\]$|^[a-z]+:[a-z][-a-z0-9/[\]]*$/;
14111
+
14112
+ // Check if a string contains only CSS/Tailwind class names
14113
+ const isTailwindClassStringHandler = (str) => {
14114
+ // Split by whitespace and filter empty strings
14115
+ const tokens = str.trim().split(/\s+/).filter(Boolean);
14116
+
14117
+ // Must have at least one token
14118
+ if (tokens.length === 0) return false;
14119
+
14120
+ // Check if all tokens look like CSS classes
14121
+ return tokens.every((token) => {
14122
+ // Skip template literal expressions placeholders if any
14123
+ if (token.includes("${")) return true;
14124
+
14125
+ // Common Tailwind patterns
14126
+ return (
14127
+ // Basic kebab-case with numbers: w-5, p-4, pr-12, text-2xl, gap-4
14128
+ /^-?[a-z]+(-[a-z0-9]+)*$/.test(token)
14129
+ // With fractions: w-1/2, -translate-y-1/2
14130
+ || /^-?[a-z]+(-[a-z0-9]+)*\/\d+$/.test(token)
14131
+ // With modifiers: hover:bg-primary, focus:ring-2, sm:flex
14132
+ || /^[a-z0-9]+:[a-z][-a-z0-9/[\]]*$/.test(token)
14133
+ // Arbitrary values: w-[100px], bg-[#ff0000]
14134
+ || /^-?[a-z]+(-[a-z]+)*-?\[.+\]$/.test(token)
14135
+ // Single word utilities: flex, hidden, block
14136
+ || /^[a-z]+$/.test(token)
14137
+ );
14138
+ });
14139
+ };
14140
+
14093
14141
  // UI component patterns - only ignored in JSX attributes, not in logic
14094
14142
  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)$/;
14095
14143
 
@@ -14311,6 +14359,9 @@ const noHardcodedStrings = {
14311
14359
  // Always flag HTTP status codes and role names
14312
14360
  if (isFlaggedSpecialStringHandler(str)) return false;
14313
14361
 
14362
+ // Skip Tailwind/CSS class strings
14363
+ if (isTailwindClassStringHandler(str)) return true;
14364
+
14314
14365
  return allIgnorePatterns.some((pattern) => pattern.test(str));
14315
14366
  };
14316
14367
 
@@ -14421,8 +14472,11 @@ const noHardcodedStrings = {
14421
14472
  // Check if string is in an object that looks like constants definition
14422
14473
  const isInConstantsObjectHandler = (node) => {
14423
14474
  let current = node.parent;
14475
+ let depth = 0;
14424
14476
 
14425
14477
  while (current) {
14478
+ depth++;
14479
+
14426
14480
  if (current.type === "VariableDeclarator") {
14427
14481
  const varName = current.id && current.id.name;
14428
14482
 
@@ -14440,9 +14494,34 @@ const noHardcodedStrings = {
14440
14494
  }
14441
14495
  }
14442
14496
 
14443
- // Check for export const CONSTANT_NAME = "value"
14444
- if (current.type === "ExportNamedDeclaration") {
14445
- return true;
14497
+ // Check for export const CONSTANT_NAME = "value" - only direct assignments (depth <= 3)
14498
+ // e.g., export const X = "value" or export const X = { key: "value" }
14499
+ // But NOT strings inside exported functions like export const Component = () => { const x = "value" }
14500
+ if (current.type === "ExportNamedDeclaration" && depth <= 3) {
14501
+ // Only skip if the export is a direct literal or object, not a function
14502
+ const declaration = current.declaration;
14503
+
14504
+ if (declaration && declaration.type === "VariableDeclaration") {
14505
+ const declarator = declaration.declarations[0];
14506
+
14507
+ if (declarator && declarator.init) {
14508
+ const initType = declarator.init.type;
14509
+
14510
+ // Skip if it's a direct string, object, or array constant
14511
+ if (initType === "Literal" || initType === "ObjectExpression" || initType === "ArrayExpression") {
14512
+ return true;
14513
+ }
14514
+ }
14515
+ }
14516
+ }
14517
+
14518
+ // Stop traversing if we hit a function - strings inside functions should be checked
14519
+ if (
14520
+ current.type === "FunctionDeclaration"
14521
+ || current.type === "FunctionExpression"
14522
+ || current.type === "ArrowFunctionExpression"
14523
+ ) {
14524
+ return false;
14446
14525
  }
14447
14526
 
14448
14527
  current = current.parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
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",