source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/require-default-props.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 7.2 KB
Line 
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 entries = require('object.entries');
9const values = require('object.values');
10const Components = require('../util/Components');
11const docsUrl = require('../util/docsUrl');
12const astUtil = require('../util/ast');
13const report = require('../util/report');
14
15// ------------------------------------------------------------------------------
16// Rule Definition
17// ------------------------------------------------------------------------------
18
19const messages = {
20 noDefaultWithRequired: 'propType "{{name}}" is required and should not have a defaultProps declaration.',
21 shouldHaveDefault: 'propType "{{name}}" is not required, but has no corresponding defaultProps declaration.',
22 noDefaultPropsWithFunction: 'Don’t use defaultProps with function components.',
23 shouldAssignObjectDefault: 'propType "{{name}}" is not required, but has no corresponding default argument value.',
24 destructureInSignature: 'Must destructure props in the function signature to initialize an optional prop.',
25};
26
27function isPropWithNoDefaulVal(prop) {
28 if (prop.type === 'RestElement' || prop.type === 'ExperimentalRestProperty') {
29 return false;
30 }
31 return prop.value.type !== 'AssignmentPattern';
32}
33
34/** @type {import('eslint').Rule.RuleModule} */
35module.exports = {
36 meta: {
37 docs: {
38 description: 'Enforce a defaultProps definition for every prop that is not a required prop',
39 category: 'Best Practices',
40 url: docsUrl('require-default-props'),
41 },
42
43 messages,
44
45 schema: [{
46 type: 'object',
47 properties: {
48 forbidDefaultForRequired: {
49 type: 'boolean',
50 },
51 classes: {
52 enum: ['defaultProps', 'ignore'],
53 },
54 functions: {
55 enum: ['defaultArguments', 'defaultProps', 'ignore'],
56 },
57 /**
58 * @deprecated
59 */
60 ignoreFunctionalComponents: {
61 type: 'boolean',
62 },
63 },
64 additionalProperties: false,
65 }],
66 },
67
68 create: Components.detect((context, components) => {
69 const configuration = context.options[0] || {};
70 const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false;
71 const classes = configuration.classes || 'defaultProps';
72 /**
73 * @todo
74 * - Remove ignoreFunctionalComponents
75 * - Change default to 'defaultArguments'
76 */
77 const functions = configuration.ignoreFunctionalComponents
78 ? 'ignore'
79 : configuration.functions || 'defaultProps';
80
81 /**
82 * Reports all propTypes passed in that don't have a defaultProps counterpart.
83 * @param {Object[]} propTypes List of propTypes to check.
84 * @param {Object} defaultProps Object of defaultProps to check. Keys are the props names.
85 * @return {void}
86 */
87 function reportPropTypesWithoutDefault(propTypes, defaultProps) {
88 entries(propTypes).forEach((propType) => {
89 const propName = propType[0];
90 const prop = propType[1];
91
92 if (!prop.node) {
93 return;
94 }
95 if (prop.isRequired) {
96 if (forbidDefaultForRequired && defaultProps[propName]) {
97 report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', {
98 node: prop.node,
99 data: { name: propName },
100 });
101 }
102 return;
103 }
104
105 if (defaultProps[propName]) {
106 return;
107 }
108
109 report(context, messages.shouldHaveDefault, 'shouldHaveDefault', {
110 node: prop.node,
111 data: { name: propName },
112 });
113 });
114 }
115
116 /**
117 * If functions option is 'defaultArguments', reports defaultProps is used and all params that doesn't initialized.
118 * @param {Object} componentNode Node of component.
119 * @param {Object[]} declaredPropTypes List of propTypes to check `isRequired`.
120 * @param {Object} defaultProps Object of defaultProps to check used.
121 */
122 function reportFunctionComponent(componentNode, declaredPropTypes, defaultProps) {
123 if (defaultProps) {
124 report(context, messages.noDefaultPropsWithFunction, 'noDefaultPropsWithFunction', {
125 node: componentNode,
126 });
127 }
128
129 const props = componentNode.params[0];
130 const propTypes = declaredPropTypes;
131
132 if (!props) {
133 return;
134 }
135
136 if (props.type === 'Identifier') {
137 const hasOptionalProp = values(propTypes).some((propType) => !propType.isRequired);
138 if (hasOptionalProp) {
139 report(context, messages.destructureInSignature, 'destructureInSignature', {
140 node: props,
141 });
142 }
143 } else if (props.type === 'ObjectPattern') {
144 // Filter required props with default value and report error
145 props.properties.filter((prop) => {
146 const propName = prop && prop.key && prop.key.name;
147 const isPropRequired = propTypes[propName] && propTypes[propName].isRequired;
148 return propTypes[propName] && isPropRequired && !isPropWithNoDefaulVal(prop);
149 }).forEach((prop) => {
150 report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', {
151 node: prop,
152 data: { name: prop.key.name },
153 });
154 });
155
156 // Filter non required props with no default value and report error
157 props.properties.filter((prop) => {
158 const propName = prop && prop.key && prop.key.name;
159 const isPropRequired = propTypes[propName] && propTypes[propName].isRequired;
160 return propTypes[propName] && !isPropRequired && isPropWithNoDefaulVal(prop);
161 }).forEach((prop) => {
162 report(context, messages.shouldAssignObjectDefault, 'shouldAssignObjectDefault', {
163 node: prop,
164 data: { name: prop.key.name },
165 });
166 });
167 }
168 }
169
170 // --------------------------------------------------------------------------
171 // Public API
172 // --------------------------------------------------------------------------
173
174 return {
175 'Program:exit'() {
176 const list = components.list();
177
178 values(list).filter((component) => {
179 if (functions === 'ignore' && astUtil.isFunctionLike(component.node)) {
180 return false;
181 }
182 if (classes === 'ignore' && astUtil.isClass(component.node)) {
183 return false;
184 }
185
186 // If this defaultProps is "unresolved", then we should ignore this component and not report
187 // any errors for it, to avoid false-positives with e.g. external defaultProps declarations or spread operators.
188 if (component.defaultProps === 'unresolved') {
189 return false;
190 }
191 return component.declaredPropTypes !== undefined;
192 }).forEach((component) => {
193 if (functions === 'defaultArguments' && astUtil.isFunctionLike(component.node)) {
194 reportFunctionComponent(
195 component.node,
196 component.declaredPropTypes,
197 component.defaultProps
198 );
199 } else {
200 reportPropTypesWithoutDefault(
201 component.declaredPropTypes,
202 component.defaultProps || {}
203 );
204 }
205 });
206 },
207 };
208 }),
209};
Note: See TracBrowser for help on using the repository browser.