@hero-design/eslint-plugin 9.2.1-rc.0 → 9.2.2
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 +43 -0
- package/README.md +125 -15
- package/docs/rules/banning-snowflake-approve-comment.md +42 -0
- package/docs/rules/no-deprecated-component-prop-next.md +32 -0
- package/docs/rules/no-direct-color-palette-access.md +34 -0
- package/docs/rules/react-no-text-outside-typography.md +57 -0
- package/eslint.config.js +31 -0
- package/lib/index.js +15 -298
- package/lib/rules/banning-snowflake-approve-comment.js +32 -0
- package/lib/rules/no-deprecated-component-prop-next.js +3 -0
- package/lib/rules/no-deprecated-theme-key.js +3 -15
- package/lib/rules/no-direct-color-palette-access.js +56 -0
- package/lib/rules/react-no-text-outside-typography.js +216 -0
- package/lib/utils.js +17 -0
- package/package.json +8 -6
- package/tests/lib/rules/banning-snowflake-approve-comment.js +46 -0
- package/tests/lib/rules/no-direct-color-palette-access.js +45 -0
- package/tests/lib/rules/react-no-text-outside-typography.js +248 -0
- package/.eslintrc.js +0 -19
- package/.turbo/turbo-publish:npm.log +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hero-design/eslint-plugin",
|
|
3
|
-
"version": "9.2.
|
|
3
|
+
"version": "9.2.2",
|
|
4
4
|
"description": "Hero Design's eslint plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"exports": "./lib/index.js",
|
|
13
13
|
"prettier": "prettier-config-hd",
|
|
14
14
|
"scripts": {
|
|
15
|
-
"lint": "eslint
|
|
15
|
+
"lint": "eslint lib tests --quiet",
|
|
16
16
|
"test": "jest --runInBand",
|
|
17
17
|
"test:watch": "jest --runInBand --watch",
|
|
18
18
|
"test:ci": "jest --runInBand --logHeapUsage",
|
|
@@ -22,14 +22,16 @@
|
|
|
22
22
|
"requireindex": "^1.2.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"eslint": "^
|
|
25
|
+
"@eslint/eslintrc": "^3.1.0",
|
|
26
|
+
"@eslint/js": "^9.8.0",
|
|
27
|
+
"eslint": "^8.56.0",
|
|
26
28
|
"eslint-plugin-eslint-plugin": "^5.0.0",
|
|
27
29
|
"eslint-plugin-node": "^11.1.0",
|
|
28
|
-
"jest": "^
|
|
29
|
-
"prettier-config-hd": "
|
|
30
|
+
"jest": "^29.2.1",
|
|
31
|
+
"prettier-config-hd": "8.42.4"
|
|
30
32
|
},
|
|
31
33
|
"engines": {
|
|
32
|
-
"node": "^14.17.0 || ^16.0.0 || >=
|
|
34
|
+
"node": "^14.17.0 || ^16.0.0 || ^18.0.0 || ^20.0.0 || >= 22.0.0"
|
|
33
35
|
},
|
|
34
36
|
"peerDependencies": {
|
|
35
37
|
"eslint": ">=7"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { RuleTester } = require('eslint');
|
|
2
|
+
const rule = require('../../../lib/rules/banning-snowflake-approve-comment');
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
// Define valid and invalid test cases
|
|
7
|
+
ruleTester.run('no-snowflake-guard', rule, {
|
|
8
|
+
// Valid cases (should not trigger the rule)
|
|
9
|
+
valid: [
|
|
10
|
+
'// A normal comment without snowflake-guard',
|
|
11
|
+
'// Another valid comment',
|
|
12
|
+
'/* @no-snowflake-guard */',
|
|
13
|
+
'console.log("no issues here");',
|
|
14
|
+
],
|
|
15
|
+
|
|
16
|
+
// Invalid cases (should trigger the rule)
|
|
17
|
+
invalid: [
|
|
18
|
+
{
|
|
19
|
+
code: '// @snowflake-guard/none-css-classname', // Triggers the rule
|
|
20
|
+
errors: [
|
|
21
|
+
{
|
|
22
|
+
message:
|
|
23
|
+
'Comments including @snowflake-guard/ are not allowed. Please contact Andromeda team for the approval.',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
code: '// @snowflake-guard/another-pattern', // Triggers the rule
|
|
29
|
+
errors: [
|
|
30
|
+
{
|
|
31
|
+
message:
|
|
32
|
+
'Comments including @snowflake-guard/ are not allowed. Please contact Andromeda team for the approval.',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
code: '/* @snowflake-guard/custom-pattern */', // Triggers the rule for block comments
|
|
38
|
+
errors: [
|
|
39
|
+
{
|
|
40
|
+
message:
|
|
41
|
+
'Comments including @snowflake-guard/ are not allowed. Please contact Andromeda team for the approval.',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const rule = require('../../../lib/rules/no-direct-color-palette-access');
|
|
2
|
+
const RuleTester = require('eslint').RuleTester;
|
|
3
|
+
|
|
4
|
+
//------------------------------------------------------------------------------
|
|
5
|
+
// Tests
|
|
6
|
+
//------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
const config = {
|
|
9
|
+
parserOptions: {
|
|
10
|
+
sourceType: 'module',
|
|
11
|
+
ecmaVersion: 6,
|
|
12
|
+
ecmaFeatures: { jsx: true },
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ruleTester = new RuleTester();
|
|
17
|
+
ruleTester.run('no-direct-color-palette-access', rule, {
|
|
18
|
+
valid: [
|
|
19
|
+
{
|
|
20
|
+
code: 'const color = theme.colors.hoverDanger',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
code: '<Box sx={{ backgroundColor: theme.colors.hoverDanger }} />',
|
|
24
|
+
},
|
|
25
|
+
].map((test) => ({ ...test, ...config })),
|
|
26
|
+
invalid: [
|
|
27
|
+
{
|
|
28
|
+
code: 'const color = theme.colors.palette.redLight30',
|
|
29
|
+
errors: [{ messageId: 'invalidColorAccess' }],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
code: 'const color = theme.colors.palette',
|
|
33
|
+
errors: [{ messageId: 'invalidColorAccess' }],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
code: '<Box sx={{ backgroundColor: theme.colors.palette.redLight30 }} />',
|
|
37
|
+
errors: [{ messageId: 'invalidColorAccess' }],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
code: `const colors = theme.colors;
|
|
41
|
+
const color = colors.palette.redLight30`,
|
|
42
|
+
errors: [{ messageId: 'invalidColorAccess' }],
|
|
43
|
+
}
|
|
44
|
+
].map((test) => ({ ...test, ...config })),
|
|
45
|
+
});
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
const rule = require('../../../lib/rules/react-no-text-outside-typography');
|
|
2
|
+
const RuleTester = require('eslint').RuleTester;
|
|
3
|
+
|
|
4
|
+
//------------------------------------------------------------------------------
|
|
5
|
+
// Tests
|
|
6
|
+
//------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
const config = {
|
|
9
|
+
parserOptions: {
|
|
10
|
+
sourceType: 'module',
|
|
11
|
+
ecmaVersion: 6,
|
|
12
|
+
ecmaFeatures: { jsx: true },
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ruleTester = new RuleTester();
|
|
17
|
+
ruleTester.run('react-no-text-outside-typography', rule, {
|
|
18
|
+
valid: [
|
|
19
|
+
// Common cases
|
|
20
|
+
{
|
|
21
|
+
code: '<Typography.Text>Content</Typography.Text>',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
code: '<Typography.Title>Content</Typography.Title>',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
code: `<Typography.Text>
|
|
28
|
+
Click <a href='..'>here</a>
|
|
29
|
+
</Typography.Text>`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
code: `<Typography.Text>
|
|
33
|
+
<Box>Content</Box>
|
|
34
|
+
</Typography.Text>
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
// With Intl.formatMessage and conditions
|
|
38
|
+
{
|
|
39
|
+
code: `<Typography.Text>
|
|
40
|
+
<Box>{Intl.formatMessage({ id: 'someId' })}</Box>
|
|
41
|
+
</Typography.Text>
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
code: `<Typography.Text>
|
|
46
|
+
<Box>{condition1 && Intl.formatMessage({ id: 'someId' })}</Box>
|
|
47
|
+
</Typography.Text>
|
|
48
|
+
`,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
code: `<Typography.Text>
|
|
52
|
+
<Box>{condition1 ? Intl.formatMessage({ id: 'someId' }) : null}</Box>
|
|
53
|
+
</Typography.Text>
|
|
54
|
+
`,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
code: `
|
|
58
|
+
<Typography.Text>
|
|
59
|
+
{Intl.formatMessage({ id: 'someId' })}
|
|
60
|
+
</Typography.Text>`,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: `
|
|
64
|
+
<Typography.Text>
|
|
65
|
+
{condition1 && Intl.formatMessage({ id: 'someId' })}
|
|
66
|
+
</Typography.Text>`,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
code: `
|
|
70
|
+
<Typography.Text>
|
|
71
|
+
{condition1 ? Intl.formatMessage({ id: 'someId' }) : null}
|
|
72
|
+
</Typography.Text>`,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
code: `
|
|
76
|
+
<Typography.Text>
|
|
77
|
+
{condition1 ? Intl.formatMessage({ id: 'someId' }) : Intl.formatMessage({ id: 'someId2' })}
|
|
78
|
+
</Typography.Text>`,
|
|
79
|
+
},
|
|
80
|
+
// Custom namings
|
|
81
|
+
{
|
|
82
|
+
code: `
|
|
83
|
+
import { Typography as HDTypography } from '@hero-design/react';
|
|
84
|
+
<HDTypography.Text>Content</HDTypography.Text>
|
|
85
|
+
`,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
code: `
|
|
89
|
+
import { Typography as HDTypography } from '@hero-design/react';
|
|
90
|
+
const { Text } = HDTypography;
|
|
91
|
+
<Text>Content</Text>
|
|
92
|
+
`,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
code: `
|
|
96
|
+
import { Typography } from '@hero-design/react';
|
|
97
|
+
const { Title } = Typography;
|
|
98
|
+
<Title>Content</Title>
|
|
99
|
+
`,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
code: `
|
|
103
|
+
import { Typography } from '@hero-design/react';
|
|
104
|
+
const { Title: HDTypographyTitle } = Typography;
|
|
105
|
+
<HDTypographyTitle>Content</HDTypographyTitle>
|
|
106
|
+
`,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
code: `
|
|
110
|
+
import { Typography } from '@hero-design/react';
|
|
111
|
+
const Title = Typography.Title;
|
|
112
|
+
<Title>Content</Title>
|
|
113
|
+
`,
|
|
114
|
+
},
|
|
115
|
+
// Exceptions with tagName prop
|
|
116
|
+
{
|
|
117
|
+
code: `
|
|
118
|
+
<Typography.Text tagName='p'>
|
|
119
|
+
<span>Content</span>
|
|
120
|
+
</Typography.Text>
|
|
121
|
+
`,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
code: `
|
|
125
|
+
<Typography.Text tagName='div'>
|
|
126
|
+
<ul>
|
|
127
|
+
<li>Content</li>
|
|
128
|
+
</ul>
|
|
129
|
+
</Typography.Text>
|
|
130
|
+
`,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
code: `
|
|
134
|
+
<Typography.Text tagName='div'>
|
|
135
|
+
<p>
|
|
136
|
+
<a>Content</a>
|
|
137
|
+
</p>
|
|
138
|
+
</Typography.Text>
|
|
139
|
+
`,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
code: `
|
|
143
|
+
<Typography.Text tagName='span'>
|
|
144
|
+
<span>Content</span>
|
|
145
|
+
</Typography.Text>
|
|
146
|
+
`,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
code: `
|
|
150
|
+
<Typography.Text tagName='label'>
|
|
151
|
+
<span>Content</span>
|
|
152
|
+
</Typography.Text>
|
|
153
|
+
`,
|
|
154
|
+
},
|
|
155
|
+
].map((test) => ({ ...test, ...config })),
|
|
156
|
+
invalid: [
|
|
157
|
+
// Common cases
|
|
158
|
+
{
|
|
159
|
+
code: '<div>Content</div>',
|
|
160
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
code: '<p>Content</p>',
|
|
164
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
code: '<h1>Content</h1>',
|
|
168
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
169
|
+
},
|
|
170
|
+
// With Intl.formatMessage and conditions
|
|
171
|
+
{
|
|
172
|
+
code: `
|
|
173
|
+
<div>
|
|
174
|
+
{Intl.formatMessage({ id: 'someId' })}
|
|
175
|
+
</div>`,
|
|
176
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
code: `
|
|
180
|
+
<div>
|
|
181
|
+
{ condition && Intl.formatMessage({ id: 'someId' })}
|
|
182
|
+
</div>`,
|
|
183
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
code: `
|
|
187
|
+
<div>
|
|
188
|
+
{ condition && condition2 && Intl.formatMessage({ id: 'someId' })}
|
|
189
|
+
</div>`,
|
|
190
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
code: `
|
|
194
|
+
<div>
|
|
195
|
+
{ condition && condition2 || condition3 && Intl.formatMessage({ id: 'someId' })}
|
|
196
|
+
</div>`,
|
|
197
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
code: `
|
|
201
|
+
<div>
|
|
202
|
+
{condition1 ? Intl.formatMessage({ id: 'someId' }) : null}
|
|
203
|
+
</div>`,
|
|
204
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
205
|
+
},
|
|
206
|
+
// Exceptions with tagName prop
|
|
207
|
+
{
|
|
208
|
+
code: `
|
|
209
|
+
<Typography.Text tagName='p'>
|
|
210
|
+
<span>
|
|
211
|
+
<span>Content</span>
|
|
212
|
+
</span>
|
|
213
|
+
</Typography.Text>
|
|
214
|
+
`,
|
|
215
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
code: `
|
|
219
|
+
<Typography.Text tagName='div'>
|
|
220
|
+
<span>
|
|
221
|
+
<li>Content</li>
|
|
222
|
+
</span>
|
|
223
|
+
</Typography.Text>
|
|
224
|
+
`,
|
|
225
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
code: `
|
|
229
|
+
<Typography.Text tagName='span'>
|
|
230
|
+
<span>
|
|
231
|
+
<span>Content</span>
|
|
232
|
+
</span>
|
|
233
|
+
</Typography.Text>
|
|
234
|
+
`,
|
|
235
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
code: `
|
|
239
|
+
<Typography.Text tagName='label'>
|
|
240
|
+
<span>
|
|
241
|
+
<span>Content</span>
|
|
242
|
+
</span>
|
|
243
|
+
</Typography.Text>
|
|
244
|
+
`,
|
|
245
|
+
errors: [{ messageId: 'textNodeOutsideTypography' }],
|
|
246
|
+
},
|
|
247
|
+
].map((test) => ({ ...test, ...config })),
|
|
248
|
+
});
|
package/.eslintrc.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
root: true,
|
|
5
|
-
extends: [
|
|
6
|
-
'eslint:recommended',
|
|
7
|
-
'plugin:eslint-plugin/recommended',
|
|
8
|
-
'plugin:node/recommended',
|
|
9
|
-
],
|
|
10
|
-
env: {
|
|
11
|
-
node: true,
|
|
12
|
-
},
|
|
13
|
-
overrides: [
|
|
14
|
-
{
|
|
15
|
-
files: ['tests/**/*.js'],
|
|
16
|
-
env: { jest: true },
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
};
|
|
File without changes
|