Skip to content

Commit ce9ba34

Browse files
committed
Make require-default-props use defaultProps detection
1 parent efb5bd7 commit ce9ba34

File tree

3 files changed

+26
-120
lines changed

3 files changed

+26
-120
lines changed

lib/rules/default-props-match-prop-types.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,9 @@ module.exports = {
264264
return;
265265
}
266266

267-
defaultProps.forEach(defaultProp => {
268-
const prop = propFromName(propTypes, defaultProp.name);
267+
Object.keys(defaultProps).forEach(defaultPropName => {
268+
const defaultProp = defaultProps[defaultPropName];
269+
const prop = propFromName(propTypes, defaultPropName);
269270

270271
if (prop && (allowRequiredDefaults || !prop.isRequired)) {
271272
return;
@@ -275,13 +276,13 @@ module.exports = {
275276
context.report(
276277
defaultProp.node,
277278
'defaultProp "{{name}}" defined for isRequired propType.',
278-
{name: defaultProp.name}
279+
{name: defaultPropName}
279280
);
280281
} else {
281282
context.report(
282283
defaultProp.node,
283284
'defaultProp "{{name}}" has no corresponding propTypes declaration.',
284-
{name: defaultProp.name}
285+
{name: defaultPropName}
285286
);
286287
}
287288
});

lib/rules/require-default-props.js

Lines changed: 11 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -175,36 +175,6 @@ module.exports = {
175175
});
176176
}
177177

178-
/**
179-
* Extracts a DefaultProp from an ObjectExpression node.
180-
* @param {ASTNode} objectExpression ObjectExpression node.
181-
* @returns {Object|string} Object representation of a defaultProp, to be consumed by
182-
* `addDefaultPropsToComponent`, or string "unresolved", if the defaultProps
183-
* from this ObjectExpression can't be resolved.
184-
*/
185-
function getDefaultPropsFromObjectExpression(objectExpression) {
186-
const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement');
187-
188-
if (hasSpread) {
189-
return 'unresolved';
190-
}
191-
192-
return objectExpression.properties.map(property => sourceCode.getText(property.key).replace(QUOTES_REGEX, ''));
193-
}
194-
195-
/**
196-
* Marks a component's DefaultProps declaration as "unresolved". A component's DefaultProps is
197-
* marked as "unresolved" if we cannot safely infer the values of its defaultProps declarations
198-
* without risking false negatives.
199-
* @param {Object} component The component to mark.
200-
* @returns {void}
201-
*/
202-
function markDefaultPropsAsUnresolved(component) {
203-
components.set(component.node, {
204-
defaultProps: 'unresolved'
205-
});
206-
}
207-
208178
/**
209179
* Adds propTypes to the component passed in.
210180
* @param {ASTNode} component The component to add the propTypes to.
@@ -219,35 +189,6 @@ module.exports = {
219189
});
220190
}
221191

222-
/**
223-
* Adds defaultProps to the component passed in.
224-
* @param {ASTNode} component The component to add the defaultProps to.
225-
* @param {String[]|String} defaultProps defaultProps to add to the component or the string "unresolved"
226-
* if this component has defaultProps that can't be resolved.
227-
* @returns {void}
228-
*/
229-
function addDefaultPropsToComponent(component, defaultProps) {
230-
// Early return if this component's defaultProps is already marked as "unresolved".
231-
if (component.defaultProps === 'unresolved') {
232-
return;
233-
}
234-
235-
if (defaultProps === 'unresolved') {
236-
markDefaultPropsAsUnresolved(component);
237-
return;
238-
}
239-
240-
const defaults = component.defaultProps || {};
241-
242-
defaultProps.forEach(defaultProp => {
243-
defaults[defaultProp] = true;
244-
});
245-
246-
components.set(component.node, {
247-
defaultProps: defaults
248-
});
249-
}
250-
251192
/**
252193
* Tries to find a props type annotation in a stateless component.
253194
* @param {ASTNode} node The AST node to look for a props type annotation.
@@ -369,9 +310,8 @@ module.exports = {
369310
return {
370311
MemberExpression: function(node) {
371312
const isPropType = propsUtil.isPropTypesDeclaration(node);
372-
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node);
373313

374-
if (!isPropType && !isDefaultProp) {
314+
if (!isPropType) {
375315
return;
376316
}
377317

@@ -393,39 +333,21 @@ module.exports = {
393333
if (node.parent.type === 'AssignmentExpression') {
394334
const expression = resolveNodeValue(node.parent.right);
395335
if (!expression || expression.type !== 'ObjectExpression') {
396-
// If a value can't be found, we mark the defaultProps declaration as "unresolved", because
397-
// we should ignore this component and not report any errors for it, to avoid false-positives
398-
// with e.g. external defaultProps declarations.
399-
if (isDefaultProp) {
400-
markDefaultPropsAsUnresolved(component);
401-
}
402-
403336
return;
404337
}
405338

406-
if (isPropType) {
407-
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
408-
} else {
409-
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
410-
}
411-
339+
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
412340
return;
413341
}
414342

415343
// e.g.:
416344
// MyComponent.propTypes.baz = PropTypes.string;
417345
if (node.parent.type === 'MemberExpression' && node.parent.parent.type === 'AssignmentExpression') {
418-
if (isPropType) {
419-
addPropTypesToComponent(component, [{
420-
name: node.parent.property.name,
421-
isRequired: propsUtil.isRequiredPropType(node.parent.parent.right),
422-
node: node.parent.parent
423-
}]);
424-
} else {
425-
addDefaultPropsToComponent(component, [node.parent.property.name]);
426-
}
427-
428-
return;
346+
addPropTypesToComponent(component, [{
347+
name: node.parent.property.name,
348+
isRequired: propsUtil.isRequiredPropType(node.parent.parent.right),
349+
node: node.parent.parent
350+
}]);
429351
}
430352
},
431353

@@ -451,9 +373,8 @@ module.exports = {
451373
}
452374

453375
const isPropType = propsUtil.isPropTypesDeclaration(node);
454-
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node);
455376

456-
if (!isPropType && !isDefaultProp) {
377+
if (!isPropType) {
457378
return;
458379
}
459380

@@ -473,11 +394,7 @@ module.exports = {
473394
return;
474395
}
475396

476-
if (isPropType) {
477-
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
478-
} else {
479-
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
480-
}
397+
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
481398
},
482399

483400
// e.g.:
@@ -507,9 +424,8 @@ module.exports = {
507424
}
508425

509426
const isPropType = astUtil.getPropertyName(node) === 'propTypes';
510-
const isDefaultProp = astUtil.getPropertyName(node) === 'defaultProps' || astUtil.getPropertyName(node) === 'getDefaultProps';
511427

512-
if (!isPropType && !isDefaultProp) {
428+
if (!isPropType) {
513429
return;
514430
}
515431

@@ -524,11 +440,7 @@ module.exports = {
524440
return;
525441
}
526442

527-
if (isPropType) {
528-
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
529-
} else {
530-
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
531-
}
443+
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
532444
},
533445

534446
// e.g.:
@@ -559,25 +471,11 @@ module.exports = {
559471
}
560472

561473
const isPropType = propsUtil.isPropTypesDeclaration(property);
562-
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(property);
563-
564-
if (!isPropType && !isDefaultProp) {
565-
return;
566-
}
567474

568475
if (isPropType && property.value.type === 'ObjectExpression') {
569476
addPropTypesToComponent(component, getPropTypesFromObjectExpression(property.value));
570477
return;
571478
}
572-
573-
if (isDefaultProp && property.value.type === 'FunctionExpression') {
574-
const returnStatement = utils.findReturnStatement(property);
575-
if (!returnStatement || returnStatement.argument.type !== 'ObjectExpression') {
576-
return;
577-
}
578-
579-
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(returnStatement.argument));
580-
}
581479
});
582480
},
583481

lib/util/defaultProps.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ const astUtil = require('./ast');
77
const propsUtil = require('./props');
88
const variableUtil = require('./variable');
99

10+
const QUOTES_REGEX = /^["']|["']$/g;
11+
1012
module.exports = function defaultPropsInstructions(context, components, utils) {
13+
const sourceCode = context.getSourceCode();
1114
const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []);
1215

1316
/**
@@ -45,7 +48,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) {
4548
}
4649

4750
return objectExpression.properties.map(defaultProp => ({
48-
name: defaultProp.key.name,
51+
name: sourceCode.getText(defaultProp.key).replace(QUOTES_REGEX, ''),
4952
node: defaultProp
5053
}));
5154
}
@@ -81,10 +84,14 @@ module.exports = function defaultPropsInstructions(context, components, utils) {
8184
return;
8285
}
8386

84-
const defaults = component.defaultProps || [];
87+
const defaults = component.defaultProps || {};
88+
const newDefaultProps = defaultProps.reduce((acc, prop) => {
89+
acc[prop.name] = prop;
90+
return acc;
91+
}, Object.assign({}, defaults));
8592

8693
components.set(component.node, {
87-
defaultProps: defaults.concat(defaultProps)
94+
defaultProps: newDefaultProps
8895
});
8996
}
9097

0 commit comments

Comments
 (0)