uilint-eslint 0.2.56 → 0.2.58
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 +6 -2
- package/dist/index.js +144 -7
- package/dist/index.js.map +1 -1
- package/dist/rules/prefer-tailwind.js +138 -5
- package/dist/rules/prefer-tailwind.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +6 -2
- package/src/rule-registry.test.ts +3 -3
- package/src/rules/prefer-tailwind.test.ts +254 -0
- package/src/rules/prefer-tailwind.ts +206 -5
package/dist/index.d.ts
CHANGED
|
@@ -790,11 +790,13 @@ declare const rules: {
|
|
|
790
790
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
791
791
|
name: string;
|
|
792
792
|
};
|
|
793
|
-
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind", [({
|
|
793
|
+
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors", [({
|
|
794
794
|
styleRatioThreshold?: number;
|
|
795
795
|
minElementsForAnalysis?: number;
|
|
796
796
|
allowedStyleProperties?: string[];
|
|
797
797
|
ignoreComponents?: string[];
|
|
798
|
+
preferSemanticColors?: boolean;
|
|
799
|
+
allowedHardCodedColors?: string[];
|
|
798
800
|
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
799
801
|
name: string;
|
|
800
802
|
};
|
|
@@ -933,11 +935,13 @@ declare const plugin: {
|
|
|
933
935
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
934
936
|
name: string;
|
|
935
937
|
};
|
|
936
|
-
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind", [({
|
|
938
|
+
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors", [({
|
|
937
939
|
styleRatioThreshold?: number;
|
|
938
940
|
minElementsForAnalysis?: number;
|
|
939
941
|
allowedStyleProperties?: string[];
|
|
940
942
|
ignoreComponents?: string[];
|
|
943
|
+
preferSemanticColors?: boolean;
|
|
944
|
+
allowedHardCodedColors?: string[];
|
|
941
945
|
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
942
946
|
name: string;
|
|
943
947
|
};
|
package/dist/index.js
CHANGED
|
@@ -6501,7 +6501,9 @@ var meta16 = defineRuleMeta({
|
|
|
6501
6501
|
styleRatioThreshold: 0.3,
|
|
6502
6502
|
minElementsForAnalysis: 3,
|
|
6503
6503
|
allowedStyleProperties: [],
|
|
6504
|
-
ignoreComponents: []
|
|
6504
|
+
ignoreComponents: [],
|
|
6505
|
+
preferSemanticColors: true,
|
|
6506
|
+
allowedHardCodedColors: []
|
|
6505
6507
|
}
|
|
6506
6508
|
],
|
|
6507
6509
|
optionSchema: {
|
|
@@ -6533,6 +6535,20 @@ var meta16 = defineRuleMeta({
|
|
|
6533
6535
|
type: "text",
|
|
6534
6536
|
defaultValue: "",
|
|
6535
6537
|
description: "Comma-separated component names to skip (e.g., motion.div,animated.View)"
|
|
6538
|
+
},
|
|
6539
|
+
{
|
|
6540
|
+
key: "preferSemanticColors",
|
|
6541
|
+
label: "Prefer semantic colors",
|
|
6542
|
+
type: "boolean",
|
|
6543
|
+
defaultValue: true,
|
|
6544
|
+
description: "Warn against hard-coded colors (bg-red-500) in favor of semantic theme colors (bg-destructive)"
|
|
6545
|
+
},
|
|
6546
|
+
{
|
|
6547
|
+
key: "allowedHardCodedColors",
|
|
6548
|
+
label: "Allowed hard-coded colors",
|
|
6549
|
+
type: "text",
|
|
6550
|
+
defaultValue: "",
|
|
6551
|
+
description: "Comma-separated color names to allow when preferSemanticColors is enabled (e.g., gray,slate)"
|
|
6536
6552
|
}
|
|
6537
6553
|
]
|
|
6538
6554
|
},
|
|
@@ -6581,10 +6597,36 @@ but only when the file exceeds a configurable threshold ratio.
|
|
|
6581
6597
|
styleRatioThreshold: 0.3, // Warn when >30% of elements are style-only
|
|
6582
6598
|
minElementsForAnalysis: 3, // Need at least 3 styled elements to analyze
|
|
6583
6599
|
allowedStyleProperties: ["transform", "animation"], // Skip these properties
|
|
6584
|
-
ignoreComponents: ["motion.div", "animated.View"]
|
|
6600
|
+
ignoreComponents: ["motion.div", "animated.View"], // Skip animation libraries
|
|
6601
|
+
preferSemanticColors: true, // Warn on hard-coded colors like bg-red-500
|
|
6602
|
+
allowedHardCodedColors: ["gray", "slate"] // Allow specific color palettes
|
|
6585
6603
|
}]
|
|
6586
6604
|
\`\`\`
|
|
6587
6605
|
|
|
6606
|
+
## Semantic Colors
|
|
6607
|
+
|
|
6608
|
+
When \`preferSemanticColors\` is enabled, the rule warns against hard-coded Tailwind color classes
|
|
6609
|
+
in favor of semantic theme colors:
|
|
6610
|
+
|
|
6611
|
+
### \u274C Hard-coded colors (when enabled)
|
|
6612
|
+
|
|
6613
|
+
\`\`\`tsx
|
|
6614
|
+
<div className="bg-red-500 text-white">Error</div>
|
|
6615
|
+
<button className="hover:bg-blue-600">Click</button>
|
|
6616
|
+
\`\`\`
|
|
6617
|
+
|
|
6618
|
+
### \u2705 Semantic colors (preferred)
|
|
6619
|
+
|
|
6620
|
+
\`\`\`tsx
|
|
6621
|
+
<div className="bg-destructive text-destructive-foreground">Error</div>
|
|
6622
|
+
<button className="hover:bg-primary">Click</button>
|
|
6623
|
+
\`\`\`
|
|
6624
|
+
|
|
6625
|
+
Semantic colors like \`bg-background\`, \`text-foreground\`, \`bg-primary\`, \`bg-destructive\`,
|
|
6626
|
+
\`bg-muted\`, etc. work better with theming and dark mode.
|
|
6627
|
+
|
|
6628
|
+
Colors that are always allowed: \`white\`, \`black\`, \`transparent\`, \`inherit\`, \`current\`.
|
|
6629
|
+
|
|
6588
6630
|
## Notes
|
|
6589
6631
|
|
|
6590
6632
|
- Elements with BOTH \`style\` and \`className\` are considered acceptable
|
|
@@ -6636,6 +6678,63 @@ function hasOnlyAllowedProperties(styleProperties, allowedProperties) {
|
|
|
6636
6678
|
}
|
|
6637
6679
|
return styleProperties.every((prop) => allowedProperties.includes(prop));
|
|
6638
6680
|
}
|
|
6681
|
+
var HARD_CODED_COLOR_NAMES = [
|
|
6682
|
+
"red",
|
|
6683
|
+
"orange",
|
|
6684
|
+
"amber",
|
|
6685
|
+
"yellow",
|
|
6686
|
+
"lime",
|
|
6687
|
+
"green",
|
|
6688
|
+
"emerald",
|
|
6689
|
+
"teal",
|
|
6690
|
+
"cyan",
|
|
6691
|
+
"sky",
|
|
6692
|
+
"blue",
|
|
6693
|
+
"indigo",
|
|
6694
|
+
"violet",
|
|
6695
|
+
"purple",
|
|
6696
|
+
"fuchsia",
|
|
6697
|
+
"pink",
|
|
6698
|
+
"rose",
|
|
6699
|
+
"slate",
|
|
6700
|
+
"gray",
|
|
6701
|
+
"zinc",
|
|
6702
|
+
"neutral",
|
|
6703
|
+
"stone"
|
|
6704
|
+
];
|
|
6705
|
+
function createHardCodedColorRegex(colorNames) {
|
|
6706
|
+
const colorPattern = colorNames.join("|");
|
|
6707
|
+
return new RegExp(
|
|
6708
|
+
`(?:^|\\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|$)`,
|
|
6709
|
+
"g"
|
|
6710
|
+
);
|
|
6711
|
+
}
|
|
6712
|
+
function getClassNameValue(attr) {
|
|
6713
|
+
if (!attr.value) return null;
|
|
6714
|
+
if (attr.value.type === "Literal" && typeof attr.value.value === "string") {
|
|
6715
|
+
return attr.value.value;
|
|
6716
|
+
}
|
|
6717
|
+
if (attr.value.type === "JSXExpressionContainer" && attr.value.expression.type === "Literal" && typeof attr.value.expression.value === "string") {
|
|
6718
|
+
return attr.value.expression.value;
|
|
6719
|
+
}
|
|
6720
|
+
if (attr.value.type === "JSXExpressionContainer" && attr.value.expression.type === "TemplateLiteral") {
|
|
6721
|
+
return attr.value.expression.quasis.map((q) => q.value.raw).join(" ");
|
|
6722
|
+
}
|
|
6723
|
+
return null;
|
|
6724
|
+
}
|
|
6725
|
+
function findHardCodedColors(className, allowedColors) {
|
|
6726
|
+
const disallowedColorNames = HARD_CODED_COLOR_NAMES.filter(
|
|
6727
|
+
(c) => !allowedColors.includes(c)
|
|
6728
|
+
);
|
|
6729
|
+
if (disallowedColorNames.length === 0) return [];
|
|
6730
|
+
const regex = createHardCodedColorRegex(disallowedColorNames);
|
|
6731
|
+
const matches = [];
|
|
6732
|
+
let match;
|
|
6733
|
+
while ((match = regex.exec(className)) !== null) {
|
|
6734
|
+
matches.push(match[0].trim());
|
|
6735
|
+
}
|
|
6736
|
+
return matches;
|
|
6737
|
+
}
|
|
6639
6738
|
var prefer_tailwind_default = createRule({
|
|
6640
6739
|
name: "prefer-tailwind",
|
|
6641
6740
|
meta: {
|
|
@@ -6644,7 +6743,8 @@ var prefer_tailwind_default = createRule({
|
|
|
6644
6743
|
description: "Encourage Tailwind className over inline style attributes"
|
|
6645
6744
|
},
|
|
6646
6745
|
messages: {
|
|
6647
|
-
preferTailwind: "Prefer Tailwind className over inline style. This element uses style attribute without className."
|
|
6746
|
+
preferTailwind: "Prefer Tailwind className over inline style. This element uses style attribute without className.",
|
|
6747
|
+
preferSemanticColors: "Prefer semantic color classes (e.g., bg-destructive, text-primary) over hard-coded colors (e.g., bg-red-500)."
|
|
6648
6748
|
},
|
|
6649
6749
|
schema: [
|
|
6650
6750
|
{
|
|
@@ -6670,6 +6770,15 @@ var prefer_tailwind_default = createRule({
|
|
|
6670
6770
|
type: "array",
|
|
6671
6771
|
items: { type: "string" },
|
|
6672
6772
|
description: "Component names to skip"
|
|
6773
|
+
},
|
|
6774
|
+
preferSemanticColors: {
|
|
6775
|
+
type: "boolean",
|
|
6776
|
+
description: "Warn against hard-coded colors in favor of semantic theme colors"
|
|
6777
|
+
},
|
|
6778
|
+
allowedHardCodedColors: {
|
|
6779
|
+
type: "array",
|
|
6780
|
+
items: { type: "string" },
|
|
6781
|
+
description: "Hard-coded color names to allow when preferSemanticColors is enabled"
|
|
6673
6782
|
}
|
|
6674
6783
|
},
|
|
6675
6784
|
additionalProperties: false
|
|
@@ -6681,7 +6790,9 @@ var prefer_tailwind_default = createRule({
|
|
|
6681
6790
|
styleRatioThreshold: 0.3,
|
|
6682
6791
|
minElementsForAnalysis: 3,
|
|
6683
6792
|
allowedStyleProperties: [],
|
|
6684
|
-
ignoreComponents: []
|
|
6793
|
+
ignoreComponents: [],
|
|
6794
|
+
preferSemanticColors: false,
|
|
6795
|
+
allowedHardCodedColors: []
|
|
6685
6796
|
}
|
|
6686
6797
|
],
|
|
6687
6798
|
create(context) {
|
|
@@ -6690,6 +6801,8 @@ var prefer_tailwind_default = createRule({
|
|
|
6690
6801
|
const minElementsForAnalysis = options.minElementsForAnalysis ?? 3;
|
|
6691
6802
|
const allowedStyleProperties = options.allowedStyleProperties ?? [];
|
|
6692
6803
|
const ignoreComponents = options.ignoreComponents ?? [];
|
|
6804
|
+
const preferSemanticColors = options.preferSemanticColors ?? false;
|
|
6805
|
+
const allowedHardCodedColors = options.allowedHardCodedColors ?? [];
|
|
6693
6806
|
const styledElements = [];
|
|
6694
6807
|
function isStyleAttribute(attr) {
|
|
6695
6808
|
return attr.name.type === "JSXIdentifier" && attr.name.name === "style" && attr.value?.type === "JSXExpressionContainer";
|
|
@@ -6716,11 +6829,31 @@ var prefer_tailwind_default = createRule({
|
|
|
6716
6829
|
}
|
|
6717
6830
|
if (isClassNameAttribute(attr)) {
|
|
6718
6831
|
hasClassName = true;
|
|
6832
|
+
if (preferSemanticColors) {
|
|
6833
|
+
const classNameValue = getClassNameValue(attr);
|
|
6834
|
+
if (classNameValue) {
|
|
6835
|
+
const hardCodedColors = findHardCodedColors(
|
|
6836
|
+
classNameValue,
|
|
6837
|
+
allowedHardCodedColors
|
|
6838
|
+
);
|
|
6839
|
+
if (hardCodedColors.length > 0) {
|
|
6840
|
+
context.report({
|
|
6841
|
+
node,
|
|
6842
|
+
messageId: "preferSemanticColors"
|
|
6843
|
+
});
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
6846
|
+
}
|
|
6719
6847
|
}
|
|
6720
6848
|
}
|
|
6721
6849
|
}
|
|
6722
6850
|
if (hasStyle || hasClassName) {
|
|
6723
|
-
styledElements.push({
|
|
6851
|
+
styledElements.push({
|
|
6852
|
+
node,
|
|
6853
|
+
hasStyle,
|
|
6854
|
+
hasClassName,
|
|
6855
|
+
styleProperties
|
|
6856
|
+
});
|
|
6724
6857
|
}
|
|
6725
6858
|
},
|
|
6726
6859
|
"Program:exit"() {
|
|
@@ -6968,7 +7101,9 @@ var recommendedConfig = {
|
|
|
6968
7101
|
"styleRatioThreshold": 0.3,
|
|
6969
7102
|
"minElementsForAnalysis": 3,
|
|
6970
7103
|
"allowedStyleProperties": [],
|
|
6971
|
-
"ignoreComponents": []
|
|
7104
|
+
"ignoreComponents": [],
|
|
7105
|
+
"preferSemanticColors": true,
|
|
7106
|
+
"allowedHardCodedColors": []
|
|
6972
7107
|
}
|
|
6973
7108
|
]]
|
|
6974
7109
|
}
|
|
@@ -7114,7 +7249,9 @@ var strictConfig = {
|
|
|
7114
7249
|
"styleRatioThreshold": 0.3,
|
|
7115
7250
|
"minElementsForAnalysis": 3,
|
|
7116
7251
|
"allowedStyleProperties": [],
|
|
7117
|
-
"ignoreComponents": []
|
|
7252
|
+
"ignoreComponents": [],
|
|
7253
|
+
"preferSemanticColors": true,
|
|
7254
|
+
"allowedHardCodedColors": []
|
|
7118
7255
|
}
|
|
7119
7256
|
]]
|
|
7120
7257
|
}
|