source: imaps-frontend/node_modules/eslint-plugin-react/lib/util/defaultProps.js

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/**
2 * @fileoverview Common defaultProps detection functionality.
3 */
4
5'use strict';
6
7const fromEntries = require('object.fromentries');
8const astUtil = require('./ast');
9const componentUtil = require('./componentUtil');
10const propsUtil = require('./props');
11const variableUtil = require('./variable');
12const propWrapperUtil = require('./propWrapper');
13const getText = require('./eslint').getText;
14
15const QUOTES_REGEX = /^["']|["']$/g;
16
17module.exports = function defaultPropsInstructions(context, components, utils) {
18 /**
19 * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not
20 * an Identifier, then the node is simply returned.
21 * @param {ASTNode} node The node to resolve.
22 * @returns {ASTNode|null} Return null if the value could not be resolved, ASTNode otherwise.
23 */
24 function resolveNodeValue(node) {
25 if (node.type === 'Identifier') {
26 return variableUtil.findVariableByName(context, node, node.name);
27 }
28 if (
29 node.type === 'CallExpression'
30 && propWrapperUtil.isPropWrapperFunction(context, node.callee.name)
31 && node.arguments && node.arguments[0]
32 ) {
33 return resolveNodeValue(node.arguments[0]);
34 }
35 return node;
36 }
37
38 /**
39 * Extracts a DefaultProp from an ObjectExpression node.
40 * @param {ASTNode} objectExpression ObjectExpression node.
41 * @returns {Object|string} Object representation of a defaultProp, to be consumed by
42 * `addDefaultPropsToComponent`, or string "unresolved", if the defaultProps
43 * from this ObjectExpression can't be resolved.
44 */
45 function getDefaultPropsFromObjectExpression(objectExpression) {
46 const hasSpread = objectExpression.properties.find((property) => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement');
47
48 if (hasSpread) {
49 return 'unresolved';
50 }
51
52 return objectExpression.properties.map((defaultProp) => ({
53 name: getText(context, defaultProp.key).replace(QUOTES_REGEX, ''),
54 node: defaultProp,
55 }));
56 }
57
58 /**
59 * Marks a component's DefaultProps declaration as "unresolved". A component's DefaultProps is
60 * marked as "unresolved" if we cannot safely infer the values of its defaultProps declarations
61 * without risking false negatives.
62 * @param {Object} component The component to mark.
63 * @returns {void}
64 */
65 function markDefaultPropsAsUnresolved(component) {
66 components.set(component.node, {
67 defaultProps: 'unresolved',
68 });
69 }
70
71 /**
72 * Adds defaultProps to the component passed in.
73 * @param {ASTNode} component The component to add the defaultProps to.
74 * @param {Object[]|'unresolved'} defaultProps defaultProps to add to the component or the string "unresolved"
75 * if this component has defaultProps that can't be resolved.
76 * @returns {void}
77 */
78 function addDefaultPropsToComponent(component, defaultProps) {
79 // Early return if this component's defaultProps is already marked as "unresolved".
80 if (component.defaultProps === 'unresolved') {
81 return;
82 }
83
84 if (defaultProps === 'unresolved') {
85 markDefaultPropsAsUnresolved(component);
86 return;
87 }
88
89 const defaults = component.defaultProps || {};
90 const newDefaultProps = Object.assign(
91 {},
92 defaults,
93 fromEntries(defaultProps.map((prop) => [prop.name, prop]))
94 );
95
96 components.set(component.node, {
97 defaultProps: newDefaultProps,
98 });
99 }
100
101 return {
102 MemberExpression(node) {
103 const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node);
104
105 if (!isDefaultProp) {
106 return;
107 }
108
109 // find component this defaultProps belongs to
110 const component = utils.getRelatedComponent(node);
111 if (!component) {
112 return;
113 }
114
115 // e.g.:
116 // MyComponent.propTypes = {
117 // foo: React.PropTypes.string.isRequired,
118 // bar: React.PropTypes.string
119 // };
120 //
121 // or:
122 //
123 // MyComponent.propTypes = myPropTypes;
124 if (node.parent.type === 'AssignmentExpression') {
125 const expression = resolveNodeValue(node.parent.right);
126 if (!expression || expression.type !== 'ObjectExpression') {
127 // If a value can't be found, we mark the defaultProps declaration as "unresolved", because
128 // we should ignore this component and not report any errors for it, to avoid false-positives
129 // with e.g. external defaultProps declarations.
130 if (isDefaultProp) {
131 markDefaultPropsAsUnresolved(component);
132 }
133
134 return;
135 }
136
137 addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
138
139 return;
140 }
141
142 // e.g.:
143 // MyComponent.propTypes.baz = React.PropTypes.string;
144 if (node.parent.type === 'MemberExpression' && node.parent.parent
145 && node.parent.parent.type === 'AssignmentExpression') {
146 addDefaultPropsToComponent(component, [{
147 name: node.parent.property.name,
148 node: node.parent.parent,
149 }]);
150 }
151 },
152
153 // e.g.:
154 // class Hello extends React.Component {
155 // static get defaultProps() {
156 // return {
157 // name: 'Dean'
158 // };
159 // }
160 // render() {
161 // return <div>Hello {this.props.name}</div>;
162 // }
163 // }
164 MethodDefinition(node) {
165 if (!node.static || node.kind !== 'get') {
166 return;
167 }
168
169 if (!propsUtil.isDefaultPropsDeclaration(node)) {
170 return;
171 }
172
173 // find component this propTypes/defaultProps belongs to
174 const component = components.get(componentUtil.getParentES6Component(context, node));
175 if (!component) {
176 return;
177 }
178
179 const returnStatement = utils.findReturnStatement(node);
180 if (!returnStatement) {
181 return;
182 }
183
184 const expression = resolveNodeValue(returnStatement.argument);
185 if (!expression || expression.type !== 'ObjectExpression') {
186 return;
187 }
188
189 addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
190 },
191
192 // e.g.:
193 // class Greeting extends React.Component {
194 // render() {
195 // return (
196 // <h1>Hello, {this.props.foo} {this.props.bar}</h1>
197 // );
198 // }
199 // static defaultProps = {
200 // foo: 'bar',
201 // bar: 'baz'
202 // };
203 // }
204 'ClassProperty, PropertyDefinition'(node) {
205 if (!(node.static && node.value)) {
206 return;
207 }
208
209 const propName = astUtil.getPropertyName(node);
210 const isDefaultProp = propName === 'defaultProps' || propName === 'getDefaultProps';
211
212 if (!isDefaultProp) {
213 return;
214 }
215
216 // find component this propTypes/defaultProps belongs to
217 const component = components.get(componentUtil.getParentES6Component(context, node));
218 if (!component) {
219 return;
220 }
221
222 const expression = resolveNodeValue(node.value);
223 if (!expression || expression.type !== 'ObjectExpression') {
224 return;
225 }
226
227 addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
228 },
229
230 // e.g.:
231 // React.createClass({
232 // render: function() {
233 // return <div>{this.props.foo}</div>;
234 // },
235 // getDefaultProps: function() {
236 // return {
237 // foo: 'default'
238 // };
239 // }
240 // });
241 ObjectExpression(node) {
242 // find component this propTypes/defaultProps belongs to
243 const component = componentUtil.isES5Component(node, context) && components.get(node);
244 if (!component) {
245 return;
246 }
247
248 // Search for the proptypes declaration
249 node.properties.forEach((property) => {
250 if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
251 return;
252 }
253
254 const isDefaultProp = propsUtil.isDefaultPropsDeclaration(property);
255
256 if (isDefaultProp && property.value.type === 'FunctionExpression') {
257 const returnStatement = utils.findReturnStatement(property);
258 if (!returnStatement || returnStatement.argument.type !== 'ObjectExpression') {
259 return;
260 }
261
262 addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(returnStatement.argument));
263 }
264 });
265 },
266 };
267};
Note: See TracBrowser for help on using the repository browser.