UNPKG

3.4 kBJavaScriptView Raw
1/**
2 * @fileOverview Enforce a defaultProps definition for every prop that is not a required prop.
3 * @author Vitor Balocco
4 */
5
6'use strict';
7
8const Components = require('../util/Components');
9const docsUrl = require('../util/docsUrl');
10const astUtil = require('../util/ast');
11
12// ------------------------------------------------------------------------------
13// Rule Definition
14// ------------------------------------------------------------------------------
15
16module.exports = {
17 meta: {
18 docs: {
19 description: 'Enforce a defaultProps definition for every prop that is not a required prop.',
20 category: 'Best Practices',
21 url: docsUrl('require-default-props')
22 },
23
24 schema: [{
25 type: 'object',
26 properties: {
27 forbidDefaultForRequired: {
28 type: 'boolean'
29 },
30 ignoreFunctionalComponents: {
31 type: 'boolean'
32 }
33 },
34 additionalProperties: false
35 }]
36 },
37
38 create: Components.detect((context, components) => {
39 const configuration = context.options[0] || {};
40 const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false;
41 const ignoreFunctionalComponents = configuration.ignoreFunctionalComponents || false;
42
43 /**
44 * Reports all propTypes passed in that don't have a defaultProps counterpart.
45 * @param {Object[]} propTypes List of propTypes to check.
46 * @param {Object} defaultProps Object of defaultProps to check. Keys are the props names.
47 * @return {void}
48 */
49 function reportPropTypesWithoutDefault(propTypes, defaultProps) {
50 // If this defaultProps is "unresolved", then we should ignore this component and not report
51 // any errors for it, to avoid false-positives with e.g. external defaultProps declarations or spread operators.
52 if (defaultProps === 'unresolved') {
53 return;
54 }
55
56 Object.keys(propTypes).forEach((propName) => {
57 const prop = propTypes[propName];
58 if (prop.isRequired) {
59 if (forbidDefaultForRequired && defaultProps[propName]) {
60 context.report({
61 node: prop.node,
62 message: 'propType "{{name}}" is required and should not have a defaultProps declaration.',
63 data: {name: propName}
64 });
65 }
66 return;
67 }
68
69 if (defaultProps[propName]) {
70 return;
71 }
72
73 context.report({
74 node: prop.node,
75 message: 'propType "{{name}}" is not required, but has no corresponding defaultProps declaration.',
76 data: {name: propName}
77 });
78 });
79 }
80
81 // --------------------------------------------------------------------------
82 // Public API
83 // --------------------------------------------------------------------------
84
85 return {
86 'Program:exit'() {
87 const list = components.list();
88
89 Object.keys(list).filter((component) => {
90 if (ignoreFunctionalComponents
91 && (astUtil.isFunction(list[component].node) || astUtil.isFunctionLikeExpression(list[component].node))) {
92 return false;
93 }
94 return list[component].declaredPropTypes;
95 }).forEach((component) => {
96 reportPropTypesWithoutDefault(
97 list[component].declaredPropTypes,
98 list[component].defaultProps || {}
99 );
100 });
101 }
102 };
103 })
104};