Skip to content

Commit e8dc9c1

Browse files
author
Paweł Nowak
committed
Validate boolean prop names nested deeper
1 parent 35de81b commit e8dc9c1

File tree

2 files changed

+144
-16
lines changed

2 files changed

+144
-16
lines changed

lib/rules/boolean-prop-naming.js

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,64 @@ module.exports = {
104104
return node.key.name;
105105
}
106106

107+
/**
108+
* Checks if prop is declared in flow way
109+
* @param {Object} prop Property object, single prop type declaration
110+
* @returns {Boolean}
111+
*/
112+
function flowCheck(prop) {
113+
return (
114+
prop.type === 'ObjectTypeProperty' &&
115+
prop.value.type === 'BooleanTypeAnnotation' &&
116+
rule.test(getPropName(prop)) === false
117+
);
118+
}
119+
120+
/**
121+
* Checks if prop is declared in regular way
122+
* @param {Object} prop Property object, single prop type declaration
123+
* @returns {Boolean}
124+
*/
125+
function regularCheck(prop) {
126+
const propKey = getPropKey(prop);
127+
return (
128+
propKey &&
129+
propTypeNames.indexOf(propKey) >= 0 &&
130+
rule.test(getPropName(prop)) === false
131+
);
132+
}
133+
134+
/**
135+
* Checks if prop is nested
136+
* @param {Object} prop Property object, single prop type declaration
137+
* @returns {Boolean}
138+
*/
139+
function nestedPropTypes(prop) {
140+
return (
141+
prop.type === 'Property' &&
142+
prop.value.type === 'CallExpression'
143+
);
144+
}
145+
146+
/**
147+
* Runs recursive check on all proptypes
148+
* @param {Array} proptypes A list of Property object (for each proptype defined)
149+
* @param {Function} addInvalidProp callback to run for each error
150+
*/
151+
function runCheck(proptypes, addInvalidProp) {
152+
proptypes = proptypes || [];
153+
154+
proptypes.forEach(prop => {
155+
if (nestedPropTypes(prop)) {
156+
runCheck(prop.value.arguments[0].properties, addInvalidProp);
157+
return;
158+
}
159+
if (flowCheck(prop) || regularCheck(prop)) {
160+
addInvalidProp(prop);
161+
}
162+
});
163+
}
164+
107165
/**
108166
* Checks and mark props with invalid naming
109167
* @param {Object} node The component node we're testing
@@ -113,22 +171,8 @@ module.exports = {
113171
const component = components.get(node) || node;
114172
const invalidProps = component.invalidProps || [];
115173

116-
(proptypes || []).forEach(prop => {
117-
const propKey = getPropKey(prop);
118-
const flowCheck = (
119-
prop.type === 'ObjectTypeProperty' &&
120-
prop.value.type === 'BooleanTypeAnnotation' &&
121-
rule.test(getPropName(prop)) === false
122-
);
123-
const regularCheck = (
124-
propKey &&
125-
propTypeNames.indexOf(propKey) >= 0 &&
126-
rule.test(getPropName(prop)) === false
127-
);
128-
129-
if (flowCheck || regularCheck) {
130-
invalidProps.push(prop);
131-
}
174+
runCheck(proptypes, prop => {
175+
invalidProps.push(prop);
132176
});
133177

134178
components.set(node, {

tests/lib/rules/boolean-prop-naming.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,42 @@ ruleTester.run('boolean-prop-naming', rule, {
373373
rule: '^is[A-Z]([A-Za-z0-9]?)+'
374374
}],
375375
parser: 'babel-eslint'
376+
}, {
377+
code: `
378+
class Hello extends React.Component {
379+
render() {
380+
return (
381+
<div />
382+
);
383+
}
384+
}
385+
386+
Hello.propTypes = {
387+
isSomething: PropTypes.bool.isRequired,
388+
nested: PropTypes.shape({
389+
isWorking: PropTypes.bool
390+
})
391+
};
392+
`
393+
}, {
394+
code: `
395+
class Hello extends React.Component {
396+
render() {
397+
return (
398+
<div />
399+
);
400+
}
401+
}
402+
403+
Hello.propTypes = {
404+
isSomething: PropTypes.bool.isRequired,
405+
nested: PropTypes.shape({
406+
nested: PropTypes.shape({
407+
isWorking: PropTypes.bool
408+
})
409+
})
410+
};
411+
`
376412
}],
377413

378414
invalid: [{
@@ -807,5 +843,53 @@ ruleTester.run('boolean-prop-naming', rule, {
807843
errors: [{
808844
message: 'Prop name (something) doesn\'t match rule (^is[A-Z]([A-Za-z0-9]?)+)'
809845
}]
846+
}, {
847+
code: `
848+
class Hello extends React.Component {
849+
render() {
850+
return (
851+
<div />
852+
);
853+
}
854+
}
855+
856+
Hello.propTypes = {
857+
isSomething: PropTypes.bool.isRequired,
858+
nested: PropTypes.shape({
859+
failingItIs: PropTypes.bool
860+
})
861+
};
862+
`,
863+
options: [{
864+
rule: '^is[A-Z]([A-Za-z0-9]?)+'
865+
}],
866+
errors: [{
867+
message: 'Prop name (failingItIs) doesn\'t match rule (^is[A-Z]([A-Za-z0-9]?)+)'
868+
}]
869+
}, {
870+
code: `
871+
class Hello extends React.Component {
872+
render() {
873+
return (
874+
<div />
875+
);
876+
}
877+
}
878+
879+
Hello.propTypes = {
880+
isSomething: PropTypes.bool.isRequired,
881+
nested: PropTypes.shape({
882+
nested: PropTypes.shape({
883+
failingItIs: PropTypes.bool
884+
})
885+
})
886+
};
887+
`,
888+
options: [{
889+
rule: '^is[A-Z]([A-Za-z0-9]?)+'
890+
}],
891+
errors: [{
892+
message: 'Prop name (failingItIs) doesn\'t match rule (^is[A-Z]([A-Za-z0-9]?)+)'
893+
}]
810894
}]
811895
});

0 commit comments

Comments
 (0)