Skip to content

Commit 1a8ca6a

Browse files
authored
Support checking for required props in a component (#1320)
1 parent ccb1e48 commit 1a8ca6a

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

eslint-rules/lib/rules/component-prop-deprecation.js

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const MAP_SCHEMA = {
3030
message: {
3131
type: 'string'
3232
},
33+
isRequired: {
34+
type: 'boolean'
35+
},
3336
fix: {
3437
type: 'object',
3538
required: ['propName'],
@@ -80,32 +83,48 @@ module.exports = {
8083
}
8184
}
8285

86+
function reportRequiredProps({node, name, prop, message: customMessage}) {
87+
try {
88+
const message = `The '${name}' component's prop '${prop}' is required. ${customMessage}`;
89+
context.report({
90+
node: node,
91+
message
92+
});
93+
} catch (err) {
94+
console.log('Found error in: ', context.getFilename());
95+
}
96+
}
97+
8398
const {deprecations} = context.options[0];
8499
const organizedDeprecations = organizeDeprecations(deprecations);
85100

86101
const imports = [];
87102

88103
function checkPropDeprecation(node, fixNode, propName, deprecatedPropList, componentName) {
89104
const deprecatedProp = _.find(deprecatedPropList, {prop: propName});
90-
if (deprecatedProp) {
105+
if (deprecatedProp && !deprecatedProp.isRequired) {
91106
const {prop, message, fix} = deprecatedProp;
92107
reportDeprecatedProps({node, name: componentName, prop, message, fixNode, fix});
93108
}
109+
110+
return !!deprecatedProp;
94111
}
95112

96113
function testAttributeForDeprecation(attribute, deprecatedPropList, componentName) {
114+
let wasFound = false;
97115
if (attribute.type === 'JSXAttribute') {
98-
checkPropDeprecation(attribute, attribute.name, attribute.name.name, deprecatedPropList, componentName);
116+
wasFound = checkPropDeprecation(attribute, attribute.name, attribute.name.name, deprecatedPropList, componentName);
99117
} else if (attribute.type === 'JSXSpreadAttribute') {
100118
const spreadSource = findValueNodeOfIdentifier(attribute.argument.name, context.getScope());
101119
if (spreadSource) {
102120
_.forEach(spreadSource.properties, property => {
103121
const key = _.get(property, 'key');
104122
const propName = _.get(property, 'key.name');
105-
checkPropDeprecation(key, key, propName, deprecatedPropList, componentName);
123+
wasFound = checkPropDeprecation(key, key, propName, deprecatedPropList, componentName);
106124
});
107125
}
108126
}
127+
return wasFound;
109128
}
110129

111130
function deprecationCheck(node) {
@@ -123,13 +142,29 @@ module.exports = {
123142
currentImport,
124143
deprecationSource
125144
);
126-
145+
127146
foundPossibleDeprecations.forEach(foundPossibleDeprecation => {
128-
const deprecatedPropList = foundPossibleDeprecation.props;
147+
const deprecatedPropList = [...foundPossibleDeprecation.props];
148+
const requiredPropList = _.remove(deprecatedPropList, p => !!p.isRequired);
129149
const attributes = node.attributes;
150+
151+
/* handle deprecated props */
152+
if (!_.isEmpty(deprecatedPropList)) {
153+
attributes.forEach(attribute => {
154+
testAttributeForDeprecation(attribute, deprecatedPropList, componentName);
155+
});
156+
}
157+
158+
/* handle required props */
159+
let foundAttribute = false;
130160
attributes.forEach(attribute => {
131-
testAttributeForDeprecation(attribute, deprecatedPropList, componentName);
161+
foundAttribute = foundAttribute || testAttributeForDeprecation(attribute, requiredPropList, componentName);
132162
});
163+
164+
if (!foundAttribute && requiredPropList[0]) {
165+
const prop = requiredPropList[0];
166+
reportRequiredProps({node, name: componentName, prop: prop.prop, message: prop.message})
167+
}
133168
});
134169
}
135170
}

eslint-rules/tests/component_prop_deprecation.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@
6868
"fix": {"propName": "subtitle"}
6969
}
7070
]
71+
},
72+
{
73+
"component": "Picker",
74+
"source": "module-with-deprecations",
75+
"props": [
76+
{
77+
"prop": "migrate",
78+
"message": "Please make sure to pass the 'migrate' prop.",
79+
"isRequired": true
80+
}
81+
]
7182
}
7283
]
7384

eslint-rules/tests/lib/rules/component-prop-deprecation.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ ruleTester.run('component-prop-deprecation', rule, {
8181
import {View as V} from 'module-with-deprecations';
8282
<Button text="my button"/>
8383
`
84-
}
84+
},
85+
{
86+
options: ruleOptions,
87+
code: `
88+
import {Picker} from 'module-with-deprecations';
89+
<Picker value="value" migrate={true}/>
90+
`
91+
},
8592
],
8693
invalid: [
8794
{
@@ -347,6 +354,13 @@ ruleTester.run('component-prop-deprecation', rule, {
347354
{message: "The 'Text' component's prop 't' is deprecated. Please use the 'title' prop instead."},
348355
{message: "The 'Text' component's prop 's' is deprecated. Please use the 'subtitle' prop instead."}
349356
]
357+
},
358+
{
359+
options: ruleOptions,
360+
code: 'import {Picker} from \'module-with-deprecations\'; <Picker t="title" s="subtitle"/>',
361+
errors: [
362+
{message: "The 'Picker' component's prop 'migrate' is required. Please make sure to pass the 'migrate' prop."},
363+
]
350364
}
351365
]
352366
});

0 commit comments

Comments
 (0)