@gitlab/eslint-plugin 20.2.0 → 20.3.0
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/CHANGELOG.md +15 -0
- package/docs/rules/tailwind.md +6 -0
- package/docs/rules/vue-tailwind.md +10 -0
- package/lib/configs/typescript.js +6 -0
- package/lib/rules/tailwind.js +7 -1
- package/lib/rules/vue-tailwind.js +14 -5
- package/lib/utils/tailwind-utils.js +33 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [20.3.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v20.2.1...v20.3.0) (2024-10-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **tailwind:** add check for max-width CSS utils ([ec8026a](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/ec8026a4bf1961b90808cdfc7984640ab16fdc40))
|
|
7
|
+
|
|
8
|
+
## [20.2.1](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v20.2.0...v20.2.1) (2024-09-06)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **tailwind:** catch forward slashes in CSS utils ([42c2133](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/42c2133fb8a0ca677512f0dc4bd555e5fdb51323))
|
|
14
|
+
* **tailwind:** only match the beginning of a string or after a space ([735a6ec](https://gitlab.com/gitlab-org/frontend/eslint-plugin/commit/735a6ec69d62424a8eb4e764375e9318e0a29428))
|
|
15
|
+
|
|
1
16
|
# [20.2.0](https://gitlab.com/gitlab-org/frontend/eslint-plugin/compare/v20.1.0...v20.2.0) (2024-09-03)
|
|
2
17
|
|
|
3
18
|
|
package/docs/rules/tailwind.md
CHANGED
|
@@ -13,6 +13,9 @@ const cssUtilsStringLiteral = 'gl-bg-red-' + variant;
|
|
|
13
13
|
|
|
14
14
|
// Using interpolation to build a utility in a template literal
|
|
15
15
|
const cssUtilsTemplateLiteral = `gl-border-top gl-bg-red-${color} gl-text-gray-400`;
|
|
16
|
+
|
|
17
|
+
// Using max-width media query utility classes
|
|
18
|
+
const cssClasses = ['gl-mt-5', 'max-md:gl-mt-3'];
|
|
16
19
|
```
|
|
17
20
|
|
|
18
21
|
### Examples of **correct** code for this rule
|
|
@@ -23,6 +26,9 @@ const cssUtilsStringLiteral = 'gl-bg-red-800';
|
|
|
23
26
|
|
|
24
27
|
// Utilities declared as a template literal without interpolation
|
|
25
28
|
const cssUtils = `gl-border-top gl-bg-red-800 gl-text-gray-400`;
|
|
29
|
+
|
|
30
|
+
// Using max-width media query utility classes
|
|
31
|
+
const cssClasses = ['gl-mt-3', 'md:gl-mt-5'];
|
|
26
32
|
```
|
|
27
33
|
|
|
28
34
|
## Options
|
|
@@ -16,6 +16,11 @@ This rule enforces the same conventions as [tailwind](./tailwind.md) in Vue temp
|
|
|
16
16
|
<template>
|
|
17
17
|
<span :class="`gl-border-top gl-bg-red-${color} gl-text-gray-400`"></span>
|
|
18
18
|
</template>
|
|
19
|
+
|
|
20
|
+
<!-- Using max-width media query utility classes -->
|
|
21
|
+
<template>
|
|
22
|
+
<span class="gl-mt-5 max-md:gl-mt-3"></span>
|
|
23
|
+
</template>
|
|
19
24
|
```
|
|
20
25
|
|
|
21
26
|
### Examples of **correct** code for this rule
|
|
@@ -30,6 +35,11 @@ This rule enforces the same conventions as [tailwind](./tailwind.md) in Vue temp
|
|
|
30
35
|
<template>
|
|
31
36
|
<span :class="`gl-bg-red-800`"></span>
|
|
32
37
|
</template>
|
|
38
|
+
|
|
39
|
+
<!-- Using max-width media query utility classes -->
|
|
40
|
+
<template>
|
|
41
|
+
<span class="gl-mt-3 md:gl-mt-5"></span>
|
|
42
|
+
</template>
|
|
33
43
|
```
|
|
34
44
|
|
|
35
45
|
## Options
|
|
@@ -48,6 +48,12 @@ const tsConfig = {
|
|
|
48
48
|
'@typescript-eslint/no-shadow': 'error',
|
|
49
49
|
'@typescript-eslint/return-await': 'error',
|
|
50
50
|
'@typescript-eslint/no-floating-promises': 'error',
|
|
51
|
+
'@typescript-eslint/explicit-member-accessibility': [
|
|
52
|
+
'error',
|
|
53
|
+
{
|
|
54
|
+
accessibility: 'no-public',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
51
57
|
'class-methods-use-this': 'off',
|
|
52
58
|
'import/no-cycle': 'error',
|
|
53
59
|
'promise/param-names': 'off',
|
package/lib/rules/tailwind.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Requirements
|
|
3
3
|
// ------------------------------------------------------------------------------
|
|
4
4
|
const { DOCS_BASE_URL } = require('../constants');
|
|
5
|
-
const { validateInterpolatedUtils } = require('../utils/tailwind-utils');
|
|
5
|
+
const { validateInterpolatedUtils, validateMaxWidthUtils } = require('../utils/tailwind-utils');
|
|
6
6
|
|
|
7
7
|
// ------------------------------------------------------------------------------
|
|
8
8
|
// Rule Definition
|
|
@@ -25,6 +25,12 @@ module.exports = {
|
|
|
25
25
|
TemplateLiteral(node) {
|
|
26
26
|
validateInterpolatedUtils(context, node);
|
|
27
27
|
},
|
|
28
|
+
TemplateElement(node) {
|
|
29
|
+
validateMaxWidthUtils(context, node);
|
|
30
|
+
},
|
|
31
|
+
Literal(node) {
|
|
32
|
+
validateMaxWidthUtils(context, node);
|
|
33
|
+
},
|
|
28
34
|
};
|
|
29
35
|
},
|
|
30
36
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
// ------------------------------------------------------------------------------
|
|
3
2
|
// Requirements
|
|
4
3
|
// ------------------------------------------------------------------------------
|
|
5
4
|
const { DOCS_BASE_URL } = require('../constants');
|
|
6
5
|
const { defineTemplateBodyVisitor } = require('../utils/index');
|
|
7
|
-
const { validateInterpolatedUtils } = require('../utils/tailwind-utils');
|
|
6
|
+
const { validateInterpolatedUtils, validateMaxWidthUtils } = require('../utils/tailwind-utils');
|
|
8
7
|
|
|
9
8
|
// ------------------------------------------------------------------------------
|
|
10
9
|
// Rule Definition
|
|
@@ -14,7 +13,8 @@ module.exports = {
|
|
|
14
13
|
meta: {
|
|
15
14
|
type: 'error',
|
|
16
15
|
docs: {
|
|
17
|
-
description:
|
|
16
|
+
description:
|
|
17
|
+
'Ensures Tailwind CSS is used according to our internal guidelines in Vue templates.',
|
|
18
18
|
category: 'css',
|
|
19
19
|
url: DOCS_BASE_URL + '/vue-tailwind.md',
|
|
20
20
|
},
|
|
@@ -22,10 +22,19 @@ module.exports = {
|
|
|
22
22
|
create(context) {
|
|
23
23
|
return defineTemplateBodyVisitor(context, {
|
|
24
24
|
BinaryExpression(node) {
|
|
25
|
-
validateInterpolatedUtils(context, node)
|
|
25
|
+
validateInterpolatedUtils(context, node);
|
|
26
26
|
},
|
|
27
27
|
TemplateLiteral(node) {
|
|
28
|
-
validateInterpolatedUtils(context, node)
|
|
28
|
+
validateInterpolatedUtils(context, node);
|
|
29
|
+
},
|
|
30
|
+
VLiteral(node) {
|
|
31
|
+
validateMaxWidthUtils(context, node);
|
|
32
|
+
},
|
|
33
|
+
Literal(node) {
|
|
34
|
+
validateMaxWidthUtils(context, node);
|
|
35
|
+
},
|
|
36
|
+
TemplateElement(node) {
|
|
37
|
+
validateMaxWidthUtils(context, node);
|
|
29
38
|
},
|
|
30
39
|
});
|
|
31
40
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const INTERPOLATION_PATTERN = /gl-[a-z0-9
|
|
1
|
+
const INTERPOLATION_PATTERN = /(^|\s)gl-[a-z0-9-/]*$/i;
|
|
2
2
|
const INTERPOLATED_UTIL_ERROR =
|
|
3
3
|
'You are building a CSS utility class name using string interpolation which is forbidden because Tailwind CSS needs fully qualified names to properly generate the utilities we use.';
|
|
4
4
|
const INTERPOLATED_UTILS_VALIDATORS = {
|
|
@@ -6,6 +6,15 @@ const INTERPOLATED_UTILS_VALIDATORS = {
|
|
|
6
6
|
TemplateLiteral: templateLiteralHasInterpolatedUtils,
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
const MAX_WIDTH_PATTERN = /max-(sm|md|lg|xl):gl-/;
|
|
10
|
+
const MAX_WIDTH_UTIL_ERROR =
|
|
11
|
+
'Do not use max-width media query utility classes unless absolutely necessary. Use min-width media query utility classes instead.';
|
|
12
|
+
const MAX_WIDTH_UTILS_VALIDATORS = {
|
|
13
|
+
Literal: literalHasMaxWidthUtils,
|
|
14
|
+
VLiteral: literalHasMaxWidthUtils,
|
|
15
|
+
TemplateElement: templateElementHasMaxWidthUtils,
|
|
16
|
+
};
|
|
17
|
+
|
|
9
18
|
function binaryExpressionHasInterpolatedUtils(node) {
|
|
10
19
|
return node.operator === '+' && INTERPOLATION_PATTERN.test(node.left.value);
|
|
11
20
|
}
|
|
@@ -29,7 +38,30 @@ function validateInterpolatedUtils(context, node) {
|
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
|
|
41
|
+
function literalHasMaxWidthUtils(node) {
|
|
42
|
+
return MAX_WIDTH_PATTERN.test(node.value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function templateElementHasMaxWidthUtils(node) {
|
|
46
|
+
return MAX_WIDTH_PATTERN.test(node.value.raw);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function validateMaxWidthUtils(context, node) {
|
|
50
|
+
if (MAX_WIDTH_UTILS_VALIDATORS[node.type] === undefined) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (MAX_WIDTH_UTILS_VALIDATORS[node.type](node)) {
|
|
55
|
+
context.report({
|
|
56
|
+
node,
|
|
57
|
+
message: MAX_WIDTH_UTIL_ERROR,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
32
62
|
module.exports = {
|
|
33
63
|
validateInterpolatedUtils,
|
|
64
|
+
validateMaxWidthUtils,
|
|
34
65
|
INTERPOLATED_UTIL_ERROR,
|
|
66
|
+
MAX_WIDTH_UTIL_ERROR,
|
|
35
67
|
};
|