Skip to content

Commit 4abebc3

Browse files
brettdhljharb
authored andcommitted
Detect import and require of PropTypes
(rebased)
1 parent 0e1009d commit 4abebc3

File tree

2 files changed

+188
-56
lines changed

2 files changed

+188
-56
lines changed

lib/rules/no-typos.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ module.exports = {
3636
},
3737

3838
create: Components.detect((context, components, utils) => {
39+
let propTypesPackageName;
40+
3941
function checkValidPropTypeQualfier(node) {
4042
if (node.name !== 'isRequired') {
4143
context.report({
@@ -56,14 +58,28 @@ module.exports = {
5658

5759
/* eslint-disable no-use-before-define */
5860
function checkValidProp(node) {
59-
if (node && node.type === 'MemberExpression' && node.object.type === 'MemberExpression') {
61+
if (!propTypesPackageName || !node) {
62+
return;
63+
}
64+
65+
if (
66+
node.type === 'MemberExpression' &&
67+
node.object.type === 'MemberExpression' &&
68+
node.object.object.name === propTypesPackageName
69+
) { // PropTypes.myProp.isRequired
6070
checkValidPropType(node.object.property);
6171
checkValidPropTypeQualfier(node.property);
62-
} else if (node && node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.property.name !== 'isRequired') {
72+
} else if (
73+
node.type === 'MemberExpression' &&
74+
node.object.type === 'Identifier' &&
75+
node.object.name === propTypesPackageName &&
76+
node.property.name !== 'isRequired'
77+
) { // PropTypes.myProp
6378
checkValidPropType(node.property);
64-
} else if (node && (
65-
node.type === 'MemberExpression' && node.object.type === 'CallExpression' || node.type === 'CallExpression'
66-
)) {
79+
} else if (
80+
(node.type === 'MemberExpression' && node.object.type === 'CallExpression') ||
81+
node.type === 'CallExpression'
82+
) { // Shapes
6783
if (node.type === 'MemberExpression') {
6884
checkValidPropTypeQualfier(node.property);
6985
node = node.object;
@@ -113,6 +129,18 @@ module.exports = {
113129
}
114130

115131
return {
132+
ImportDeclaration: function(node) {
133+
if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
134+
propTypesPackageName = node.specifiers[0].local.name;
135+
}
136+
},
137+
138+
CallExpression: function(node) {
139+
if (node.callee.name === 'require' && node.arguments[0].value === 'prop-types') {
140+
propTypesPackageName = node.parent.id.name;
141+
}
142+
},
143+
116144
ClassProperty: function(node) {
117145
if (!node.static || !utils.isES6Component(node.parent.parent)) {
118146
return;

tests/lib/rules/no-typos.js

Lines changed: 155 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -260,81 +260,134 @@ ruleTester.run('no-typos', rule, {
260260
parser: 'babel-eslint',
261261
parserOptions: parserOptions
262262
}, {
263-
code: `class Component extends React.Component {};
264-
Component.propTypes = {
265-
a: PropTypes.number.isRequired
266-
}
263+
code: `
264+
import PropTypes from "prop-types";
265+
class Component extends React.Component {};
266+
Component.propTypes = {
267+
a: PropTypes.number.isRequired
268+
}
267269
`,
268270
parser: 'babel-eslint',
269271
parserOptions: parserOptions
270272
}, {
271-
code: `class Component extends React.Component {};
272-
Component.propTypes = {
273-
e: PropTypes.shape({
274-
ea: PropTypes.string,
275-
})
276-
}
273+
code: `
274+
import PropTypes from "prop-types";
275+
class Component extends React.Component {};
276+
Component.propTypes = {
277+
e: PropTypes.shape({
278+
ea: PropTypes.string,
279+
})
280+
}
277281
`,
278282
parser: 'babel-eslint',
279283
parserOptions: parserOptions
280284
}, {
281-
code: `class Component extends React.Component {};
282-
Component.propTypes = {
283-
a: PropTypes.string,
284-
b: PropTypes.string.isRequired,
285-
c: PropTypes.shape({
286-
d: PropTypes.string,
287-
e: PropTypes.number.isRequired,
288-
}).isRequired
289-
}
285+
code: `
286+
import PropTypes from "prop-types";
287+
class Component extends React.Component {};
288+
Component.propTypes = {
289+
a: PropTypes.string,
290+
b: PropTypes.string.isRequired,
291+
c: PropTypes.shape({
292+
d: PropTypes.string,
293+
e: PropTypes.number.isRequired,
294+
}).isRequired
295+
}
290296
`,
291297
parser: 'babel-eslint',
292298
parserOptions: parserOptions
293299
}, {
294-
code: `class Component extends React.Component {};
295-
Component.propTypes = {
296-
a: PropTypes.oneOfType([
297-
PropTypes.string,
298-
PropTypes.number
299-
])
300-
}
300+
code: `
301+
import PropTypes from "prop-types";
302+
class Component extends React.Component {};
303+
Component.propTypes = {
304+
a: PropTypes.oneOfType([
305+
PropTypes.string,
306+
PropTypes.number
307+
])
308+
}
301309
`,
302310
parser: 'babel-eslint',
303311
parserOptions: parserOptions
304312
}, {
305-
code: `class Component extends React.Component {};
306-
Component.propTypes = {
307-
a: PropTypes.oneOf([
308-
'hello',
309-
'hi'
310-
])
311-
}
313+
code: `
314+
import PropTypes from "prop-types";
315+
class Component extends React.Component {};
316+
Component.propTypes = {
317+
a: PropTypes.oneOf([
318+
'hello',
319+
'hi'
320+
])
321+
}
312322
`,
313323
parser: 'babel-eslint',
314324
parserOptions: parserOptions
315325
}, {
316-
code: `class Component extends React.Component {};
317-
Component.childContextTypes = {
318-
a: PropTypes.string,
319-
b: PropTypes.string.isRequired,
320-
c: PropTypes.shape({
321-
d: PropTypes.string,
322-
e: PropTypes.number.isRequired,
323-
}).isRequired
324-
}
326+
code: `
327+
import PropTypes from "prop-types";
328+
class Component extends React.Component {};
329+
Component.childContextTypes = {
330+
a: PropTypes.string,
331+
b: PropTypes.string.isRequired,
332+
c: PropTypes.shape({
333+
d: PropTypes.string,
334+
e: PropTypes.number.isRequired,
335+
}).isRequired
336+
}
325337
`,
326338
parser: 'babel-eslint',
327339
parserOptions: parserOptions
328340
}, {
329-
code: `class Component extends React.Component {};
330-
Component.contextTypes = {
331-
a: PropTypes.string,
332-
b: PropTypes.string.isRequired,
333-
c: PropTypes.shape({
334-
d: PropTypes.string,
335-
e: PropTypes.number.isRequired,
336-
}).isRequired
337-
}
341+
code: `
342+
import PropTypes from "prop-types";
343+
class Component extends React.Component {};
344+
Component.contextTypes = {
345+
a: PropTypes.string,
346+
b: PropTypes.string.isRequired,
347+
c: PropTypes.shape({
348+
d: PropTypes.string,
349+
e: PropTypes.number.isRequired,
350+
}).isRequired
351+
}
352+
`,
353+
parser: 'babel-eslint',
354+
parserOptions: parserOptions
355+
}, {
356+
code: `
357+
import PropTypes from 'prop-types'
358+
import * as MyPropTypes from 'lib/my-prop-types'
359+
class Component extends React.Component {};
360+
Component.propTypes = {
361+
a: PropTypes.string,
362+
b: MyPropTypes.MYSTRING,
363+
c: MyPropTypes.MYSTRING.isRequired,
364+
}
365+
`,
366+
parser: 'babel-eslint',
367+
parserOptions: parserOptions
368+
}, {
369+
code: `
370+
const PropTypes = require('prop-types');
371+
const MyPropTypes = require('lib/my-prop-types')
372+
class Component extends React.Component {};
373+
Component.propTypes = {
374+
a: PropTypes.string,
375+
b: MyPropTypes.MYSTRING,
376+
c: MyPropTypes.MYSTRING.isRequired,
377+
}
378+
`,
379+
parser: 'babel-eslint',
380+
parserOptions: parserOptions
381+
}, {
382+
code: `
383+
const RealPropTypes = require('prop-types');
384+
const MyPropTypes = require('lib/my-prop-types')
385+
class Component extends React.Component {};
386+
Component.propTypes = {
387+
a: RealPropTypes.string,
388+
b: MyPropTypes.MYSTRING,
389+
c: MyPropTypes.MYSTRING.isRequired,
390+
}
338391
`,
339392
parser: 'babel-eslint',
340393
parserOptions: parserOptions
@@ -710,6 +763,7 @@ ruleTester.run('no-typos', rule, {
710763
}]
711764
}, {
712765
code: `
766+
import PropTypes from "prop-types";
713767
class Component extends React.Component {};
714768
Component.propTypes = {
715769
a: PropTypes.Number.isRequired
@@ -722,6 +776,7 @@ ruleTester.run('no-typos', rule, {
722776
}]
723777
}, {
724778
code: `
779+
import PropTypes from "prop-types";
725780
class Component extends React.Component {};
726781
Component.propTypes = {
727782
a: PropTypes.number.isrequired
@@ -734,6 +789,7 @@ ruleTester.run('no-typos', rule, {
734789
}]
735790
}, {
736791
code: `
792+
import PropTypes from "prop-types";
737793
class Component extends React.Component {};
738794
Component.propTypes = {
739795
a: PropTypes.Number
@@ -746,6 +802,7 @@ ruleTester.run('no-typos', rule, {
746802
}]
747803
}, {
748804
code: `
805+
import PropTypes from "prop-types";
749806
class Component extends React.Component {};
750807
Component.propTypes = {
751808
a: PropTypes.shape({
@@ -761,6 +818,7 @@ ruleTester.run('no-typos', rule, {
761818
}]
762819
}, {
763820
code: `
821+
import PropTypes from "prop-types";
764822
class Component extends React.Component {};
765823
Component.propTypes = {
766824
a: PropTypes.oneOfType([
@@ -776,6 +834,7 @@ ruleTester.run('no-typos', rule, {
776834
}]
777835
}, {
778836
code: `
837+
import PropTypes from "prop-types";
779838
class Component extends React.Component {};
780839
Component.propTypes = {
781840
a: PropTypes.bools,
@@ -797,6 +856,29 @@ ruleTester.run('no-typos', rule, {
797856
}]
798857
}, {
799858
code: `
859+
import PropTypes from "prop-types";
860+
class Component extends React.Component {};
861+
Component.childContextTypes = {
862+
a: PropTypes.bools,
863+
b: PropTypes.Array,
864+
c: PropTypes.function,
865+
d: PropTypes.objectof,
866+
}
867+
`,
868+
parser: 'babel-eslint',
869+
parserOptions: parserOptions,
870+
errors: [{
871+
message: 'Typo in declared prop type: bools'
872+
}, {
873+
message: 'Typo in declared prop type: Array'
874+
}, {
875+
message: 'Typo in declared prop type: function'
876+
}, {
877+
message: 'Typo in declared prop type: objectof'
878+
}]
879+
}, {
880+
code: `
881+
const PropTypes = require('prop-types');
800882
class Component extends React.Component {};
801883
Component.childContextTypes = {
802884
a: PropTypes.bools,
@@ -847,5 +929,27 @@ ruleTester.run('no-typos', rule, {
847929
}, {
848930
message: 'Typo in prop type chain qualifier: isrequired'
849931
}]
932+
}, {
933+
code: `
934+
const RealPropTypes = require('prop-types');
935+
class Component extends React.Component {};
936+
Component.childContextTypes = {
937+
a: RealPropTypes.bools,
938+
b: RealPropTypes.Array,
939+
c: RealPropTypes.function,
940+
d: RealPropTypes.objectof,
941+
}
942+
`,
943+
parser: 'babel-eslint',
944+
parserOptions: parserOptions,
945+
errors: [{
946+
message: 'Typo in declared prop type: bools'
947+
}, {
948+
message: 'Typo in declared prop type: Array'
949+
}, {
950+
message: 'Typo in declared prop type: function'
951+
}, {
952+
message: 'Typo in declared prop type: objectof'
953+
}]
850954
}]
851955
});

0 commit comments

Comments
 (0)