Skip to content

Commit b588947

Browse files
committed
Make require-default-props use defaultProps detection
1 parent 4744f92 commit b588947

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
@@ -265,8 +265,9 @@ module.exports = {
265265
return;
266266
}
267267

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

271272
if (prop && (allowRequiredDefaults || !prop.isRequired)) {
272273
return;
@@ -276,13 +277,13 @@ module.exports = {
276277
context.report(
277278
defaultProp.node,
278279
'defaultProp "{{name}}" defined for isRequired propType.',
279-
{name: defaultProp.name}
280+
{name: defaultPropName}
280281
);
281282
} else {
282283
context.report(
283284
defaultProp.node,
284285
'defaultProp "{{name}}" has no corresponding propTypes declaration.',
285-
{name: defaultProp.name}
286+
{name: defaultPropName}
286287
);
287288
}
288289
});

lib/rules/require-default-props.js

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

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

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

375-
if (!isPropType && !isDefaultProp) {
315+
if (!isPropType) {
376316
return;
377317
}
378318

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

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

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

@@ -452,9 +374,8 @@ module.exports = {
452374
}
453375

454376
const isPropType = propsUtil.isPropTypesDeclaration(node);
455-
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node);
456377

457-
if (!isPropType && !isDefaultProp) {
378+
if (!isPropType) {
458379
return;
459380
}
460381

@@ -474,11 +395,7 @@ module.exports = {
474395
return;
475396
}
476397

477-
if (isPropType) {
478-
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
479-
} else {
480-
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
481-
}
398+
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
482399
},
483400

484401
// e.g.:
@@ -508,9 +425,8 @@ module.exports = {
508425
}
509426

510427
const isPropType = astUtil.getPropertyName(node) === 'propTypes';
511-
const isDefaultProp = astUtil.getPropertyName(node) === 'defaultProps' || astUtil.getPropertyName(node) === 'getDefaultProps';
512428

513-
if (!isPropType && !isDefaultProp) {
429+
if (!isPropType) {
514430
return;
515431
}
516432

@@ -525,11 +441,7 @@ module.exports = {
525441
return;
526442
}
527443

528-
if (isPropType) {
529-
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
530-
} else {
531-
addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression));
532-
}
444+
addPropTypesToComponent(component, getPropTypesFromObjectExpression(expression));
533445
},
534446

535447
// e.g.:
@@ -560,25 +472,11 @@ module.exports = {
560472
}
561473

562474
const isPropType = propsUtil.isPropTypesDeclaration(property);
563-
const isDefaultProp = propsUtil.isDefaultPropsDeclaration(property);
564-
565-
if (!isPropType && !isDefaultProp) {
566-
return;
567-
}
568475

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

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)