Skip to content

Commit f5bbad7

Browse files
prop value shape deprecation lint rule (#1094)
* set new lint rule for prop value * fix schema * add cases to prop-value-shape lint rule * refactor propShapeValueDeprecation * Adding two (failing) tests * Fix test and add two more big tests * More tests * add getComponentName to utils * Renaming * update message when there's no fix * fix deprecation message * update version Co-authored-by: Miki Leib <[email protected]>
1 parent 79e8e37 commit f5bbad7

File tree

5 files changed

+462
-1
lines changed

5 files changed

+462
-1
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
const _ = require('lodash');
2+
const utils = require('../utils');
3+
4+
const MAP_SCHEMA = {
5+
type: 'object',
6+
properties: {
7+
components: {
8+
type: 'array',
9+
items: {
10+
type: 'string'
11+
}
12+
},
13+
propNames: {
14+
type: 'array',
15+
items: {
16+
type: 'string'
17+
}
18+
},
19+
shape: {
20+
type: 'array',
21+
items: {
22+
type: 'object',
23+
required: ['prop', 'message', 'fix'],
24+
properties: {
25+
prop: {
26+
type: 'string'
27+
},
28+
message: {
29+
type: 'string'
30+
},
31+
fix: {
32+
type: 'object'
33+
}
34+
}
35+
}
36+
}
37+
}
38+
};
39+
40+
module.exports = {
41+
meta: {
42+
docs: {
43+
description: "Detect deprecation of prop's value shape",
44+
category: 'Best Practices',
45+
recommended: true
46+
},
47+
messages: {
48+
uiLib: "This prop's value shape is deprecated."
49+
},
50+
fixable: 'code',
51+
schema: [MAP_SCHEMA]
52+
},
53+
create(context) {
54+
function reportPropValueShapeDeprecation(propKey, prop, deprecation, node) {
55+
const componentName = utils.getComponentName(node);
56+
const newProp = _.get(deprecation, 'fix.propName');
57+
const fixMessage = _.get(deprecation, 'message') ? ' ' + _.get(deprecation, 'message') : '';
58+
const message = `The shape of '${prop}' prop of '${componentName}' doesn't contain '${deprecation.prop}' anymore.${fixMessage}`;
59+
context.report({
60+
node,
61+
message,
62+
fix(fixer) {
63+
if (newProp && propKey) {
64+
return fixer.replaceText(propKey, newProp);
65+
}
66+
}
67+
});
68+
}
69+
70+
function testJSXAttributes(node) {
71+
try {
72+
const {deprecations} = _.get(context, 'options[0]');
73+
const componentName = utils.getComponentName(node);
74+
_.forEach(deprecations, deprecation => {
75+
if (_.includes(deprecation.components, componentName)) {
76+
_.forEach(node.attributes, attribute => {
77+
const attributeName = _.get(attribute, 'name.name');
78+
if (attribute.type === 'JSXSpreadAttribute') {
79+
const spreadSource = utils.findValueNodeOfIdentifier(attribute.argument.name, context.getScope());
80+
const spreadSourceName = _.get(spreadSource, 'properties[0].key.name');
81+
checkAttributeProperties(
82+
spreadSource.properties[0].value.properties,
83+
spreadSourceName,
84+
deprecation,
85+
node,
86+
context
87+
);
88+
} else if (_.includes(deprecation.propNames, attributeName)) {
89+
checkAttribute(attribute, deprecation, node);
90+
}
91+
});
92+
}
93+
});
94+
} catch (err) {
95+
console.log('Found error in: ', context.getFilename());
96+
}
97+
}
98+
99+
function checkAttribute(attribute, deprecation, node) {
100+
const attributeName = _.get(attribute, 'name.name');
101+
const attributeType = _.get(attribute, 'value.expression.type');
102+
if (attributeType === 'Identifier') {
103+
const passedProp = utils.findValueNodeOfIdentifier(attribute.value.expression.name, context.getScope());
104+
if (passedProp && passedProp.properties) {
105+
checkAttributeProperties(passedProp.properties, attributeName, deprecation, node, context);
106+
}
107+
}
108+
const attributeProps = _.get(attribute, 'value.expression.properties');
109+
for (let index = 0; index < attributeProps.length; index++) {
110+
const spreadElementType = _.get(attribute, `value.expression.properties[${index}].type`);
111+
if (attributeType === 'ObjectExpression' && spreadElementType === 'ExperimentalSpreadProperty') {
112+
const spreadSource = utils.findValueNodeOfIdentifier(
113+
attribute.value.expression.properties[index].argument.name,
114+
context.getScope()
115+
);
116+
if (spreadSource && spreadSource.properties) {
117+
checkAttributeProperties(spreadSource.properties, attributeName, deprecation, node);
118+
}
119+
}
120+
}
121+
const attributeProperties = _.get(attribute, 'value.expression.properties');
122+
checkAttributeProperties(attributeProperties, attributeName, deprecation, node);
123+
}
124+
125+
function checkAttributeProperties(attributeProperties, attributeName, deprecation, node) {
126+
for (let i = 0; i <= attributeProperties.length; i++) {
127+
const propertyName = _.get(attributeProperties[i], 'key.name');
128+
const origin = propertyName && _.find(deprecation.shape, ['prop', propertyName]);
129+
if (origin && origin.prop && propertyName === origin.prop) {
130+
reportPropValueShapeDeprecation(attributeProperties[i].key, attributeName, origin, node);
131+
}
132+
}
133+
}
134+
135+
return {
136+
JSXOpeningElement: testJSXAttributes
137+
};
138+
}
139+
};

eslint-rules/lib/utils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ function getSpecifierIndex(node, name) {
9292
return matchIndex;
9393
}
9494

95+
function getComponentName(node) {
96+
const nodeProperty = _.get(node, 'name.property.name');
97+
const nodeName = nodeProperty ? _.get(node, 'name.object.name') : _.get(node, 'name.name');
98+
return nodeProperty ? (nodeName + '.' + nodeProperty) : nodeName;
99+
}
100+
95101
module.exports = {
96102
isPropFont,
97103
findAndReportHardCodedValues,
@@ -101,4 +107,5 @@ module.exports = {
101107
findValueNodeOfIdentifier,
102108
getLocalImportSpecifier,
103109
getSpecifierIndex,
110+
getComponentName
104111
};

eslint-rules/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "eslint-plugin-uilib",
3-
"version": "1.0.30",
3+
"version": "1.0.31",
44
"description": "uilib set of eslint rules",
55
"keywords": [
66
"eslint",

0 commit comments

Comments
 (0)