@dialpad/eslint-plugin-dialtone 1.11.0 → 1.11.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.
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Recommends using
|
|
2
|
+
* @fileoverview Recommends using props instead of CSS utilities on Stack component
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
//------------------------------------------------------------------------------
|
|
7
|
+
// Constants
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Gap utility classes that have DtStack equivalents.
|
|
12
|
+
* Maps utility class suffix to DtStack gap prop value.
|
|
13
|
+
* Gaps > 64px have no equivalent and should not be flagged.
|
|
14
|
+
*/
|
|
15
|
+
const GAP_WITH_EQUIVALENTS = {
|
|
16
|
+
'0': '0',
|
|
17
|
+
'8': '400',
|
|
18
|
+
'16': '500',
|
|
19
|
+
'24': '550',
|
|
20
|
+
'32': '600',
|
|
21
|
+
'48': '650',
|
|
22
|
+
'64': '700',
|
|
23
|
+
};
|
|
5
24
|
|
|
6
25
|
//------------------------------------------------------------------------------
|
|
7
26
|
// Rule Definition
|
|
@@ -11,7 +30,7 @@ module.exports = {
|
|
|
11
30
|
meta: {
|
|
12
31
|
type: 'suggestion',
|
|
13
32
|
docs: {
|
|
14
|
-
description:
|
|
33
|
+
description: 'Recommend using props instead of CSS utilities on Stack component',
|
|
15
34
|
recommended: false,
|
|
16
35
|
url: 'https://github.com/dialpad/dialtone/blob/staging/packages/eslint-plugin-dialtone/docs/rules/deprecated-stack-alignment-classes.md',
|
|
17
36
|
},
|
|
@@ -20,19 +39,23 @@ module.exports = {
|
|
|
20
39
|
messages: {
|
|
21
40
|
useAlignProp: 'Use the `align` prop instead of `d-ai-*` utility classes on <dt-stack>. See: https://dialtone.dialpad.com/components/stack.html#align',
|
|
22
41
|
useJustifyProp: 'Use the `justify` prop instead of `d-jc-*` utility classes on <dt-stack>. See: https://dialtone.dialpad.com/components/stack.html#justify',
|
|
42
|
+
useDirectionProp: 'Use the `direction` prop instead of `d-fd-*` utility classes on <dt-stack>. See: https://dialtone.dialpad.com/components/stack.html#direction',
|
|
43
|
+
useGapProp: 'Use the `gap` prop instead of `d-g*` utility classes on <dt-stack>. See: https://dialtone.dialpad.com/components/stack.html#gap',
|
|
44
|
+
removeRedundantFlex: 'Remove `d-d-flex` from <dt-stack> - it is already a flex container.',
|
|
23
45
|
},
|
|
24
46
|
},
|
|
25
47
|
|
|
26
48
|
create(context) {
|
|
27
49
|
const sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
28
50
|
return sourceCode.parserServices.defineTemplateBodyVisitor({
|
|
51
|
+
|
|
29
52
|
VElement(node) {
|
|
30
53
|
// Check if element is dt-stack or DtStack
|
|
31
54
|
const elementName = node.name || node.rawName;
|
|
32
55
|
if (elementName === 'dt-stack' || elementName === 'DtStack') {
|
|
33
56
|
// Find class attribute
|
|
34
57
|
const classAttr = node.startTag.attributes.find(
|
|
35
|
-
attr => attr.key && attr.key.name === 'class'
|
|
58
|
+
attr => attr.key && attr.key.name === 'class',
|
|
36
59
|
);
|
|
37
60
|
|
|
38
61
|
if (classAttr && classAttr.value && classAttr.value.value) {
|
|
@@ -53,9 +76,35 @@ module.exports = {
|
|
|
53
76
|
messageId: 'useJustifyProp',
|
|
54
77
|
});
|
|
55
78
|
}
|
|
79
|
+
|
|
80
|
+
// Check for d-fd-* classes (flex-direction utilities)
|
|
81
|
+
if (/d-fd-(row|column|row-reverse|column-reverse)/.test(classes)) {
|
|
82
|
+
context.report({
|
|
83
|
+
node: classAttr,
|
|
84
|
+
messageId: 'useDirectionProp',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check for d-g* and d-gg* classes (gap utilities) - only those with DtStack equivalents
|
|
89
|
+
// d-gg* uses deprecated grid-gap property but works the same as d-g*
|
|
90
|
+
const gapMatch = classes.match(/\bd-gg?(\d+)\b/);
|
|
91
|
+
if (gapMatch && GAP_WITH_EQUIVALENTS[gapMatch[1]]) {
|
|
92
|
+
context.report({
|
|
93
|
+
node: classAttr,
|
|
94
|
+
messageId: 'useGapProp',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check for d-d-flex (redundant on DtStack)
|
|
99
|
+
if (/\bd-d-flex\b/.test(classes)) {
|
|
100
|
+
context.report({
|
|
101
|
+
node: classAttr,
|
|
102
|
+
messageId: 'removeRedundantFlex',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
56
105
|
}
|
|
57
106
|
}
|
|
58
|
-
}
|
|
107
|
+
},
|
|
59
108
|
});
|
|
60
|
-
}
|
|
109
|
+
},
|
|
61
110
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prefer DtStack component over flex utility classes
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
//------------------------------------------------------------------------------
|
|
7
|
+
// Rule Definition
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
11
|
+
module.exports = {
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'suggestion',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Prefer DtStack component over flex utility classes',
|
|
16
|
+
recommended: false,
|
|
17
|
+
url: 'https://github.com/dialpad/dialtone/blob/staging/packages/eslint-plugin-dialtone/docs/rules/prefer-stack-over-flex.md',
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [],
|
|
21
|
+
messages: {
|
|
22
|
+
preferStack: 'Consider using `<dt-stack>` instead of `d-d-flex`. See: https://dialtone.dialpad.com/components/stack.html',
|
|
23
|
+
dynamicFlexBinding: 'Flex utilities detected in dynamic `:class` binding. Consider using `<dt-stack>` with conditional props instead. Manual migration required.',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
create(context) {
|
|
28
|
+
const sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
29
|
+
return sourceCode.parserServices.defineTemplateBodyVisitor({
|
|
30
|
+
VElement(node) {
|
|
31
|
+
// Skip if already dt-stack or DtStack
|
|
32
|
+
const elementName = node.name || node.rawName;
|
|
33
|
+
if (elementName === 'dt-stack' || elementName === 'DtStack') return;
|
|
34
|
+
|
|
35
|
+
// Find class attribute
|
|
36
|
+
const classAttr = node.startTag.attributes.find(
|
|
37
|
+
attr => attr.key && attr.key.name === 'class',
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (classAttr && classAttr.value && classAttr.value.value) {
|
|
41
|
+
const classes = classAttr.value.value;
|
|
42
|
+
|
|
43
|
+
// Flag any element with d-d-flex (no exclusions)
|
|
44
|
+
if (/\bd-d-flex\b/.test(classes)) {
|
|
45
|
+
context.report({
|
|
46
|
+
node: classAttr,
|
|
47
|
+
messageId: 'preferStack',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
VAttribute(node) {
|
|
54
|
+
// Check for :class or v-bind:class directives
|
|
55
|
+
if (node.directive &&
|
|
56
|
+
node.key.name.name === 'bind' &&
|
|
57
|
+
node.key.argument?.name === 'class') {
|
|
58
|
+
|
|
59
|
+
// Get the raw source of the binding expression
|
|
60
|
+
const bindingText = sourceCode.getText(node.value);
|
|
61
|
+
|
|
62
|
+
// Check if it contains flex utilities (as string literals)
|
|
63
|
+
// Look for patterns like 'd-d-flex', 'd-ai-', 'd-jc-', 'd-fd-', 'd-g\d', 'd-gg\d'
|
|
64
|
+
if (/['"]d-d-flex['"]|['"]d-ai-|['"]d-jc-|['"]d-fd-|['"]d-gg?\d/.test(bindingText)) {
|
|
65
|
+
context.report({
|
|
66
|
+
node: node,
|
|
67
|
+
messageId: 'dynamicFlexBinding',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview
|
|
2
|
+
* @fileoverview Combining multiple typography utility categories is discouraged in favor of composed typography utilities
|
|
3
3
|
* @author Nina Repetto
|
|
4
4
|
*/
|
|
5
5
|
'use strict';
|
|
@@ -8,21 +8,30 @@
|
|
|
8
8
|
// Rule Definition
|
|
9
9
|
// ------------------------------------------------------------------------------
|
|
10
10
|
|
|
11
|
+
const typographyCategories = {
|
|
12
|
+
'font-weight': ['d-fw-normal', 'd-fw-medium', 'd-fw-semibold', 'd-fw-bold'],
|
|
13
|
+
'font-size': ['d-fs'], // prefix match
|
|
14
|
+
'line-height': ['d-lh'], // prefix match
|
|
15
|
+
'font-family': ['d-ff-custom', 'd-ff-sans', 'd-ff-mono', 'd-ff-marketing', 'd-ff-unset'],
|
|
16
|
+
};
|
|
17
|
+
|
|
11
18
|
module.exports = {
|
|
12
19
|
meta: {
|
|
13
20
|
type: 'suggestion', // `problem`, `suggestion`, or `layout`
|
|
14
21
|
docs: {
|
|
15
|
-
description: '
|
|
22
|
+
description: 'Combining multiple typography utility categories is discouraged in favor of composed typography utilities',
|
|
16
23
|
recommended: false,
|
|
17
24
|
url: 'https://github.com/dialpad/dialtone/blob/staging/packages/eslint-plugin-dialtone/docs/rules/recommend-typography-style.md', // URL to the documentation page for this rule
|
|
18
25
|
},
|
|
19
26
|
fixable: null, // Or `code` or `whitespace`
|
|
20
27
|
schema: [], // Add a schema if the rule has options
|
|
21
28
|
messages: {
|
|
22
|
-
recommendTypographyStyle: `
|
|
23
|
-
discouraged in favor of composed typography utilities.
|
|
29
|
+
recommendTypographyStyle: `Combining multiple typography utility categories (Font family, Font weight, Font size, Line height) is
|
|
30
|
+
discouraged in favor of composed typography utilities. Check out the available classes here:
|
|
24
31
|
https://dialtone.dialpad.com/design/typography/#api. There can be cases where using these utilities is intentional and valid,
|
|
25
32
|
in which case you can ignore this lint warning.`,
|
|
33
|
+
conflictingTypographyUtilities: `Conflicting typography utilities detected: multiple {{category}} classes found ({{classes}}).
|
|
34
|
+
Only one will be applied. Remove the conflicting classes.`,
|
|
26
35
|
}, // Add messageId and message
|
|
27
36
|
},
|
|
28
37
|
|
|
@@ -33,28 +42,43 @@ module.exports = {
|
|
|
33
42
|
VAttribute (node) {
|
|
34
43
|
if (node.key.name === 'class') {
|
|
35
44
|
const classes = node.value.value.split(' ');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if
|
|
45
|
+
|
|
46
|
+
// For each class, determine which category it belongs to and track all matches
|
|
47
|
+
const categoryCounts = {};
|
|
48
|
+
classes.forEach((className) => {
|
|
49
|
+
for (const [category, patterns] of Object.entries(typographyCategories)) {
|
|
50
|
+
if (patterns.some((pattern) => className.startsWith(pattern))) {
|
|
51
|
+
if (!categoryCounts[category]) {
|
|
52
|
+
categoryCounts[category] = [];
|
|
53
|
+
}
|
|
54
|
+
categoryCounts[category].push(className);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const categoriesFound = Object.keys(categoryCounts);
|
|
60
|
+
|
|
61
|
+
// Report if 2+ different categories are present
|
|
62
|
+
if (categoriesFound.length >= 2) {
|
|
53
63
|
context.report({
|
|
54
64
|
node,
|
|
55
65
|
messageId: 'recommendTypographyStyle',
|
|
56
66
|
});
|
|
57
67
|
}
|
|
68
|
+
|
|
69
|
+
// Report conflicting utilities within the same category
|
|
70
|
+
for (const [category, matchedClasses] of Object.entries(categoryCounts)) {
|
|
71
|
+
if (matchedClasses.length >= 2) {
|
|
72
|
+
context.report({
|
|
73
|
+
node,
|
|
74
|
+
messageId: 'conflictingTypographyUtilities',
|
|
75
|
+
data: {
|
|
76
|
+
category,
|
|
77
|
+
classes: matchedClasses.join(', '),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
58
82
|
}
|
|
59
83
|
},
|
|
60
84
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dialpad/eslint-plugin-dialtone",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "dialtone eslint plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Dialpad",
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"email": "dialtone@dialpad.com"
|
|
36
36
|
},
|
|
37
37
|
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/dialpad/dialtone.git",
|
|
41
|
+
"directory": "packages/eslint-plugin-dialtone"
|
|
42
|
+
},
|
|
38
43
|
"main": "./lib/index.js",
|
|
39
44
|
"files": [
|
|
40
45
|
"lib"
|