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 +8 -2
- package/dist/index.js +158 -8
- 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/dist/rules/require-test-coverage.js +14 -1
- package/dist/rules/require-test-coverage.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/src/rules/require-test-coverage/index.ts +18 -0
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"]
|
|
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({
|
|
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
|
}
|