uilint-eslint 0.2.57 → 0.2.59

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/dist/index.d.ts CHANGED
@@ -787,14 +787,17 @@ declare const rules: {
787
787
  focusNonReact?: boolean;
788
788
  relaxedThreshold?: number;
789
789
  chunkSeverity?: "error" | "warn" | "off";
790
+ minStatements?: number;
790
791
  }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
791
792
  name: string;
792
793
  };
793
- "prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind", [({
794
+ "prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors", [({
794
795
  styleRatioThreshold?: number;
795
796
  minElementsForAnalysis?: number;
796
797
  allowedStyleProperties?: string[];
797
798
  ignoreComponents?: string[];
799
+ preferSemanticColors?: boolean;
800
+ allowedHardCodedColors?: string[];
798
801
  } | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
799
802
  name: string;
800
803
  };
@@ -930,14 +933,17 @@ declare const plugin: {
930
933
  focusNonReact?: boolean;
931
934
  relaxedThreshold?: number;
932
935
  chunkSeverity?: "error" | "warn" | "off";
936
+ minStatements?: number;
933
937
  }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
934
938
  name: string;
935
939
  };
936
- "prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind", [({
940
+ "prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors", [({
937
941
  styleRatioThreshold?: number;
938
942
  minElementsForAnalysis?: number;
939
943
  allowedStyleProperties?: string[];
940
944
  ignoreComponents?: string[];
945
+ preferSemanticColors?: boolean;
946
+ allowedHardCodedColors?: string[];
941
947
  } | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
942
948
  name: string;
943
949
  };
package/dist/index.js CHANGED
@@ -5879,6 +5879,13 @@ var meta15 = defineRuleMeta({
5879
5879
  type: "number",
5880
5880
  defaultValue: 50,
5881
5881
  description: "Threshold for components/handlers when focusNonReact is enabled"
5882
+ },
5883
+ {
5884
+ key: "minStatements",
5885
+ label: "Minimum statements for coverage",
5886
+ type: "number",
5887
+ defaultValue: 5,
5888
+ description: "Files with fewer statements are exempt from coverage requirements"
5882
5889
  }
5883
5890
  ]
5884
5891
  },
@@ -6221,7 +6228,8 @@ var require_test_coverage_default = createRule({
6221
6228
  chunkThreshold: 80,
6222
6229
  focusNonReact: false,
6223
6230
  relaxedThreshold: 50,
6224
- chunkSeverity: "warn"
6231
+ chunkSeverity: "warn",
6232
+ minStatements: 5
6225
6233
  }
6226
6234
  ],
6227
6235
  create(context) {
@@ -6255,6 +6263,7 @@ var require_test_coverage_default = createRule({
6255
6263
  const focusNonReact = options.focusNonReact ?? false;
6256
6264
  const relaxedThreshold = options.relaxedThreshold ?? 50;
6257
6265
  const chunkSeverity = options.chunkSeverity ?? "warn";
6266
+ const minStatements = options.minStatements ?? 5;
6258
6267
  const filename = context.filename || context.getFilename();
6259
6268
  const projectRoot = findProjectRoot6(dirname8(filename));
6260
6269
  const relPath = relative4(projectRoot, filename);
@@ -6298,6 +6307,10 @@ var require_test_coverage_default = createRule({
6298
6307
  if (!fileCoverage) {
6299
6308
  return;
6300
6309
  }
6310
+ const statementCount = Object.keys(fileCoverage.s).length;
6311
+ if (statementCount < minStatements) {
6312
+ return;
6313
+ }
6301
6314
  let coveragePercent;
6302
6315
  if (mode === "changed") {
6303
6316
  const changedLines = getChangedLines(
@@ -6501,7 +6514,9 @@ var meta16 = defineRuleMeta({
6501
6514
  styleRatioThreshold: 0.3,
6502
6515
  minElementsForAnalysis: 3,
6503
6516
  allowedStyleProperties: [],
6504
- ignoreComponents: []
6517
+ ignoreComponents: [],
6518
+ preferSemanticColors: true,
6519
+ allowedHardCodedColors: []
6505
6520
  }
6506
6521
  ],
6507
6522
  optionSchema: {
@@ -6533,6 +6548,20 @@ var meta16 = defineRuleMeta({
6533
6548
  type: "text",
6534
6549
  defaultValue: "",
6535
6550
  description: "Comma-separated component names to skip (e.g., motion.div,animated.View)"
6551
+ },
6552
+ {
6553
+ key: "preferSemanticColors",
6554
+ label: "Prefer semantic colors",
6555
+ type: "boolean",
6556
+ defaultValue: true,
6557
+ description: "Warn against hard-coded colors (bg-red-500) in favor of semantic theme colors (bg-destructive)"
6558
+ },
6559
+ {
6560
+ key: "allowedHardCodedColors",
6561
+ label: "Allowed hard-coded colors",
6562
+ type: "text",
6563
+ defaultValue: "",
6564
+ description: "Comma-separated color names to allow when preferSemanticColors is enabled (e.g., gray,slate)"
6536
6565
  }
6537
6566
  ]
6538
6567
  },
@@ -6581,10 +6610,36 @@ but only when the file exceeds a configurable threshold ratio.
6581
6610
  styleRatioThreshold: 0.3, // Warn when >30% of elements are style-only
6582
6611
  minElementsForAnalysis: 3, // Need at least 3 styled elements to analyze
6583
6612
  allowedStyleProperties: ["transform", "animation"], // Skip these properties
6584
- ignoreComponents: ["motion.div", "animated.View"] // Skip animation libraries
6613
+ ignoreComponents: ["motion.div", "animated.View"], // Skip animation libraries
6614
+ preferSemanticColors: true, // Warn on hard-coded colors like bg-red-500
6615
+ allowedHardCodedColors: ["gray", "slate"] // Allow specific color palettes
6585
6616
  }]
6586
6617
  \`\`\`
6587
6618
 
6619
+ ## Semantic Colors
6620
+
6621
+ When \`preferSemanticColors\` is enabled, the rule warns against hard-coded Tailwind color classes
6622
+ in favor of semantic theme colors:
6623
+
6624
+ ### \u274C Hard-coded colors (when enabled)
6625
+
6626
+ \`\`\`tsx
6627
+ <div className="bg-red-500 text-white">Error</div>
6628
+ <button className="hover:bg-blue-600">Click</button>
6629
+ \`\`\`
6630
+
6631
+ ### \u2705 Semantic colors (preferred)
6632
+
6633
+ \`\`\`tsx
6634
+ <div className="bg-destructive text-destructive-foreground">Error</div>
6635
+ <button className="hover:bg-primary">Click</button>
6636
+ \`\`\`
6637
+
6638
+ Semantic colors like \`bg-background\`, \`text-foreground\`, \`bg-primary\`, \`bg-destructive\`,
6639
+ \`bg-muted\`, etc. work better with theming and dark mode.
6640
+
6641
+ Colors that are always allowed: \`white\`, \`black\`, \`transparent\`, \`inherit\`, \`current\`.
6642
+
6588
6643
  ## Notes
6589
6644
 
6590
6645
  - Elements with BOTH \`style\` and \`className\` are considered acceptable
@@ -6636,6 +6691,63 @@ function hasOnlyAllowedProperties(styleProperties, allowedProperties) {
6636
6691
  }
6637
6692
  return styleProperties.every((prop) => allowedProperties.includes(prop));
6638
6693
  }
6694
+ var HARD_CODED_COLOR_NAMES = [
6695
+ "red",
6696
+ "orange",
6697
+ "amber",
6698
+ "yellow",
6699
+ "lime",
6700
+ "green",
6701
+ "emerald",
6702
+ "teal",
6703
+ "cyan",
6704
+ "sky",
6705
+ "blue",
6706
+ "indigo",
6707
+ "violet",
6708
+ "purple",
6709
+ "fuchsia",
6710
+ "pink",
6711
+ "rose",
6712
+ "slate",
6713
+ "gray",
6714
+ "zinc",
6715
+ "neutral",
6716
+ "stone"
6717
+ ];
6718
+ function createHardCodedColorRegex(colorNames) {
6719
+ const colorPattern = colorNames.join("|");
6720
+ return new RegExp(
6721
+ `(?:^|\\s)(?:[a-z-]+:)*(?:bg|text|border|ring|outline|decoration|accent|fill|stroke|from|via|to|divide|placeholder|caret|shadow)-(${colorPattern})-\\d{1,3}(?:/\\d{1,3})?(?=\\s|$)`,
6722
+ "g"
6723
+ );
6724
+ }
6725
+ function getClassNameValue(attr) {
6726
+ if (!attr.value) return null;
6727
+ if (attr.value.type === "Literal" && typeof attr.value.value === "string") {
6728
+ return attr.value.value;
6729
+ }
6730
+ if (attr.value.type === "JSXExpressionContainer" && attr.value.expression.type === "Literal" && typeof attr.value.expression.value === "string") {
6731
+ return attr.value.expression.value;
6732
+ }
6733
+ if (attr.value.type === "JSXExpressionContainer" && attr.value.expression.type === "TemplateLiteral") {
6734
+ return attr.value.expression.quasis.map((q) => q.value.raw).join(" ");
6735
+ }
6736
+ return null;
6737
+ }
6738
+ function findHardCodedColors(className, allowedColors) {
6739
+ const disallowedColorNames = HARD_CODED_COLOR_NAMES.filter(
6740
+ (c) => !allowedColors.includes(c)
6741
+ );
6742
+ if (disallowedColorNames.length === 0) return [];
6743
+ const regex = createHardCodedColorRegex(disallowedColorNames);
6744
+ const matches = [];
6745
+ let match;
6746
+ while ((match = regex.exec(className)) !== null) {
6747
+ matches.push(match[0].trim());
6748
+ }
6749
+ return matches;
6750
+ }
6639
6751
  var prefer_tailwind_default = createRule({
6640
6752
  name: "prefer-tailwind",
6641
6753
  meta: {
@@ -6644,7 +6756,8 @@ var prefer_tailwind_default = createRule({
6644
6756
  description: "Encourage Tailwind className over inline style attributes"
6645
6757
  },
6646
6758
  messages: {
6647
- preferTailwind: "Prefer Tailwind className over inline style. This element uses style attribute without className."
6759
+ preferTailwind: "Prefer Tailwind className over inline style. This element uses style attribute without className.",
6760
+ preferSemanticColors: "Prefer semantic color classes (e.g., bg-destructive, text-primary) over hard-coded colors (e.g., bg-red-500)."
6648
6761
  },
6649
6762
  schema: [
6650
6763
  {
@@ -6670,6 +6783,15 @@ var prefer_tailwind_default = createRule({
6670
6783
  type: "array",
6671
6784
  items: { type: "string" },
6672
6785
  description: "Component names to skip"
6786
+ },
6787
+ preferSemanticColors: {
6788
+ type: "boolean",
6789
+ description: "Warn against hard-coded colors in favor of semantic theme colors"
6790
+ },
6791
+ allowedHardCodedColors: {
6792
+ type: "array",
6793
+ items: { type: "string" },
6794
+ description: "Hard-coded color names to allow when preferSemanticColors is enabled"
6673
6795
  }
6674
6796
  },
6675
6797
  additionalProperties: false
@@ -6681,7 +6803,9 @@ var prefer_tailwind_default = createRule({
6681
6803
  styleRatioThreshold: 0.3,
6682
6804
  minElementsForAnalysis: 3,
6683
6805
  allowedStyleProperties: [],
6684
- ignoreComponents: []
6806
+ ignoreComponents: [],
6807
+ preferSemanticColors: false,
6808
+ allowedHardCodedColors: []
6685
6809
  }
6686
6810
  ],
6687
6811
  create(context) {
@@ -6690,6 +6814,8 @@ var prefer_tailwind_default = createRule({
6690
6814
  const minElementsForAnalysis = options.minElementsForAnalysis ?? 3;
6691
6815
  const allowedStyleProperties = options.allowedStyleProperties ?? [];
6692
6816
  const ignoreComponents = options.ignoreComponents ?? [];
6817
+ const preferSemanticColors = options.preferSemanticColors ?? false;
6818
+ const allowedHardCodedColors = options.allowedHardCodedColors ?? [];
6693
6819
  const styledElements = [];
6694
6820
  function isStyleAttribute(attr) {
6695
6821
  return attr.name.type === "JSXIdentifier" && attr.name.name === "style" && attr.value?.type === "JSXExpressionContainer";
@@ -6716,11 +6842,31 @@ var prefer_tailwind_default = createRule({
6716
6842
  }
6717
6843
  if (isClassNameAttribute(attr)) {
6718
6844
  hasClassName = true;
6845
+ if (preferSemanticColors) {
6846
+ const classNameValue = getClassNameValue(attr);
6847
+ if (classNameValue) {
6848
+ const hardCodedColors = findHardCodedColors(
6849
+ classNameValue,
6850
+ allowedHardCodedColors
6851
+ );
6852
+ if (hardCodedColors.length > 0) {
6853
+ context.report({
6854
+ node,
6855
+ messageId: "preferSemanticColors"
6856
+ });
6857
+ }
6858
+ }
6859
+ }
6719
6860
  }
6720
6861
  }
6721
6862
  }
6722
6863
  if (hasStyle || hasClassName) {
6723
- styledElements.push({ node, hasStyle, hasClassName, styleProperties });
6864
+ styledElements.push({
6865
+ node,
6866
+ hasStyle,
6867
+ hasClassName,
6868
+ styleProperties
6869
+ });
6724
6870
  }
6725
6871
  },
6726
6872
  "Program:exit"() {
@@ -6968,7 +7114,9 @@ var recommendedConfig = {
6968
7114
  "styleRatioThreshold": 0.3,
6969
7115
  "minElementsForAnalysis": 3,
6970
7116
  "allowedStyleProperties": [],
6971
- "ignoreComponents": []
7117
+ "ignoreComponents": [],
7118
+ "preferSemanticColors": true,
7119
+ "allowedHardCodedColors": []
6972
7120
  }
6973
7121
  ]]
6974
7122
  }
@@ -7114,7 +7262,9 @@ var strictConfig = {
7114
7262
  "styleRatioThreshold": 0.3,
7115
7263
  "minElementsForAnalysis": 3,
7116
7264
  "allowedStyleProperties": [],
7117
- "ignoreComponents": []
7265
+ "ignoreComponents": [],
7266
+ "preferSemanticColors": true,
7267
+ "allowedHardCodedColors": []
7118
7268
  }
7119
7269
  ]]
7120
7270
  }