eslint-plugin-tailwind-variants 2.0.3 → 2.1.1
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/README.md +6 -4
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/rules/index.d.ts +7 -0
- package/dist/src/rules/index.d.ts.map +1 -0
- package/dist/src/rules/limited-inline-classes.d.ts +41 -0
- package/dist/src/rules/limited-inline-classes.d.ts.map +1 -0
- package/dist/src/rules/require-variants-call-styles-name.d.ts +19 -0
- package/dist/src/rules/require-variants-call-styles-name.d.ts.map +1 -0
- package/dist/src/rules/require-variants-suffix.d.ts +19 -0
- package/dist/src/rules/require-variants-suffix.d.ts.map +1 -0
- package/dist/src/rules/sort-custom-properties.d.ts +86 -0
- package/dist/src/rules/sort-custom-properties.d.ts.map +1 -0
- package/dist/src/utils/create-rule-visitors.d.ts +2 -0
- package/dist/src/utils/create-rule-visitors.d.ts.map +1 -0
- package/dist/src/utils/get-bind-class-expression.d.ts +3 -0
- package/dist/src/utils/get-bind-class-expression.d.ts.map +1 -0
- package/package.json +43 -37
- package/src/index.js +61 -0
- package/{dist → src}/rules/index.js +5 -5
- package/src/rules/limited-inline-classes.js +440 -0
- package/src/rules/limited-inline-classes.test.js +268 -0
- package/src/rules/require-variants-call-styles-name.js +195 -0
- package/src/rules/require-variants-call-styles-name.test.js +105 -0
- package/src/rules/require-variants-suffix.js +146 -0
- package/src/rules/require-variants-suffix.test.js +81 -0
- package/src/rules/sort-custom-properties.js +596 -0
- package/src/rules/sort-custom-properties.test.js +758 -0
- package/src/utils/create-rule-visitors.js +28 -0
- package/src/utils/get-bind-class-expression.js +36 -0
- package/dist/index.d.ts +0 -11
- package/dist/index.js +0 -43
- package/dist/rules/index.d.ts +0 -2
- package/dist/rules/limited-inline-classes.d.ts +0 -23
- package/dist/rules/limited-inline-classes.js +0 -198
- package/dist/rules/require-variants-call-styles-name.d.ts +0 -17
- package/dist/rules/require-variants-call-styles-name.js +0 -75
- package/dist/rules/require-variants-suffix.d.ts +0 -17
- package/dist/rules/require-variants-suffix.js +0 -66
- package/dist/rules/sort-custom-properties.d.ts +0 -37
- package/dist/rules/sort-custom-properties.js +0 -210
- package/dist/utils/create-rule-visitors.d.ts +0 -6
- package/dist/utils/create-rule-visitors.js +0 -18
- package/dist/utils/get-bind-class-expression.d.ts +0 -6
- package/dist/utils/get-bind-class-expression.js +0 -20
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create and return rule visitors that work for both Vue single-file components
|
|
3
|
+
* and regular script files.
|
|
4
|
+
* @param {import("eslint").Rule.RuleContext} context
|
|
5
|
+
* @param {import("eslint").Rule.RuleListener} templateVisitor
|
|
6
|
+
* @param {import("eslint").Rule.RuleListener} scriptVisitor
|
|
7
|
+
* @returns {import("eslint").Rule.RuleListener} Appropriate visitor based on the file type.
|
|
8
|
+
*/
|
|
9
|
+
export const createRuleVisitors = (context, templateVisitor, scriptVisitor) => {
|
|
10
|
+
const fileName = context.filename;
|
|
11
|
+
|
|
12
|
+
if (fileName.endsWith(".vue")) {
|
|
13
|
+
const { sourceCode } = context;
|
|
14
|
+
const { parserServices } = sourceCode;
|
|
15
|
+
|
|
16
|
+
if (
|
|
17
|
+
typeof parserServices !== "undefined" &&
|
|
18
|
+
"defineTemplateBodyVisitor" in parserServices
|
|
19
|
+
) {
|
|
20
|
+
return parserServices.defineTemplateBodyVisitor(
|
|
21
|
+
templateVisitor,
|
|
22
|
+
scriptVisitor,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return scriptVisitor;
|
|
28
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the expression container for a bound class attribute,
|
|
3
|
+
* or undefined if not applicable.
|
|
4
|
+
* @param {import("vue-eslint-parser").AST.VDirectiveKey} key
|
|
5
|
+
* @param {import("vue-eslint-parser").AST.VLiteral | import("vue-eslint-parser").AST.VExpressionContainer | null} value
|
|
6
|
+
* @returns {import("vue-eslint-parser").AST.VExpressionContainer | undefined} Expression container if valid.
|
|
7
|
+
*/
|
|
8
|
+
export const getBindClassExpression = (key, value) => {
|
|
9
|
+
if (!isBindClassDirective(key)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!value || value.type !== "VExpressionContainer") {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if node is a valid directive with an expression value.
|
|
22
|
+
* @param {import("vue-eslint-parser").AST.VAttribute} node
|
|
23
|
+
* @returns {boolean} `true` if node is a valid directive with an expression value.
|
|
24
|
+
*/
|
|
25
|
+
export const isValidDirective = (node) =>
|
|
26
|
+
Boolean(node.directive && node.value && "expression" in node.value);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if directive is a v-bind:class or :class directive.
|
|
30
|
+
* @param {import("vue-eslint-parser").AST.VDirectiveKey} key
|
|
31
|
+
* @returns {boolean} `true` if directive is a v-bind:class or :class directive.
|
|
32
|
+
*/
|
|
33
|
+
const isBindClassDirective = (key) =>
|
|
34
|
+
key.name.name === "bind" &&
|
|
35
|
+
key.argument?.type === "VIdentifier" &&
|
|
36
|
+
key.argument.name === "class";
|
package/dist/index.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ESLint, Linter } from "eslint";
|
|
2
|
-
type PluginConfigs = {
|
|
3
|
-
recommended: Linter.Config[];
|
|
4
|
-
};
|
|
5
|
-
declare const plugin: ESLint.Plugin;
|
|
6
|
-
export declare const configs: PluginConfigs;
|
|
7
|
-
export { plugin };
|
|
8
|
-
declare const _default: ESLint.Plugin & {
|
|
9
|
-
configs: PluginConfigs;
|
|
10
|
-
};
|
|
11
|
-
export default _default;
|
package/dist/index.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import css from "@eslint/css";
|
|
2
|
-
import { name as packageName, version as packageVersion, } from "../package.json";
|
|
3
|
-
import { rules } from "./rules/index.js";
|
|
4
|
-
const pluginName = "tailwind-variants";
|
|
5
|
-
const plugin = {
|
|
6
|
-
meta: {
|
|
7
|
-
name: packageName,
|
|
8
|
-
version: packageVersion,
|
|
9
|
-
},
|
|
10
|
-
rules,
|
|
11
|
-
};
|
|
12
|
-
export const configs = {
|
|
13
|
-
recommended: [
|
|
14
|
-
{
|
|
15
|
-
plugins: {
|
|
16
|
-
[pluginName]: plugin,
|
|
17
|
-
},
|
|
18
|
-
rules: {
|
|
19
|
-
[`${pluginName}/limited-inline-classes`]: "error",
|
|
20
|
-
[`${pluginName}/require-variants-call-styles-name`]: "error",
|
|
21
|
-
[`${pluginName}/require-variants-suffix`]: "error",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
files: ["**/*.css"],
|
|
26
|
-
language: "css/css",
|
|
27
|
-
plugins: { css },
|
|
28
|
-
rules: {
|
|
29
|
-
[`${pluginName}/sort-custom-properties`]: [
|
|
30
|
-
"error",
|
|
31
|
-
{
|
|
32
|
-
emptyLineBetweenGroups: true,
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
};
|
|
39
|
-
export { plugin };
|
|
40
|
-
export default {
|
|
41
|
-
...plugin,
|
|
42
|
-
configs,
|
|
43
|
-
};
|
package/dist/rules/index.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
export declare const MESSAGE_IDS: {
|
|
3
|
-
readonly limitedInlineClasses: "limitedInlineClasses";
|
|
4
|
-
readonly noCnInClassName: "noCnInClassName";
|
|
5
|
-
};
|
|
6
|
-
export type MessageIds = (typeof MESSAGE_IDS)[keyof typeof MESSAGE_IDS];
|
|
7
|
-
export type Options = [
|
|
8
|
-
{
|
|
9
|
-
/**
|
|
10
|
-
* Directory pattern to match
|
|
11
|
-
* @default "/components/"
|
|
12
|
-
*/
|
|
13
|
-
directoryPattern?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Maximum number of inline classes allowed
|
|
16
|
-
* @default 5
|
|
17
|
-
*/
|
|
18
|
-
maxInlineClasses?: number;
|
|
19
|
-
}
|
|
20
|
-
];
|
|
21
|
-
export declare const rule: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
|
|
22
|
-
name: string;
|
|
23
|
-
};
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES, ESLintUtils, } from "@typescript-eslint/utils";
|
|
2
|
-
import { createRuleVisitors } from "../utils/create-rule-visitors";
|
|
3
|
-
import { getBindClassExpression } from "../utils/get-bind-class-expression";
|
|
4
|
-
const createRule = ESLintUtils.RuleCreator((name) => name);
|
|
5
|
-
export const MESSAGE_IDS = {
|
|
6
|
-
limitedInlineClasses: "limitedInlineClasses",
|
|
7
|
-
noCnInClassName: "noCnInClassName",
|
|
8
|
-
};
|
|
9
|
-
function countClasses(value) {
|
|
10
|
-
return value.trim().split(/\s+/).filter(Boolean).length;
|
|
11
|
-
}
|
|
12
|
-
/** Recursively validate classes in any expression and detect cn() calls */
|
|
13
|
-
function validateExpression(node, expr, context, maxInlineClasses = 5) {
|
|
14
|
-
if (!expr)
|
|
15
|
-
return false;
|
|
16
|
-
switch (expr.type) {
|
|
17
|
-
case AST_NODE_TYPES.ArrayExpression:
|
|
18
|
-
return expr.elements
|
|
19
|
-
.filter((el) => {
|
|
20
|
-
return el !== null && el.type !== AST_NODE_TYPES.SpreadElement;
|
|
21
|
-
})
|
|
22
|
-
.some((el) => validateExpression(node, el, context, maxInlineClasses));
|
|
23
|
-
case AST_NODE_TYPES.BinaryExpression:
|
|
24
|
-
return (validateExpression(node, expr.left, context, maxInlineClasses) ||
|
|
25
|
-
validateExpression(node, expr.right, context, maxInlineClasses));
|
|
26
|
-
case AST_NODE_TYPES.CallExpression:
|
|
27
|
-
if (expr.callee.type === AST_NODE_TYPES.Identifier &&
|
|
28
|
-
expr.callee.name === "cn") {
|
|
29
|
-
context.report({
|
|
30
|
-
messageId: MESSAGE_IDS.noCnInClassName,
|
|
31
|
-
node: node,
|
|
32
|
-
});
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
return expr.arguments
|
|
36
|
-
.filter((arg) => {
|
|
37
|
-
return arg.type !== AST_NODE_TYPES.SpreadElement;
|
|
38
|
-
})
|
|
39
|
-
.some((arg) => validateExpression(node, arg, context, maxInlineClasses));
|
|
40
|
-
case AST_NODE_TYPES.ConditionalExpression:
|
|
41
|
-
return (validateExpression(node, expr.consequent, context, maxInlineClasses) ||
|
|
42
|
-
validateExpression(node, expr.alternate, context, maxInlineClasses));
|
|
43
|
-
case AST_NODE_TYPES.Identifier:
|
|
44
|
-
return false;
|
|
45
|
-
case AST_NODE_TYPES.Literal:
|
|
46
|
-
if (typeof expr.value === "string") {
|
|
47
|
-
if (countClasses(expr.value) > maxInlineClasses) {
|
|
48
|
-
context.report({
|
|
49
|
-
data: { max: maxInlineClasses.toString() },
|
|
50
|
-
messageId: MESSAGE_IDS.limitedInlineClasses,
|
|
51
|
-
node: node,
|
|
52
|
-
});
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
case AST_NODE_TYPES.LogicalExpression:
|
|
58
|
-
return (validateExpression(node, expr.left, context, maxInlineClasses) ||
|
|
59
|
-
validateExpression(node, expr.right, context, maxInlineClasses));
|
|
60
|
-
case AST_NODE_TYPES.ObjectExpression:
|
|
61
|
-
return expr.properties.some((prop) => {
|
|
62
|
-
if (prop.type === AST_NODE_TYPES.Property) {
|
|
63
|
-
return validateExpression(node, prop.value &&
|
|
64
|
-
prop.value.type !== "ObjectPattern" &&
|
|
65
|
-
prop.value.type !== "ArrayPattern"
|
|
66
|
-
? prop.value
|
|
67
|
-
: null, context, maxInlineClasses);
|
|
68
|
-
}
|
|
69
|
-
// Ignore SpreadElement and other non-Property types
|
|
70
|
-
return false;
|
|
71
|
-
});
|
|
72
|
-
case AST_NODE_TYPES.TemplateLiteral:
|
|
73
|
-
// Static template literal
|
|
74
|
-
if (expr.expressions.length === 0) {
|
|
75
|
-
const raw = expr.quasis[0]?.value.cooked ?? "";
|
|
76
|
-
if (countClasses(raw) > maxInlineClasses) {
|
|
77
|
-
context.report({
|
|
78
|
-
data: { max: maxInlineClasses.toString() },
|
|
79
|
-
messageId: MESSAGE_IDS.limitedInlineClasses,
|
|
80
|
-
node: node,
|
|
81
|
-
});
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// Recurse into expressions
|
|
86
|
-
return expr.expressions.some((el) => validateExpression(node, el, context, maxInlineClasses));
|
|
87
|
-
case AST_NODE_TYPES.ThisExpression:
|
|
88
|
-
return false;
|
|
89
|
-
default:
|
|
90
|
-
console.log("Unhandled expression type:", expr.type);
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
export const rule = createRule({
|
|
95
|
-
name: "limited-inline-classes",
|
|
96
|
-
meta: {
|
|
97
|
-
docs: {
|
|
98
|
-
description: `Allow a configurable number of inline class names; require use of tailwind-variants.`,
|
|
99
|
-
},
|
|
100
|
-
messages: {
|
|
101
|
-
limitedInlineClasses: `Inline className may contain at most {{max}} class. Use tailwind-variants instead.`,
|
|
102
|
-
noCnInClassName: "Using cn() in className is not allowed in component definition. Use tailwind-variants instead.",
|
|
103
|
-
},
|
|
104
|
-
schema: [
|
|
105
|
-
{
|
|
106
|
-
additionalProperties: false,
|
|
107
|
-
properties: {
|
|
108
|
-
directoryPattern: {
|
|
109
|
-
default: "/components/",
|
|
110
|
-
description: 'Directory pattern to match, e.g., "/components/".',
|
|
111
|
-
type: "string",
|
|
112
|
-
},
|
|
113
|
-
maxInlineClasses: {
|
|
114
|
-
default: 5,
|
|
115
|
-
description: "Maximum number of inline classes allowed (default: 5).",
|
|
116
|
-
minimum: 1,
|
|
117
|
-
type: "number",
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
type: "object",
|
|
121
|
-
},
|
|
122
|
-
],
|
|
123
|
-
type: "problem",
|
|
124
|
-
},
|
|
125
|
-
defaultOptions: [
|
|
126
|
-
{
|
|
127
|
-
directoryPattern: "/components/",
|
|
128
|
-
maxInlineClasses: 5,
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
create: (context) => {
|
|
132
|
-
const options = context.options[0] || {};
|
|
133
|
-
const directoryPattern = options.directoryPattern || "/components/";
|
|
134
|
-
const maxInlineClasses = options.maxInlineClasses ?? 5;
|
|
135
|
-
const fileName = context.filename;
|
|
136
|
-
if (!fileName.replace(/\\/g, "/").includes(directoryPattern)) {
|
|
137
|
-
return {};
|
|
138
|
-
}
|
|
139
|
-
// Script visitors (for JSX in Vue <script> or React files)
|
|
140
|
-
const scriptVisitor = {
|
|
141
|
-
JSXAttribute(node) {
|
|
142
|
-
const jsxAttr = node;
|
|
143
|
-
if (jsxAttr.name.name !== "className")
|
|
144
|
-
return;
|
|
145
|
-
const value = jsxAttr.value;
|
|
146
|
-
if (!value)
|
|
147
|
-
return;
|
|
148
|
-
// className="..."
|
|
149
|
-
if (value.type === AST_NODE_TYPES.Literal &&
|
|
150
|
-
typeof value.value === "string") {
|
|
151
|
-
if (countClasses(value.value) > maxInlineClasses) {
|
|
152
|
-
context.report({
|
|
153
|
-
data: { max: maxInlineClasses.toString() },
|
|
154
|
-
messageId: MESSAGE_IDS.limitedInlineClasses,
|
|
155
|
-
node: jsxAttr,
|
|
156
|
-
});
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// className={`...`} / className={"..."}
|
|
161
|
-
if (value.type === AST_NODE_TYPES.JSXExpressionContainer) {
|
|
162
|
-
const expr = value.expression;
|
|
163
|
-
if (expr.type !== AST_NODE_TYPES.JSXEmptyExpression) {
|
|
164
|
-
validateExpression(jsxAttr, expr, context, maxInlineClasses);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
// Template visitors (for Vue <template>)
|
|
170
|
-
const templateVisitor = {
|
|
171
|
-
VAttribute(node) {
|
|
172
|
-
const vAttr = node;
|
|
173
|
-
if (!vAttr.value)
|
|
174
|
-
return;
|
|
175
|
-
// class="..."
|
|
176
|
-
if (!vAttr.directive &&
|
|
177
|
-
vAttr.key.type === "VIdentifier" &&
|
|
178
|
-
vAttr.key.name === "class") {
|
|
179
|
-
if (countClasses(vAttr.value.value) > maxInlineClasses) {
|
|
180
|
-
context.report({
|
|
181
|
-
data: { max: maxInlineClasses.toString() },
|
|
182
|
-
messageId: MESSAGE_IDS.limitedInlineClasses,
|
|
183
|
-
node: vAttr,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
const container = getBindClassExpression(vAttr);
|
|
188
|
-
if (!container)
|
|
189
|
-
return;
|
|
190
|
-
// :class="..." / v-bind:class
|
|
191
|
-
if (container.expression) {
|
|
192
|
-
validateExpression(vAttr, container.expression, context, maxInlineClasses);
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
return createRuleVisitors(context, templateVisitor, scriptVisitor);
|
|
197
|
-
},
|
|
198
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
export declare const MESSAGE_IDS: {
|
|
3
|
-
readonly requireVariantsCallStylesName: "requireVariantsCallStylesName";
|
|
4
|
-
};
|
|
5
|
-
export type MessageIds = (typeof MESSAGE_IDS)[keyof typeof MESSAGE_IDS];
|
|
6
|
-
export type Options = [
|
|
7
|
-
{
|
|
8
|
-
/**
|
|
9
|
-
* Name required for variables assigned from tv()
|
|
10
|
-
* @default "styles"
|
|
11
|
-
*/
|
|
12
|
-
name?: string;
|
|
13
|
-
}
|
|
14
|
-
];
|
|
15
|
-
export declare const rule: ESLintUtils.RuleModule<"requireVariantsCallStylesName", Options, unknown, ESLintUtils.RuleListener> & {
|
|
16
|
-
name: string;
|
|
17
|
-
};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
const createRule = ESLintUtils.RuleCreator((name) => name);
|
|
3
|
-
export const MESSAGE_IDS = {
|
|
4
|
-
requireVariantsCallStylesName: "requireVariantsCallStylesName",
|
|
5
|
-
};
|
|
6
|
-
export const rule = createRule({
|
|
7
|
-
name: "require-variants-call-styles-name",
|
|
8
|
-
meta: {
|
|
9
|
-
docs: {
|
|
10
|
-
description: "Require variables assigned from calling a function returned by tv() to be named {{name}}.",
|
|
11
|
-
},
|
|
12
|
-
fixable: "code",
|
|
13
|
-
messages: {
|
|
14
|
-
requireVariantsCallStylesName: "Require variables assigned from calling a function returned by tv() to be named {{name}}.",
|
|
15
|
-
},
|
|
16
|
-
schema: [
|
|
17
|
-
{
|
|
18
|
-
additionalProperties: false,
|
|
19
|
-
properties: {
|
|
20
|
-
name: {
|
|
21
|
-
default: "styles",
|
|
22
|
-
description: "Name required for variables assigned from tv().",
|
|
23
|
-
type: "string",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
type: "object",
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
type: "suggestion",
|
|
30
|
-
},
|
|
31
|
-
defaultOptions: [
|
|
32
|
-
{
|
|
33
|
-
name: "styles",
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
create: (context) => {
|
|
37
|
-
const options = context.options[0] || {};
|
|
38
|
-
const requiredName = options.name || "styles";
|
|
39
|
-
const variantFunctions = new Set();
|
|
40
|
-
return {
|
|
41
|
-
VariableDeclarator(node) {
|
|
42
|
-
const init = node.init;
|
|
43
|
-
const id = node.id;
|
|
44
|
-
if (!init)
|
|
45
|
-
return;
|
|
46
|
-
if (init.type !== "CallExpression")
|
|
47
|
-
return;
|
|
48
|
-
if (init.callee.type !== "Identifier")
|
|
49
|
-
return;
|
|
50
|
-
if (id.type !== "Identifier")
|
|
51
|
-
return;
|
|
52
|
-
// Track variant functions created by tv()
|
|
53
|
-
if (init.callee.name === "tv") {
|
|
54
|
-
variantFunctions.add(id.name);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (variantFunctions.has(init.callee.name)) {
|
|
58
|
-
const variableName = id.name;
|
|
59
|
-
const functionName = init.callee.name;
|
|
60
|
-
if (variableName === requiredName)
|
|
61
|
-
return;
|
|
62
|
-
context.report({
|
|
63
|
-
data: {
|
|
64
|
-
functionName,
|
|
65
|
-
name: requiredName,
|
|
66
|
-
},
|
|
67
|
-
fix: (fixer) => fixer.replaceText(id, requiredName),
|
|
68
|
-
messageId: MESSAGE_IDS.requireVariantsCallStylesName,
|
|
69
|
-
node: id,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
},
|
|
75
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
export declare const MESSAGE_IDS: {
|
|
3
|
-
readonly requireVariantsSuffix: "requireVariantsSuffix";
|
|
4
|
-
};
|
|
5
|
-
export type MessageIds = (typeof MESSAGE_IDS)[keyof typeof MESSAGE_IDS];
|
|
6
|
-
export type Options = [
|
|
7
|
-
{
|
|
8
|
-
/**
|
|
9
|
-
* Suffix required for variables assigned from tv()
|
|
10
|
-
* @default "Variants"
|
|
11
|
-
*/
|
|
12
|
-
suffix?: string;
|
|
13
|
-
}
|
|
14
|
-
];
|
|
15
|
-
export declare const rule: ESLintUtils.RuleModule<"requireVariantsSuffix", Options, unknown, ESLintUtils.RuleListener> & {
|
|
16
|
-
name: string;
|
|
17
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
const createRule = ESLintUtils.RuleCreator((name) => name);
|
|
3
|
-
export const MESSAGE_IDS = {
|
|
4
|
-
requireVariantsSuffix: "requireVariantsSuffix",
|
|
5
|
-
};
|
|
6
|
-
export const rule = createRule({
|
|
7
|
-
name: "require-variants-suffix",
|
|
8
|
-
meta: {
|
|
9
|
-
docs: {
|
|
10
|
-
description: "Require variables assigned from tv() to end with {{suffix}}.",
|
|
11
|
-
},
|
|
12
|
-
fixable: "code",
|
|
13
|
-
messages: {
|
|
14
|
-
requireVariantsSuffix: "Variable assigned from tv() must end with '{{suffix}}'.",
|
|
15
|
-
},
|
|
16
|
-
schema: [
|
|
17
|
-
{
|
|
18
|
-
additionalProperties: false,
|
|
19
|
-
properties: {
|
|
20
|
-
suffix: {
|
|
21
|
-
default: "Variants",
|
|
22
|
-
description: "Suffix required for variables assigned from tv().",
|
|
23
|
-
type: "string",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
type: "object",
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
type: "suggestion",
|
|
30
|
-
},
|
|
31
|
-
defaultOptions: [
|
|
32
|
-
{
|
|
33
|
-
suffix: "Variants",
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
create: (context) => {
|
|
37
|
-
const options = context.options[0] || {};
|
|
38
|
-
const suffix = options.suffix || "Variants";
|
|
39
|
-
return {
|
|
40
|
-
VariableDeclarator(node) {
|
|
41
|
-
const init = node.init;
|
|
42
|
-
if (!init)
|
|
43
|
-
return;
|
|
44
|
-
if (init.type !== "CallExpression")
|
|
45
|
-
return;
|
|
46
|
-
if (init.callee.type !== "Identifier")
|
|
47
|
-
return;
|
|
48
|
-
if (init.callee.name !== "tv")
|
|
49
|
-
return;
|
|
50
|
-
const { id } = node;
|
|
51
|
-
if (id.type !== "Identifier")
|
|
52
|
-
return;
|
|
53
|
-
if (id.name.endsWith(suffix))
|
|
54
|
-
return;
|
|
55
|
-
context.report({
|
|
56
|
-
data: { suffix },
|
|
57
|
-
fix: (fixer) => {
|
|
58
|
-
return fixer.insertTextAfter(id, suffix);
|
|
59
|
-
},
|
|
60
|
-
messageId: MESSAGE_IDS.requireVariantsSuffix,
|
|
61
|
-
node: id,
|
|
62
|
-
});
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
},
|
|
66
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
-
export declare const MESSAGE_IDS: {
|
|
3
|
-
readonly missingEmptyLineBetweenGroups: "missingEmptyLineBetweenGroups";
|
|
4
|
-
readonly patternTooLong: "patternTooLong";
|
|
5
|
-
readonly unsortedCustomProperties: "unsortedCustomProperties";
|
|
6
|
-
};
|
|
7
|
-
export type MessageIds = (typeof MESSAGE_IDS)[keyof typeof MESSAGE_IDS];
|
|
8
|
-
export type Options = [
|
|
9
|
-
{
|
|
10
|
-
/**
|
|
11
|
-
* Add empty line between different prefix groups
|
|
12
|
-
* @default false
|
|
13
|
-
*/
|
|
14
|
-
emptyLineBetweenGroups?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Order of patterns (RegExp strings) for custom properties.
|
|
17
|
-
* Properties matching the first pattern appear first.
|
|
18
|
-
* @default [
|
|
19
|
-
* "^--spacing-",
|
|
20
|
-
* "^--size-",
|
|
21
|
-
* "^--font-",
|
|
22
|
-
* "^--weight-",
|
|
23
|
-
* "^--leading-",
|
|
24
|
-
* "^--tracking-",
|
|
25
|
-
* "^--radius-",
|
|
26
|
-
* "^--shadow-",
|
|
27
|
-
* "^--animate-",
|
|
28
|
-
* "^--transition-",
|
|
29
|
-
* "^--color-",
|
|
30
|
-
* ]
|
|
31
|
-
*/
|
|
32
|
-
order?: string[];
|
|
33
|
-
}
|
|
34
|
-
];
|
|
35
|
-
export declare const rule: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
|
|
36
|
-
name: string;
|
|
37
|
-
};
|