Skip to content

Commit bf675a9

Browse files
committed
Made prop-types pass
1 parent 44b51b3 commit bf675a9

File tree

5 files changed

+132
-19
lines changed

5 files changed

+132
-19
lines changed

lib/rules/no-unused-prop-types.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ module.exports = {
5555
create: Components.detect((context, components, utils) => {
5656
const sourceCode = context.getSourceCode();
5757
const checkAsyncSafeLifeCycles = versionUtil.testReactVersion(context, '16.3.0');
58-
58+
const defaults = {skipShapeProps: true, customValidators: []};
59+
const configuration = Object.assign({}, defaults, context.options[0] || {});
5960
const UNUSED_MESSAGE = '\'{{name}}\' PropType is defined but prop is never used';
6061

6162
/**
@@ -454,13 +455,18 @@ module.exports = {
454455
return;
455456
}
456457

457-
Object.values(props || {}).forEach(prop => {
458+
Object.keys(props || {}).forEach(key => {
459+
const prop = props[key];
458460
// Skip props that check instances
459461
if (prop === true) {
460462
return;
461463
}
462464

463-
if (prop.node && !isPropUsed(component, prop)) {
465+
if (prop.type === 'shape' && configuration.skipShapeProps) {
466+
return;
467+
}
468+
469+
if (prop.node && !isPropUsed(component, prop, key)) {
464470
context.report(
465471
prop.node,
466472
UNUSED_MESSAGE, {

lib/rules/prop-types.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ module.exports = {
175175
function _isDeclaredInComponent(declaredPropTypes, keyList) {
176176
for (let i = 0, j = keyList.length; i < j; i++) {
177177
const key = keyList[i];
178-
179178
const propType = (
180179
declaredPropTypes && (
181180
// Check if this key is declared
@@ -188,7 +187,8 @@ module.exports = {
188187
// If it's a computed property, we can't make any further analysis, but is valid
189188
return key === '__COMPUTED_PROP__';
190189
}
191-
if (typeof propType === 'object' && Object.keys(propType).length === 0) {
190+
191+
if (typeof propType === 'object' && !propType.type) {
192192
return true;
193193
}
194194
// Consider every children as declared

lib/util/propTypes.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,9 @@ module.exports = {
7070
// Used to track the type annotations in scope.
7171
// Necessary because babel's scopes do not track type annotations.
7272
const classExpressions = [];
73-
const defaults = {skipShapeProps: true, customValidators: []};
73+
const defaults = {customValidators: []};
7474
const configuration = Object.assign({}, defaults, context.options[0] || {});
7575
const customValidators = configuration.customValidators;
76-
const skipShapeProps = configuration.skipShapeProps;
7776
const sourceCode = context.getSourceCode();
7877
const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []);
7978

@@ -149,9 +148,6 @@ module.exports = {
149148
}
150149
return {};
151150
case 'ObjectTypeAnnotation':
152-
if (skipShapeProps) {
153-
return {};
154-
}
155151
let containsObjectTypeSpread = false;
156152
const shapeTypeDefinition = {
157153
type: 'shape',
@@ -183,13 +179,14 @@ module.exports = {
183179
for (let i = 0, j = annotation.types.length; i < j; i++) {
184180
const type = buildTypeAnnotationDeclarationTypes(annotation.types[i], parentName, seen);
185181
// keep only complex type
186-
if (Object.keys(type).length > 0) {
182+
if (type.type) {
187183
if (type.children === true) {
188184
// every child is accepted for one type, abort type analysis
189185
unionTypeDefinition.children = true;
190186
return unionTypeDefinition;
191187
}
192188
}
189+
// type.node = annotation.types[i];
193190

194191
unionTypeDefinition.children.push(type);
195192
}
@@ -200,10 +197,14 @@ module.exports = {
200197
return unionTypeDefinition;
201198
case 'ArrayTypeAnnotation':
202199
const fullName = [parentName, '*'].join('.');
200+
const child = buildTypeAnnotationDeclarationTypes(annotation.elementType, fullName, seen);
201+
child.fullName = fullName;
202+
child.name = '__ANY_KEY__';
203+
child.node = annotation;
203204
return {
204205
type: 'object',
205206
children: {
206-
__ANY_KEY__: buildTypeAnnotationDeclarationTypes(annotation.elementType, fullName, seen)
207+
__ANY_KEY__: child
207208
}
208209
};
209210
default:
@@ -316,10 +317,6 @@ module.exports = {
316317
const argument = value.arguments[0];
317318
switch (callName) {
318319
case 'shape':
319-
if (skipShapeProps) {
320-
return {};
321-
}
322-
323320
if (argument.type !== 'ObjectExpression') {
324321
// Invalid proptype or cannot analyse statically
325322
return {};
@@ -365,7 +362,7 @@ module.exports = {
365362
for (let i = 0, j = argument.elements.length; i < j; i++) {
366363
const type = buildReactDeclarationTypes(argument.elements[i], parentName);
367364
// keep only complex type
368-
if (Object.keys(type).length > 0) {
365+
if (type.type) {
369366
if (type.children === true) {
370367
// every child is accepted for one type, abort type analysis
371368
unionTypeDefinition.children = true;
@@ -417,6 +414,7 @@ module.exports = {
417414
case 'ObjectExpression':
418415
iterateProperties(context, propTypes.properties, (key, value) => {
419416
if (!value) {
417+
/* eslint-disable */
420418
ignorePropsValidation = true;
421419
return;
422420
}
@@ -453,12 +451,27 @@ module.exports = {
453451
propTypes.parent.right,
454452
propTypes.parent.left.object.property.name
455453
);
454+
456455
types.name = propTypes.property.name;
457-
types.node = propTypes.property;
458456
types.fullName = propTypes.property.name;
457+
types.node = propTypes.property;
459458
curDeclaredPropTypes[propTypes.property.name] = types;
460459
} else {
461-
ignorePropsValidation = true;
460+
let isUsedInPropTypes = false;
461+
let n = propTypes;
462+
while (n) {
463+
if (n.type === 'AssignmentExpression' && propsUtil.isPropTypesDeclaration(n.left) ||
464+
(n.type === 'ClassProperty' || n.type === 'Property') && propsUtil.isPropTypesDeclaration(n)) {
465+
// Found a propType used inside of another propType. This is not considered usage, we'll still validate
466+
// this component.
467+
isUsedInPropTypes = true;
468+
break;
469+
}
470+
n = n.parent;
471+
}
472+
if (!isUsedInPropTypes) {
473+
ignorePropsValidation = true;
474+
}
462475
}
463476
break;
464477
case 'Identifier':

tests/lib/rules/no-unused-prop-types.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4327,6 +4327,60 @@ ruleTester.run('no-unused-prop-types', rule, {
43274327
}, {
43284328
message: '\'prop2.*\' PropType is defined but prop is never used'
43294329
}]
4330+
}, {
4331+
code: [
4332+
'class Comp1 extends Component {',
4333+
' render() {',
4334+
' return <span />;',
4335+
' }',
4336+
'}',
4337+
'Comp1.propTypes = {',
4338+
' prop1: PropTypes.number',
4339+
'};',
4340+
'class Comp2 extends Component {',
4341+
' static propTypes = {',
4342+
' prop2: PropTypes.arrayOf(Comp1.propTypes.prop1)',
4343+
' }',
4344+
' render() {',
4345+
' return <span />;',
4346+
' }',
4347+
'}'
4348+
].join('\n'),
4349+
parser: 'babel-eslint',
4350+
errors: [{
4351+
message: '\'prop1\' PropType is defined but prop is never used'
4352+
}, {
4353+
message: '\'prop2\' PropType is defined but prop is never used'
4354+
}, {
4355+
message: '\'prop2.*\' PropType is defined but prop is never used'
4356+
}]
4357+
}, {
4358+
code: [
4359+
'class Comp1 extends Component {',
4360+
' render() {',
4361+
' return <span />;',
4362+
' }',
4363+
'}',
4364+
'Comp1.propTypes = {',
4365+
' prop1: PropTypes.number',
4366+
'};',
4367+
'var Comp2 = createReactClass({',
4368+
' propTypes: {',
4369+
' prop2: PropTypes.arrayOf(Comp1.propTypes.prop1)',
4370+
' },',
4371+
' render() {',
4372+
' return <span />;',
4373+
' }',
4374+
'});'
4375+
].join('\n'),
4376+
parser: 'babel-eslint',
4377+
errors: [{
4378+
message: '\'prop1\' PropType is defined but prop is never used'
4379+
}, {
4380+
message: '\'prop2\' PropType is defined but prop is never used'
4381+
}, {
4382+
message: '\'prop2.*\' PropType is defined but prop is never used'
4383+
}]
43304384
}, {
43314385
// Destructured assignment with Shape propTypes with skipShapeProps off issue #816
43324386
code: [

tests/lib/rules/prop-types.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,46 @@ ruleTester.run('prop-types', rule, {
586586
'};'
587587
].join('\n'),
588588
parser: 'babel-eslint'
589+
}, {
590+
code: [
591+
'class Comp1 extends Component {',
592+
' render() {',
593+
' return <span />;',
594+
' }',
595+
'}',
596+
'Comp1.propTypes = {',
597+
' prop1: PropTypes.number',
598+
'};',
599+
'class Comp2 extends Component {',
600+
' static propTypes = {',
601+
' prop2: PropTypes.arrayOf(Comp1.propTypes.prop1)',
602+
' }',
603+
' render() {',
604+
' return <span />;',
605+
' }',
606+
'}'
607+
].join('\n'),
608+
parser: 'babel-eslint'
609+
}, {
610+
code: [
611+
'class Comp1 extends Component {',
612+
' render() {',
613+
' return <span />;',
614+
' }',
615+
'}',
616+
'Comp1.propTypes = {',
617+
' prop1: PropTypes.number',
618+
'};',
619+
'var Comp2 = createReactClass({',
620+
' propTypes: {',
621+
' prop2: PropTypes.arrayOf(Comp1.propTypes.prop1)',
622+
' },',
623+
' render() {',
624+
' return <span />;',
625+
' }',
626+
'});'
627+
].join('\n'),
628+
parser: 'babel-eslint'
589629
}, {
590630
code: [
591631
'const SomeComponent = createReactClass({',

0 commit comments

Comments
 (0)