@hero-design/eslint-plugin 7.27.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/.eslintrc.js +19 -0
- package/.turbo/turbo-lint.log +2 -0
- package/.turbo/turbo-test:ci.log +10 -0
- package/README.md +46 -0
- package/docs/rules/no-deprecated-component-prop-value.md +63 -0
- package/docs/rules/no-deprecated-component-prop.md +37 -0
- package/docs/rules/no-deprecated-theme-key.md +38 -0
- package/docs/rules/not-recommended-import.md +53 -0
- package/jest.config.js +3 -0
- package/lib/index.js +387 -0
- package/lib/rules/no-deprecated-component-prop-value.js +189 -0
- package/lib/rules/no-deprecated-component-prop.js +146 -0
- package/lib/rules/no-deprecated-theme-key.js +92 -0
- package/lib/rules/not-recommended-import.js +97 -0
- package/package.json +38 -0
- package/tests/lib/rules/no-deprecated-component-prop-value.js +142 -0
- package/tests/lib/rules/no-deprecated-component-prop.js +110 -0
- package/tests/lib/rules/no-deprecated-theme-key.js +77 -0
- package/tests/lib/rules/not-recommended-import.js +82 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow deprecated component prop's values
|
|
3
|
+
* @author Thong Quach
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
12
|
+
module.exports = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
docs: {
|
|
16
|
+
description: "Disallow deprecated component prop's values",
|
|
17
|
+
recommended: false,
|
|
18
|
+
url: null, // URL to the documentation page for this rule
|
|
19
|
+
},
|
|
20
|
+
fixable: null, // Or `code` or `whitespace`
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
package: { type: 'string' },
|
|
26
|
+
components: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
items: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
name: { type: 'string' },
|
|
32
|
+
props: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
items: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
name: { type: 'string' },
|
|
38
|
+
values: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
required: ['package', 'components'],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
messages: {
|
|
53
|
+
deprecatedValue:
|
|
54
|
+
'The value "{{ value }}" of "{{ prop }}" prop has been deprecated.',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
create(context) {
|
|
59
|
+
const components = context.options[0].components.map((c) => ({
|
|
60
|
+
props: c.props.map((p) => p.name),
|
|
61
|
+
values: c.props.map((p) => p.values),
|
|
62
|
+
identifiers: c.name.split('.'),
|
|
63
|
+
}));
|
|
64
|
+
let importedComponents = [];
|
|
65
|
+
|
|
66
|
+
const getDeprecatedComponent = (node) => {
|
|
67
|
+
return importedComponents.find((c) => {
|
|
68
|
+
const { identifiers } = c;
|
|
69
|
+
const elementName = node.name;
|
|
70
|
+
|
|
71
|
+
// <Radio />
|
|
72
|
+
if (elementName.type === 'JSXIdentifier' && identifiers.length === 1) {
|
|
73
|
+
return elementName.name === identifiers[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (elementName.type === 'JSXMemberExpression') {
|
|
77
|
+
// <Radio.Group />
|
|
78
|
+
if (identifiers.length === 2) {
|
|
79
|
+
return (
|
|
80
|
+
elementName.object.name === identifiers[0] &&
|
|
81
|
+
elementName.property.name === identifiers[1]
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// <HD.Radio.Group />
|
|
86
|
+
if (identifiers.length === 3) {
|
|
87
|
+
return (
|
|
88
|
+
elementName.object.object.name === identifiers[0] &&
|
|
89
|
+
elementName.object.property.name === identifiers[1] &&
|
|
90
|
+
elementName.property.name === identifiers[2]
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return false;
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
ImportDeclaration(node) {
|
|
101
|
+
if (node.source.value !== context.options[0].package) return;
|
|
102
|
+
|
|
103
|
+
// import * as HD from "@hero-design/rn";
|
|
104
|
+
if (node.specifiers[0].type === 'ImportNamespaceSpecifier') {
|
|
105
|
+
const localPackageName = node.specifiers[0].local.name;
|
|
106
|
+
|
|
107
|
+
importedComponents = components.map((c) => ({
|
|
108
|
+
...c,
|
|
109
|
+
identifiers: [localPackageName].concat(c.identifiers),
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// import { Card, Box } from "@hero-design/rn";
|
|
116
|
+
// import { Card as HDCard, Box } from "hero-design/rn";
|
|
117
|
+
node.specifiers.forEach((spec) => {
|
|
118
|
+
if (spec.type !== 'ImportSpecifier') return;
|
|
119
|
+
|
|
120
|
+
const importingComponents = components
|
|
121
|
+
.filter((c) => spec.imported.name === c.identifiers[0])
|
|
122
|
+
.map((c) => ({
|
|
123
|
+
...c,
|
|
124
|
+
identifiers: [spec.local.name].concat(c.identifiers.slice(1)),
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
importedComponents.push(...importingComponents);
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
JSXOpeningElement(node) {
|
|
132
|
+
if (importedComponents.length === 0) return;
|
|
133
|
+
|
|
134
|
+
const deprecatedComponent = getDeprecatedComponent(node);
|
|
135
|
+
if (deprecatedComponent === undefined) return;
|
|
136
|
+
|
|
137
|
+
node.attributes.forEach((atb) => {
|
|
138
|
+
if (atb.type !== 'JSXAttribute') return;
|
|
139
|
+
|
|
140
|
+
const deprecatedPropIndex = deprecatedComponent.props.findIndex(
|
|
141
|
+
(p) => p === atb.name.name
|
|
142
|
+
);
|
|
143
|
+
if (deprecatedPropIndex < 0) return;
|
|
144
|
+
|
|
145
|
+
const deprecatedValues =
|
|
146
|
+
deprecatedComponent.values[deprecatedPropIndex];
|
|
147
|
+
const currValue = atb.value;
|
|
148
|
+
|
|
149
|
+
if (currValue.type === 'Literal') {
|
|
150
|
+
const deprecatedValue = deprecatedValues.find(
|
|
151
|
+
(v) => v === currValue.value
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
if (deprecatedValue !== undefined) {
|
|
155
|
+
context.report({
|
|
156
|
+
node: atb.value,
|
|
157
|
+
messageId: 'deprecatedValue',
|
|
158
|
+
data: {
|
|
159
|
+
prop: deprecatedComponent.props[deprecatedPropIndex],
|
|
160
|
+
value: deprecatedValue,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
currValue.type === 'JSXExpressionContainer' &&
|
|
168
|
+
currValue.expression.type === 'Literal'
|
|
169
|
+
) {
|
|
170
|
+
const deprecatedValue = deprecatedValues.find(
|
|
171
|
+
(v) => v === currValue.expression.value
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
if (deprecatedValue !== undefined) {
|
|
175
|
+
context.report({
|
|
176
|
+
node: atb.value,
|
|
177
|
+
messageId: 'deprecatedValue',
|
|
178
|
+
data: {
|
|
179
|
+
prop: deprecatedComponent.props[deprecatedPropIndex],
|
|
180
|
+
value: deprecatedValue,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow deprecated component props
|
|
3
|
+
* @author Thong Quach
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
12
|
+
module.exports = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
docs: {
|
|
16
|
+
description: 'Disallow deprecated component props',
|
|
17
|
+
recommended: false,
|
|
18
|
+
url: null, // URL to the documentation page for this rule
|
|
19
|
+
},
|
|
20
|
+
fixable: null, // Or `code` or `whitespace`
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
package: { type: 'string' },
|
|
26
|
+
components: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
items: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
name: { type: 'string' },
|
|
32
|
+
props: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
items: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
required: ['package', 'components'],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
messages: {
|
|
47
|
+
deprecatedProp: 'The prop "{{ prop }}" has been deprecated.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
create(context) {
|
|
52
|
+
const components = context.options[0].components.map((c) => ({
|
|
53
|
+
props: c.props,
|
|
54
|
+
identifiers: c.name.split('.'),
|
|
55
|
+
}));
|
|
56
|
+
let importedComponents = [];
|
|
57
|
+
|
|
58
|
+
const getDeprecatedComponent = (node) => {
|
|
59
|
+
return importedComponents.find((c) => {
|
|
60
|
+
const { identifiers } = c;
|
|
61
|
+
const elementName = node.name;
|
|
62
|
+
|
|
63
|
+
// <Radio />
|
|
64
|
+
if (elementName.type === 'JSXIdentifier' && identifiers.length === 1) {
|
|
65
|
+
return elementName.name === identifiers[0];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (elementName.type === 'JSXMemberExpression') {
|
|
69
|
+
// <Radio.Group />
|
|
70
|
+
if (identifiers.length === 2) {
|
|
71
|
+
return (
|
|
72
|
+
elementName.object.name === identifiers[0] &&
|
|
73
|
+
elementName.property.name === identifiers[1]
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// <HD.Radio.Group />
|
|
78
|
+
if (identifiers.length === 3) {
|
|
79
|
+
return (
|
|
80
|
+
elementName.object.object.name === identifiers[0] &&
|
|
81
|
+
elementName.object.property.name === identifiers[1] &&
|
|
82
|
+
elementName.property.name === identifiers[2]
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
ImportDeclaration(node) {
|
|
93
|
+
if (node.source.value !== context.options[0].package) return;
|
|
94
|
+
|
|
95
|
+
// import * as HD from "@hero-design/rn";
|
|
96
|
+
if (node.specifiers[0].type === 'ImportNamespaceSpecifier') {
|
|
97
|
+
const localPackageName = node.specifiers[0].local.name;
|
|
98
|
+
|
|
99
|
+
importedComponents = components.map((c) => ({
|
|
100
|
+
...c,
|
|
101
|
+
identifiers: [localPackageName].concat(c.identifiers),
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// import { Card, Box } from "@hero-design/rn";
|
|
108
|
+
// import { Card as HDCard, Box } from "hero-design/rn";
|
|
109
|
+
node.specifiers.forEach((spec) => {
|
|
110
|
+
if (spec.type !== 'ImportSpecifier') return;
|
|
111
|
+
|
|
112
|
+
const importingComponents = components
|
|
113
|
+
.filter((c) => spec.imported.name === c.identifiers[0])
|
|
114
|
+
.map((c) => ({
|
|
115
|
+
...c,
|
|
116
|
+
identifiers: [spec.local.name].concat(c.identifiers.slice(1)),
|
|
117
|
+
}));
|
|
118
|
+
|
|
119
|
+
importedComponents.push(...importingComponents);
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
JSXOpeningElement(node) {
|
|
124
|
+
if (importedComponents.length === 0) return;
|
|
125
|
+
|
|
126
|
+
const deprecatedComponent = getDeprecatedComponent(node);
|
|
127
|
+
if (deprecatedComponent === undefined) return;
|
|
128
|
+
|
|
129
|
+
node.attributes.forEach((atb) => {
|
|
130
|
+
if (atb.type !== 'JSXAttribute') return;
|
|
131
|
+
|
|
132
|
+
const deprecatedProp = deprecatedComponent.props.find(
|
|
133
|
+
(p) => p === atb.name.name
|
|
134
|
+
);
|
|
135
|
+
if (deprecatedProp !== undefined) {
|
|
136
|
+
context.report({
|
|
137
|
+
node: atb,
|
|
138
|
+
messageId: 'deprecatedProp',
|
|
139
|
+
data: { prop: deprecatedProp },
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow deprecated theme keys
|
|
3
|
+
* @author Thong Quach
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
12
|
+
module.exports = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem', // `problem`, `suggestion`, or `layout`
|
|
15
|
+
docs: {
|
|
16
|
+
description: 'Disallow deprecated theme keys',
|
|
17
|
+
recommended: false,
|
|
18
|
+
url: null, // URL to the documentation page for this rule
|
|
19
|
+
},
|
|
20
|
+
fixable: 'code', // Or `code` or `whitespace`
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
keys: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
old: { type: 'string' },
|
|
31
|
+
new: { type: 'string' },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
required: ['keys'],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
messages: {
|
|
41
|
+
deprecatedThemeKey: 'The key "{{ key }}" in theme has been deprecated.',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
create(context) {
|
|
46
|
+
const oldKeys = context.options[0].keys.map((k) => k.old);
|
|
47
|
+
const newKeys = context.options[0].keys.map((k) => k.new);
|
|
48
|
+
|
|
49
|
+
const findPath = (node, identifiers) => {
|
|
50
|
+
const property = node.property;
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
property != null &&
|
|
54
|
+
property.type === 'Identifier' &&
|
|
55
|
+
property.name != null
|
|
56
|
+
) {
|
|
57
|
+
return findPath(node.parent, [...identifiers, property.name]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return identifiers.join('.');
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
MemberExpression(node) {
|
|
65
|
+
const object = node.object;
|
|
66
|
+
if (object == null) return;
|
|
67
|
+
|
|
68
|
+
if (object.type === 'Identifier' && object.name === 'theme') {
|
|
69
|
+
const path = findPath(node, []);
|
|
70
|
+
const deprecatedKeyIndex = oldKeys.findIndex((k) => k === path);
|
|
71
|
+
|
|
72
|
+
if (deprecatedKeyIndex >= 0) {
|
|
73
|
+
const reportingNode =
|
|
74
|
+
node.parent.type === 'MemberExpression' ? node.parent : node;
|
|
75
|
+
|
|
76
|
+
context.report({
|
|
77
|
+
node: reportingNode,
|
|
78
|
+
messageId: 'deprecatedThemeKey',
|
|
79
|
+
data: { key: oldKeys[deprecatedKeyIndex] },
|
|
80
|
+
fix: function (fixer) {
|
|
81
|
+
return fixer.replaceText(
|
|
82
|
+
reportingNode,
|
|
83
|
+
`theme.${newKeys[deprecatedKeyIndex]}`
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow not recommended imports
|
|
3
|
+
* @author Thong Quach
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
12
|
+
module.exports = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
docs: {
|
|
16
|
+
description: 'Disallow not recommended imports ',
|
|
17
|
+
recommended: false,
|
|
18
|
+
url: null, // URL to the documentation page for this rule
|
|
19
|
+
},
|
|
20
|
+
fixable: null, // Or `code` or `whitespace`
|
|
21
|
+
schema: {
|
|
22
|
+
type: 'array',
|
|
23
|
+
items: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
package: { type: 'string' },
|
|
27
|
+
imports: {
|
|
28
|
+
type: 'array',
|
|
29
|
+
items: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
name: { type: 'string' },
|
|
33
|
+
message: { type: 'string' },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
required: ['package', 'imports'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
messages: {
|
|
43
|
+
notRecommendedImport:
|
|
44
|
+
'Importing "{{ import }}" from "{{ package }}" is not recommended. {{ message }}',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
create(context) {
|
|
49
|
+
const packages = context.options.map((opt) => opt.package);
|
|
50
|
+
const imports = context.options.map((opt) => opt.imports);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
ImportDeclaration(node) {
|
|
54
|
+
const packageIndex = packages.findIndex((p) => p === node.source.value);
|
|
55
|
+
if (packageIndex < 0) return;
|
|
56
|
+
|
|
57
|
+
node.specifiers.forEach((spec) => {
|
|
58
|
+
if (spec.type === 'ImportDefaultSpecifier') {
|
|
59
|
+
const defaultImport = imports[packageIndex].find(
|
|
60
|
+
(imp) => imp.name === 'default'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (defaultImport != null) {
|
|
64
|
+
context.report({
|
|
65
|
+
node: spec,
|
|
66
|
+
messageId: 'notRecommendedImport',
|
|
67
|
+
data: {
|
|
68
|
+
import: defaultImport.name,
|
|
69
|
+
package: packages[packageIndex],
|
|
70
|
+
message: defaultImport.message,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (spec.type !== 'ImportSpecifier') return;
|
|
77
|
+
|
|
78
|
+
const foundImport = imports[packageIndex].find(
|
|
79
|
+
(imp) => imp.name === spec.imported.name
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (foundImport != null) {
|
|
83
|
+
context.report({
|
|
84
|
+
node: spec,
|
|
85
|
+
messageId: 'notRecommendedImport',
|
|
86
|
+
data: {
|
|
87
|
+
import: foundImport.name,
|
|
88
|
+
package: packages[packageIndex],
|
|
89
|
+
message: foundImport.message,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hero-design/eslint-plugin",
|
|
3
|
+
"version": "7.27.1",
|
|
4
|
+
"description": "Hero Design's eslint plugin",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"eslint",
|
|
7
|
+
"eslintplugin",
|
|
8
|
+
"eslint-plugin"
|
|
9
|
+
],
|
|
10
|
+
"author": "Thong Quach",
|
|
11
|
+
"main": "./lib/index.js",
|
|
12
|
+
"exports": "./lib/index.js",
|
|
13
|
+
"prettier": "prettier-config-hd",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"lint": "eslint .",
|
|
16
|
+
"test": "jest --runInBand",
|
|
17
|
+
"test:watch": "jest --runInBand --watch",
|
|
18
|
+
"test:ci": "jest --runInBand --logHeapUsage",
|
|
19
|
+
"publish:npm": "yarn publish --access public"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"requireindex": "^1.2.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"eslint": "^8.10.0",
|
|
26
|
+
"eslint-plugin-eslint-plugin": "^5.0.0",
|
|
27
|
+
"eslint-plugin-node": "^11.1.0",
|
|
28
|
+
"jest": "^27.3.1",
|
|
29
|
+
"prettier-config-hd": "7.27.1"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": "^14.17.0 || ^16.0.0 || >= 18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"eslint": ">=7"
|
|
36
|
+
},
|
|
37
|
+
"license": "ISC"
|
|
38
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Disallow deprecated component prop's values
|
|
3
|
+
* @author Thong Quach
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const rule = require('../../../lib/rules/no-deprecated-component-prop-value'),
|
|
12
|
+
RuleTester = require('eslint').RuleTester;
|
|
13
|
+
|
|
14
|
+
//------------------------------------------------------------------------------
|
|
15
|
+
// Tests
|
|
16
|
+
//------------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
const config = {
|
|
19
|
+
options: [
|
|
20
|
+
{
|
|
21
|
+
package: '@hero-design/rn',
|
|
22
|
+
components: [
|
|
23
|
+
{
|
|
24
|
+
name: 'Tag',
|
|
25
|
+
props: [{ name: 'intent', values: ['default'] }],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'Alert',
|
|
29
|
+
props: [{ name: 'variant', values: ['default'] }],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Button',
|
|
33
|
+
props: [{ name: 'variant', values: ['basic-transparent'] }],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Icon',
|
|
37
|
+
props: [
|
|
38
|
+
{
|
|
39
|
+
name: 'icon',
|
|
40
|
+
values: [
|
|
41
|
+
'carat-down-small',
|
|
42
|
+
'carat-down',
|
|
43
|
+
'carat-left-small',
|
|
44
|
+
'carat-left',
|
|
45
|
+
'carat-right-small',
|
|
46
|
+
'carat-right',
|
|
47
|
+
'carat-up-small',
|
|
48
|
+
'carat-up',
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Select',
|
|
55
|
+
props: [{ name: 'numberOfLines', values: [1] }],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'Select.Multi',
|
|
59
|
+
props: [{ name: 'numberOfLines', values: [1] }],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
parserOptions: {
|
|
65
|
+
sourceType: 'module',
|
|
66
|
+
ecmaVersion: 6,
|
|
67
|
+
ecmaFeatures: { jsx: true },
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const ruleTester = new RuleTester();
|
|
72
|
+
ruleTester.run('no-deprecated-component-prop-value', rule, {
|
|
73
|
+
valid: [
|
|
74
|
+
{
|
|
75
|
+
code: '<Box intent="default" {...props} />',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
code: 'import HD from "@hero-design/rn"; <Tag intent="default" {...props} />',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
code: 'import { Tag, Box } from "@hero-design/rn"; <Tag intent="primary" />',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
code: 'import { Tag, Box } from "ant-design"; <Tag intent="default" />',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
code: 'import { Tag as HDTag, Box } from "@hero-design/rn"; <Tag intent="default" />',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
code: 'import { Select } from "@hero-design/rn"; <Select.Multi numberOfLines={2} />',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
code: 'import * as HD from "@hero-design/rn"; <Card variant="basic" />',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
code: 'import * as HD from "@hero-design/rn"; <HD.Select.Multi numberOfLines={2} />',
|
|
97
|
+
},
|
|
98
|
+
].map((test) => ({
|
|
99
|
+
...test,
|
|
100
|
+
...config,
|
|
101
|
+
})),
|
|
102
|
+
|
|
103
|
+
invalid: [
|
|
104
|
+
{
|
|
105
|
+
code: 'import { Tag, Box } from "@hero-design/rn"; <Tag intent="default" />',
|
|
106
|
+
errors: [{ messageId: 'deprecatedValue' }],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
code: 'import { Tag as HDTag, Box } from "@hero-design/rn"; <HDTag intent="default" />',
|
|
110
|
+
errors: [{ messageId: 'deprecatedValue' }],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
code: 'import { Button as HDButton, Icon } from "@hero-design/rn"; <><HDButton variant="basic-transparent" /><Icon icon="carat-down" /></>',
|
|
114
|
+
errors: [
|
|
115
|
+
{ messageId: 'deprecatedValue' },
|
|
116
|
+
{ messageId: 'deprecatedValue' },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
code: 'import { Select } from "@hero-design/rn"; <Select numberOfLines={1} />',
|
|
121
|
+
errors: [{ messageId: 'deprecatedValue' }],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
code: 'import { Select } from "@hero-design/rn"; <><Select numberOfLines={1} /><Select.Multi numberOfLines={1} /></>',
|
|
125
|
+
errors: [
|
|
126
|
+
{ messageId: 'deprecatedValue' },
|
|
127
|
+
{ messageId: 'deprecatedValue' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
code: 'import * as HD from "@hero-design/rn"; <HD.Select numberOfLines={1} />',
|
|
132
|
+
errors: [{ messageId: 'deprecatedValue' }],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
code: 'import * as HD from "@hero-design/rn"; <HD.Select.Multi numberOfLines={1} />',
|
|
136
|
+
errors: [{ messageId: 'deprecatedValue' }],
|
|
137
|
+
},
|
|
138
|
+
].map((test) => ({
|
|
139
|
+
...test,
|
|
140
|
+
...config,
|
|
141
|
+
})),
|
|
142
|
+
});
|