uilint-eslint 0.2.164 → 0.2.165
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 +12 -2
- package/dist/index.js +304 -23
- package/dist/index.js.map +1 -1
- package/dist/rules/prefer-tailwind.js +286 -21
- package/dist/rules/prefer-tailwind.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +18 -2
- package/src/rules/prefer-tailwind/index.test.ts +96 -0
- package/src/rules/prefer-tailwind/index.ts +405 -25
package/dist/index.d.ts
CHANGED
|
@@ -529,7 +529,7 @@ declare const rules: {
|
|
|
529
529
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
530
530
|
name: string;
|
|
531
531
|
};
|
|
532
|
-
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors" | "preferSemanticColorsWithSuggestion" | "preferSemanticClassGroups" | "semanticOpacityModifier", [({
|
|
532
|
+
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors" | "preferSemanticColorsWithSuggestion" | "preferSemanticClassGroups" | "semanticOpacityModifier" | "componentVariantLeakage", [({
|
|
533
533
|
styleRatioThreshold?: number;
|
|
534
534
|
minElementsForAnalysis?: number;
|
|
535
535
|
allowedStyleProperties?: string[];
|
|
@@ -543,6 +543,11 @@ declare const rules: {
|
|
|
543
543
|
disallowSemanticOpacityModifiers?: boolean;
|
|
544
544
|
allowedOpacityModifierClasses?: string[];
|
|
545
545
|
allowedVisualUtilityClasses?: string[];
|
|
546
|
+
preferComponentVariants?: boolean;
|
|
547
|
+
componentVariantComponents?: string[];
|
|
548
|
+
componentVariantProps?: string[];
|
|
549
|
+
componentVariantClassThreshold?: number;
|
|
550
|
+
allowedComponentVariantClasses?: string[];
|
|
546
551
|
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
547
552
|
name: string;
|
|
548
553
|
};
|
|
@@ -655,7 +660,7 @@ declare const plugin: {
|
|
|
655
660
|
}], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
656
661
|
name: string;
|
|
657
662
|
};
|
|
658
|
-
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors" | "preferSemanticColorsWithSuggestion" | "preferSemanticClassGroups" | "semanticOpacityModifier", [({
|
|
663
|
+
"prefer-tailwind": _typescript_eslint_utils_ts_eslint.RuleModule<"preferTailwind" | "preferSemanticColors" | "preferSemanticColorsWithSuggestion" | "preferSemanticClassGroups" | "semanticOpacityModifier" | "componentVariantLeakage", [({
|
|
659
664
|
styleRatioThreshold?: number;
|
|
660
665
|
minElementsForAnalysis?: number;
|
|
661
666
|
allowedStyleProperties?: string[];
|
|
@@ -669,6 +674,11 @@ declare const plugin: {
|
|
|
669
674
|
disallowSemanticOpacityModifiers?: boolean;
|
|
670
675
|
allowedOpacityModifierClasses?: string[];
|
|
671
676
|
allowedVisualUtilityClasses?: string[];
|
|
677
|
+
preferComponentVariants?: boolean;
|
|
678
|
+
componentVariantComponents?: string[];
|
|
679
|
+
componentVariantProps?: string[];
|
|
680
|
+
componentVariantClassThreshold?: number;
|
|
681
|
+
allowedComponentVariantClasses?: string[];
|
|
672
682
|
} | undefined)?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
673
683
|
name: string;
|
|
674
684
|
};
|
package/dist/index.js
CHANGED
|
@@ -4988,7 +4988,7 @@ function formatSuggestionsForMessage(suggestions, limit = 2) {
|
|
|
4988
4988
|
// src/rules/prefer-tailwind/index.ts
|
|
4989
4989
|
var meta14 = defineRuleMeta({
|
|
4990
4990
|
id: "prefer-tailwind",
|
|
4991
|
-
version: "1.
|
|
4991
|
+
version: "1.3.0",
|
|
4992
4992
|
name: "Prefer Tailwind",
|
|
4993
4993
|
description: "Encourage Tailwind className over inline style attributes",
|
|
4994
4994
|
defaultSeverity: "warn",
|
|
@@ -5011,7 +5011,12 @@ var meta14 = defineRuleMeta({
|
|
|
5011
5011
|
visualUtilityMinGroups: 2,
|
|
5012
5012
|
disallowSemanticOpacityModifiers: true,
|
|
5013
5013
|
allowedOpacityModifierClasses: [],
|
|
5014
|
-
allowedVisualUtilityClasses: []
|
|
5014
|
+
allowedVisualUtilityClasses: [],
|
|
5015
|
+
preferComponentVariants: true,
|
|
5016
|
+
componentVariantComponents: [],
|
|
5017
|
+
componentVariantProps: ["variant", "size"],
|
|
5018
|
+
componentVariantClassThreshold: 4,
|
|
5019
|
+
allowedComponentVariantClasses: []
|
|
5015
5020
|
}
|
|
5016
5021
|
],
|
|
5017
5022
|
optionSchema: {
|
|
@@ -5106,6 +5111,41 @@ var meta14 = defineRuleMeta({
|
|
|
5106
5111
|
type: "text",
|
|
5107
5112
|
defaultValue: "",
|
|
5108
5113
|
description: "Comma-separated exact visual utility classes to ignore in cluster detection"
|
|
5114
|
+
},
|
|
5115
|
+
{
|
|
5116
|
+
key: "preferComponentVariants",
|
|
5117
|
+
label: "Prefer component variants",
|
|
5118
|
+
type: "boolean",
|
|
5119
|
+
defaultValue: true,
|
|
5120
|
+
description: "Warn when design-system components with variant or size props also carry bespoke styling classes"
|
|
5121
|
+
},
|
|
5122
|
+
{
|
|
5123
|
+
key: "componentVariantComponents",
|
|
5124
|
+
label: "Component variant components",
|
|
5125
|
+
type: "text",
|
|
5126
|
+
defaultValue: "",
|
|
5127
|
+
description: "Optional comma-separated component names to inspect; empty means any custom JSX component with variant props"
|
|
5128
|
+
},
|
|
5129
|
+
{
|
|
5130
|
+
key: "componentVariantProps",
|
|
5131
|
+
label: "Component variant props",
|
|
5132
|
+
type: "text",
|
|
5133
|
+
defaultValue: "variant,size",
|
|
5134
|
+
description: "Comma-separated prop names that indicate a component already exposes styling variants"
|
|
5135
|
+
},
|
|
5136
|
+
{
|
|
5137
|
+
key: "componentVariantClassThreshold",
|
|
5138
|
+
label: "Component variant class threshold",
|
|
5139
|
+
type: "number",
|
|
5140
|
+
defaultValue: 4,
|
|
5141
|
+
description: "Minimum number of styling override classes before warning on a variant component"
|
|
5142
|
+
},
|
|
5143
|
+
{
|
|
5144
|
+
key: "allowedComponentVariantClasses",
|
|
5145
|
+
label: "Allowed component variant classes",
|
|
5146
|
+
type: "text",
|
|
5147
|
+
defaultValue: "",
|
|
5148
|
+
description: "Comma-separated exact classes to ignore when checking component variant leakage"
|
|
5109
5149
|
}
|
|
5110
5150
|
]
|
|
5111
5151
|
},
|
|
@@ -5221,6 +5261,34 @@ opacity suffixes are reported:
|
|
|
5221
5261
|
Prefer a fully semantic token such as \`text-muted-foreground\`, or define a new
|
|
5222
5262
|
theme token/class when the opacity represents a reusable state.
|
|
5223
5263
|
|
|
5264
|
+
## Component Variant Leakage
|
|
5265
|
+
|
|
5266
|
+
When \`preferComponentVariants\` is enabled, the rule checks custom JSX
|
|
5267
|
+
components that already use style-like variant props such as \`variant\` or
|
|
5268
|
+
\`size\`. It ignores lowercase HTML elements by default. When \`className\` adds
|
|
5269
|
+
several bespoke styling overrides across static strings, template chunks, and
|
|
5270
|
+
common class combiners, the rule warns because the styling probably belongs in a
|
|
5271
|
+
component variant or semantic project class.
|
|
5272
|
+
|
|
5273
|
+
### \u274C Bespoke overrides on a variant component
|
|
5274
|
+
|
|
5275
|
+
\`\`\`tsx
|
|
5276
|
+
<Button
|
|
5277
|
+
variant="ghost"
|
|
5278
|
+
size="xs"
|
|
5279
|
+
className={cn(
|
|
5280
|
+
"h-auto gap-1 rounded-md px-2 py-0.5 text-[11px] font-medium",
|
|
5281
|
+
running ? "cursor-not-allowed opacity-40" : "bg-primary/10 text-primary hover:bg-primary/20"
|
|
5282
|
+
)}
|
|
5283
|
+
/>
|
|
5284
|
+
\`\`\`
|
|
5285
|
+
|
|
5286
|
+
### \u2705 Semantic component variant
|
|
5287
|
+
|
|
5288
|
+
\`\`\`tsx
|
|
5289
|
+
<Button variant="primarySubtle" size="xs" />
|
|
5290
|
+
\`\`\`
|
|
5291
|
+
|
|
5224
5292
|
## LLM-Powered Suggestions
|
|
5225
5293
|
|
|
5226
5294
|
When \`useLlmSuggestions\` is enabled and Ollama is running locally, the rule will:
|
|
@@ -5268,6 +5336,10 @@ function getComponentName2(node) {
|
|
|
5268
5336
|
}
|
|
5269
5337
|
return "";
|
|
5270
5338
|
}
|
|
5339
|
+
function isCustomComponentName(componentName) {
|
|
5340
|
+
const localName = componentName.split(".").at(-1) ?? componentName;
|
|
5341
|
+
return /^[A-Z]/.test(localName);
|
|
5342
|
+
}
|
|
5271
5343
|
function getStylePropertyNames(value) {
|
|
5272
5344
|
const expr = value.expression;
|
|
5273
5345
|
if (expr.type === "ObjectExpression") {
|
|
@@ -5399,6 +5471,7 @@ var SHADOW_SIZE_VALUES = /* @__PURE__ */ new Set([
|
|
|
5399
5471
|
"inner",
|
|
5400
5472
|
"none"
|
|
5401
5473
|
]);
|
|
5474
|
+
var DEFAULT_COMPONENT_VARIANT_PROPS = ["variant", "size"];
|
|
5402
5475
|
function stripImportant(value) {
|
|
5403
5476
|
return value.replace(/^!/, "").replace(/!$/, "");
|
|
5404
5477
|
}
|
|
@@ -5532,6 +5605,112 @@ function findSemanticOpacityModifiers(className, allowedClasses) {
|
|
|
5532
5605
|
}
|
|
5533
5606
|
return matches;
|
|
5534
5607
|
}
|
|
5608
|
+
function isSpacingOverride(baseClass) {
|
|
5609
|
+
return /^-?(p|px|py|pt|pr|pb|pl|m|mx|my|mt|mr|mb|ml|gap|gap-x|gap-y)-/.test(
|
|
5610
|
+
baseClass
|
|
5611
|
+
) || baseClass === "gap";
|
|
5612
|
+
}
|
|
5613
|
+
function isSizingOverride(baseClass) {
|
|
5614
|
+
return /^(size|h|min-h|max-h|w|min-w|max-w)-/.test(baseClass);
|
|
5615
|
+
}
|
|
5616
|
+
function isTypographyOverride(baseClass) {
|
|
5617
|
+
return baseClass.startsWith("text-[") || baseClass.startsWith("leading-") || baseClass.startsWith("tracking-") || baseClass.startsWith("font-");
|
|
5618
|
+
}
|
|
5619
|
+
function getComponentOverrideGroup(baseClass) {
|
|
5620
|
+
const visualGroup = getVisualUtilityGroup(baseClass);
|
|
5621
|
+
if (visualGroup) {
|
|
5622
|
+
return visualGroup;
|
|
5623
|
+
}
|
|
5624
|
+
if (isSpacingOverride(baseClass)) {
|
|
5625
|
+
return "spacing";
|
|
5626
|
+
}
|
|
5627
|
+
if (isSizingOverride(baseClass)) {
|
|
5628
|
+
return "sizing";
|
|
5629
|
+
}
|
|
5630
|
+
if (isTypographyOverride(baseClass)) {
|
|
5631
|
+
return "typography";
|
|
5632
|
+
}
|
|
5633
|
+
return null;
|
|
5634
|
+
}
|
|
5635
|
+
function findComponentVariantLeakageClasses(classNames, threshold, allowedClasses) {
|
|
5636
|
+
const matches = [];
|
|
5637
|
+
for (const className of classNames) {
|
|
5638
|
+
for (const token of extractClassTokens(className)) {
|
|
5639
|
+
if (classIsAllowed(token, allowedClasses)) {
|
|
5640
|
+
continue;
|
|
5641
|
+
}
|
|
5642
|
+
const group = getComponentOverrideGroup(token.base);
|
|
5643
|
+
if (group) {
|
|
5644
|
+
matches.push({ token: token.original, group });
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
if (matches.length < threshold) {
|
|
5649
|
+
return [];
|
|
5650
|
+
}
|
|
5651
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5652
|
+
return matches.map((match) => match.token).filter((token) => {
|
|
5653
|
+
if (seen.has(token)) {
|
|
5654
|
+
return false;
|
|
5655
|
+
}
|
|
5656
|
+
seen.add(token);
|
|
5657
|
+
return true;
|
|
5658
|
+
});
|
|
5659
|
+
}
|
|
5660
|
+
function extractStaticClassStringsFromExpression(expression) {
|
|
5661
|
+
if (!expression || expression.type === "JSXEmptyExpression") {
|
|
5662
|
+
return [];
|
|
5663
|
+
}
|
|
5664
|
+
if (expression.type === "SpreadElement") {
|
|
5665
|
+
return [];
|
|
5666
|
+
}
|
|
5667
|
+
switch (expression.type) {
|
|
5668
|
+
case "Literal":
|
|
5669
|
+
return typeof expression.value === "string" ? [expression.value] : [];
|
|
5670
|
+
case "TemplateLiteral":
|
|
5671
|
+
return expression.quasis.map((quasi) => quasi.value.raw);
|
|
5672
|
+
case "ConditionalExpression":
|
|
5673
|
+
return [
|
|
5674
|
+
...extractStaticClassStringsFromExpression(expression.consequent),
|
|
5675
|
+
...extractStaticClassStringsFromExpression(expression.alternate)
|
|
5676
|
+
];
|
|
5677
|
+
case "LogicalExpression":
|
|
5678
|
+
return [
|
|
5679
|
+
...extractStaticClassStringsFromExpression(expression.left),
|
|
5680
|
+
...extractStaticClassStringsFromExpression(expression.right)
|
|
5681
|
+
];
|
|
5682
|
+
case "CallExpression":
|
|
5683
|
+
if (expression.callee.type !== "Identifier" || !CLASS_COMBINER_NAMES.has(expression.callee.name)) {
|
|
5684
|
+
return [];
|
|
5685
|
+
}
|
|
5686
|
+
return expression.arguments.flatMap(
|
|
5687
|
+
(arg) => extractStaticClassStringsFromExpression(arg)
|
|
5688
|
+
);
|
|
5689
|
+
case "ArrayExpression":
|
|
5690
|
+
return expression.elements.flatMap(
|
|
5691
|
+
(element) => extractStaticClassStringsFromExpression(element)
|
|
5692
|
+
);
|
|
5693
|
+
case "ObjectExpression":
|
|
5694
|
+
return expression.properties.flatMap((property) => {
|
|
5695
|
+
if (property.type !== "Property") {
|
|
5696
|
+
return [];
|
|
5697
|
+
}
|
|
5698
|
+
if (property.key.type === "Literal" && typeof property.key.value === "string") {
|
|
5699
|
+
return [property.key.value];
|
|
5700
|
+
}
|
|
5701
|
+
if (property.key.type === "Identifier") {
|
|
5702
|
+
return [property.key.name];
|
|
5703
|
+
}
|
|
5704
|
+
return [];
|
|
5705
|
+
});
|
|
5706
|
+
case "TSAsExpression":
|
|
5707
|
+
case "TSTypeAssertion":
|
|
5708
|
+
case "TSNonNullExpression":
|
|
5709
|
+
return extractStaticClassStringsFromExpression(expression.expression);
|
|
5710
|
+
default:
|
|
5711
|
+
return [];
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5535
5714
|
var prefer_tailwind_default = createRule({
|
|
5536
5715
|
name: "prefer-tailwind",
|
|
5537
5716
|
meta: {
|
|
@@ -5544,7 +5723,8 @@ var prefer_tailwind_default = createRule({
|
|
|
5544
5723
|
preferSemanticColors: "Hard-coded colors: {{colors}}. Use semantic classes instead.",
|
|
5545
5724
|
preferSemanticColorsWithSuggestion: "Hard-coded colors: {{colors}}. {{suggestion}}",
|
|
5546
5725
|
preferSemanticClassGroups: "Dense visual utility cluster: {{classes}}. Move repeated panel/card styling into a semantic class.",
|
|
5547
|
-
semanticOpacityModifier: "Semantic color opacity modifiers: {{classes}}. Use fully semantic classes or tokens instead."
|
|
5726
|
+
semanticOpacityModifier: "Semantic color opacity modifiers: {{classes}}. Use fully semantic classes or tokens instead.",
|
|
5727
|
+
componentVariantLeakage: "{{component}} already uses {{props}}; move bespoke styling overrides into a component variant or semantic class instead of className: {{classes}}."
|
|
5548
5728
|
},
|
|
5549
5729
|
schema: [
|
|
5550
5730
|
{
|
|
@@ -5611,6 +5791,30 @@ var prefer_tailwind_default = createRule({
|
|
|
5611
5791
|
type: "array",
|
|
5612
5792
|
items: { type: "string" },
|
|
5613
5793
|
description: "Exact visual utility classes to ignore in cluster detection"
|
|
5794
|
+
},
|
|
5795
|
+
preferComponentVariants: {
|
|
5796
|
+
type: "boolean",
|
|
5797
|
+
description: "Warn when design-system components with variant props also use bespoke styling classes"
|
|
5798
|
+
},
|
|
5799
|
+
componentVariantComponents: {
|
|
5800
|
+
type: "array",
|
|
5801
|
+
items: { type: "string" },
|
|
5802
|
+
description: "Optional component names to inspect for component variant leakage; empty means any custom JSX component"
|
|
5803
|
+
},
|
|
5804
|
+
componentVariantProps: {
|
|
5805
|
+
type: "array",
|
|
5806
|
+
items: { type: "string" },
|
|
5807
|
+
description: "Prop names that indicate a component exposes styling variants"
|
|
5808
|
+
},
|
|
5809
|
+
componentVariantClassThreshold: {
|
|
5810
|
+
type: "number",
|
|
5811
|
+
minimum: 1,
|
|
5812
|
+
description: "Minimum styling override class count before warning on a variant component"
|
|
5813
|
+
},
|
|
5814
|
+
allowedComponentVariantClasses: {
|
|
5815
|
+
type: "array",
|
|
5816
|
+
items: { type: "string" },
|
|
5817
|
+
description: "Exact classes to ignore in component variant leakage detection"
|
|
5614
5818
|
}
|
|
5615
5819
|
},
|
|
5616
5820
|
additionalProperties: false
|
|
@@ -5631,7 +5835,12 @@ var prefer_tailwind_default = createRule({
|
|
|
5631
5835
|
visualUtilityMinGroups: 2,
|
|
5632
5836
|
disallowSemanticOpacityModifiers: true,
|
|
5633
5837
|
allowedOpacityModifierClasses: [],
|
|
5634
|
-
allowedVisualUtilityClasses: []
|
|
5838
|
+
allowedVisualUtilityClasses: [],
|
|
5839
|
+
preferComponentVariants: true,
|
|
5840
|
+
componentVariantComponents: [],
|
|
5841
|
+
componentVariantProps: DEFAULT_COMPONENT_VARIANT_PROPS,
|
|
5842
|
+
componentVariantClassThreshold: 4,
|
|
5843
|
+
allowedComponentVariantClasses: []
|
|
5635
5844
|
}
|
|
5636
5845
|
],
|
|
5637
5846
|
create(context) {
|
|
@@ -5649,6 +5858,11 @@ var prefer_tailwind_default = createRule({
|
|
|
5649
5858
|
const disallowSemanticOpacityModifiers = options.disallowSemanticOpacityModifiers ?? true;
|
|
5650
5859
|
const allowedOpacityModifierClasses = options.allowedOpacityModifierClasses ?? [];
|
|
5651
5860
|
const allowedVisualUtilityClasses = options.allowedVisualUtilityClasses ?? [];
|
|
5861
|
+
const preferComponentVariants = options.preferComponentVariants ?? true;
|
|
5862
|
+
const componentVariantComponents = options.componentVariantComponents ?? [];
|
|
5863
|
+
const componentVariantProps = options.componentVariantProps ?? DEFAULT_COMPONENT_VARIANT_PROPS;
|
|
5864
|
+
const componentVariantClassThreshold = options.componentVariantClassThreshold ?? 4;
|
|
5865
|
+
const allowedComponentVariantClasses = options.allowedComponentVariantClasses ?? [];
|
|
5652
5866
|
let fileContent = null;
|
|
5653
5867
|
const filePath = context.filename;
|
|
5654
5868
|
const fileDir = dirname5(filePath);
|
|
@@ -5669,6 +5883,69 @@ var prefer_tailwind_default = createRule({
|
|
|
5669
5883
|
function isClassNameAttribute(attr) {
|
|
5670
5884
|
return attr.name.type === "JSXIdentifier" && (attr.name.name === "className" || attr.name.name === "class");
|
|
5671
5885
|
}
|
|
5886
|
+
function getJSXAttributeName(attr) {
|
|
5887
|
+
return attr.name.type === "JSXIdentifier" ? attr.name.name : null;
|
|
5888
|
+
}
|
|
5889
|
+
function getClassAttributeStaticStrings(attr) {
|
|
5890
|
+
const value = attr.value;
|
|
5891
|
+
if (value?.type === "Literal" && typeof value.value === "string") {
|
|
5892
|
+
return [value.value];
|
|
5893
|
+
}
|
|
5894
|
+
if (value?.type === "JSXExpressionContainer") {
|
|
5895
|
+
return extractStaticClassStringsFromExpression(value.expression);
|
|
5896
|
+
}
|
|
5897
|
+
return [];
|
|
5898
|
+
}
|
|
5899
|
+
function getComponentVariantPropNames(node) {
|
|
5900
|
+
const propNames = /* @__PURE__ */ new Set();
|
|
5901
|
+
for (const attr of node.attributes) {
|
|
5902
|
+
if (attr.type !== "JSXAttribute") {
|
|
5903
|
+
continue;
|
|
5904
|
+
}
|
|
5905
|
+
const attrName = getJSXAttributeName(attr);
|
|
5906
|
+
if (attrName && componentVariantProps.includes(attrName)) {
|
|
5907
|
+
propNames.add(attrName);
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
return [...propNames];
|
|
5911
|
+
}
|
|
5912
|
+
function shouldInspectComponentVariantLeakage(componentName) {
|
|
5913
|
+
if (!preferComponentVariants || !isCustomComponentName(componentName)) {
|
|
5914
|
+
return false;
|
|
5915
|
+
}
|
|
5916
|
+
if (componentVariantComponents.length === 0) {
|
|
5917
|
+
return true;
|
|
5918
|
+
}
|
|
5919
|
+
return componentVariantComponents.includes(componentName);
|
|
5920
|
+
}
|
|
5921
|
+
function checkComponentVariantLeakage(node, classNameAttr) {
|
|
5922
|
+
const componentName = getComponentName2(node);
|
|
5923
|
+
if (!shouldInspectComponentVariantLeakage(componentName)) {
|
|
5924
|
+
return;
|
|
5925
|
+
}
|
|
5926
|
+
const variantPropNames = getComponentVariantPropNames(node);
|
|
5927
|
+
if (variantPropNames.length === 0 || !classNameAttr) {
|
|
5928
|
+
return;
|
|
5929
|
+
}
|
|
5930
|
+
const classStrings = getClassAttributeStaticStrings(classNameAttr);
|
|
5931
|
+
const leakageClasses = findComponentVariantLeakageClasses(
|
|
5932
|
+
classStrings,
|
|
5933
|
+
componentVariantClassThreshold,
|
|
5934
|
+
allowedComponentVariantClasses
|
|
5935
|
+
);
|
|
5936
|
+
if (leakageClasses.length === 0) {
|
|
5937
|
+
return;
|
|
5938
|
+
}
|
|
5939
|
+
context.report({
|
|
5940
|
+
node: classNameAttr,
|
|
5941
|
+
messageId: "componentVariantLeakage",
|
|
5942
|
+
data: {
|
|
5943
|
+
component: componentName,
|
|
5944
|
+
props: variantPropNames.join("/"),
|
|
5945
|
+
classes: leakageClasses.slice(0, 12).join(", ")
|
|
5946
|
+
}
|
|
5947
|
+
});
|
|
5948
|
+
}
|
|
5672
5949
|
function checkClassString(node, className) {
|
|
5673
5950
|
if (preferSemanticColors) {
|
|
5674
5951
|
const hardCodedColors = findHardCodedColors(
|
|
@@ -5764,6 +6041,7 @@ var prefer_tailwind_default = createRule({
|
|
|
5764
6041
|
let hasStyle = false;
|
|
5765
6042
|
let hasClassName = false;
|
|
5766
6043
|
let styleProperties = [];
|
|
6044
|
+
let classNameAttr = null;
|
|
5767
6045
|
for (const attr of node.attributes) {
|
|
5768
6046
|
if (attr.type === "JSXAttribute") {
|
|
5769
6047
|
if (isStyleAttribute(attr)) {
|
|
@@ -5774,10 +6052,12 @@ var prefer_tailwind_default = createRule({
|
|
|
5774
6052
|
}
|
|
5775
6053
|
if (isClassNameAttribute(attr)) {
|
|
5776
6054
|
hasClassName = true;
|
|
6055
|
+
classNameAttr = attr;
|
|
5777
6056
|
processClassAttribute(attr);
|
|
5778
6057
|
}
|
|
5779
6058
|
}
|
|
5780
6059
|
}
|
|
6060
|
+
checkComponentVariantLeakage(node, classNameAttr);
|
|
5781
6061
|
if (hasStyle || hasClassName) {
|
|
5782
6062
|
styledElements.push({
|
|
5783
6063
|
node,
|
|
@@ -5817,23 +6097,8 @@ var prefer_tailwind_default = createRule({
|
|
|
5817
6097
|
if (!CLASS_COMBINER_NAMES.has(node.callee.name)) {
|
|
5818
6098
|
return;
|
|
5819
6099
|
}
|
|
5820
|
-
for (const
|
|
5821
|
-
|
|
5822
|
-
checkClassString(arg, arg.value);
|
|
5823
|
-
}
|
|
5824
|
-
if (arg.type === "TemplateLiteral") {
|
|
5825
|
-
processTemplateLiteral(arg);
|
|
5826
|
-
}
|
|
5827
|
-
if (arg.type === "ArrayExpression") {
|
|
5828
|
-
for (const element of arg.elements) {
|
|
5829
|
-
if (element?.type === "Literal" && typeof element.value === "string") {
|
|
5830
|
-
checkClassString(element, element.value);
|
|
5831
|
-
}
|
|
5832
|
-
if (element?.type === "TemplateLiteral") {
|
|
5833
|
-
processTemplateLiteral(element);
|
|
5834
|
-
}
|
|
5835
|
-
}
|
|
5836
|
-
}
|
|
6100
|
+
for (const className of extractStaticClassStringsFromExpression(node)) {
|
|
6101
|
+
checkClassString(node, className);
|
|
5837
6102
|
}
|
|
5838
6103
|
}
|
|
5839
6104
|
};
|
|
@@ -6265,7 +6530,15 @@ var recommendedConfig = {
|
|
|
6265
6530
|
"visualUtilityMinGroups": 2,
|
|
6266
6531
|
"disallowSemanticOpacityModifiers": true,
|
|
6267
6532
|
"allowedOpacityModifierClasses": [],
|
|
6268
|
-
"allowedVisualUtilityClasses": []
|
|
6533
|
+
"allowedVisualUtilityClasses": [],
|
|
6534
|
+
"preferComponentVariants": true,
|
|
6535
|
+
"componentVariantComponents": [],
|
|
6536
|
+
"componentVariantProps": [
|
|
6537
|
+
"variant",
|
|
6538
|
+
"size"
|
|
6539
|
+
],
|
|
6540
|
+
"componentVariantClassThreshold": 4,
|
|
6541
|
+
"allowedComponentVariantClasses": []
|
|
6269
6542
|
}
|
|
6270
6543
|
]]
|
|
6271
6544
|
}
|
|
@@ -6405,7 +6678,15 @@ var strictConfig = {
|
|
|
6405
6678
|
"visualUtilityMinGroups": 2,
|
|
6406
6679
|
"disallowSemanticOpacityModifiers": true,
|
|
6407
6680
|
"allowedOpacityModifierClasses": [],
|
|
6408
|
-
"allowedVisualUtilityClasses": []
|
|
6681
|
+
"allowedVisualUtilityClasses": [],
|
|
6682
|
+
"preferComponentVariants": true,
|
|
6683
|
+
"componentVariantComponents": [],
|
|
6684
|
+
"componentVariantProps": [
|
|
6685
|
+
"variant",
|
|
6686
|
+
"size"
|
|
6687
|
+
],
|
|
6688
|
+
"componentVariantClassThreshold": 4,
|
|
6689
|
+
"allowedComponentVariantClasses": []
|
|
6409
6690
|
}
|
|
6410
6691
|
]]
|
|
6411
6692
|
}
|