defaultPropsHandler.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import getPropertyName from '../utils/getPropertyName.js';
  2. import getMemberValuePath from '../utils/getMemberValuePath.js';
  3. import printValue from '../utils/printValue.js';
  4. import resolveToValue from '../utils/resolveToValue.js';
  5. import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue.js';
  6. import isReactComponentClass from '../utils/isReactComponentClass.js';
  7. import isReactForwardRefCall from '../utils/isReactForwardRefCall.js';
  8. function getDefaultValue(path) {
  9. let defaultValue;
  10. let resolvedPath = path;
  11. let valuePath = path;
  12. if (path.isBooleanLiteral()) {
  13. defaultValue = `${path.node.value}`;
  14. }
  15. else if (path.isNullLiteral()) {
  16. defaultValue = 'null';
  17. }
  18. else if (path.isLiteral()) {
  19. defaultValue = path.node.extra?.raw;
  20. }
  21. else {
  22. if (path.isAssignmentPattern()) {
  23. resolvedPath = resolveToValue(path.get('right'));
  24. }
  25. else {
  26. resolvedPath = resolveToValue(path);
  27. }
  28. if (resolvedPath.parentPath?.isImportDeclaration() && path.isIdentifier()) {
  29. defaultValue = path.node.name;
  30. }
  31. else {
  32. valuePath = resolvedPath;
  33. defaultValue = printValue(resolvedPath);
  34. }
  35. }
  36. if (typeof defaultValue !== 'undefined') {
  37. return {
  38. value: defaultValue,
  39. computed: valuePath.isCallExpression() ||
  40. valuePath.isMemberExpression() ||
  41. valuePath.isIdentifier(),
  42. };
  43. }
  44. return null;
  45. }
  46. function getStatelessPropsPath(componentDefinition) {
  47. let value = componentDefinition;
  48. if (isReactForwardRefCall(componentDefinition)) {
  49. value = resolveToValue(componentDefinition.get('arguments')[0]);
  50. }
  51. if (!value.isFunction()) {
  52. return;
  53. }
  54. return value.get('params')[0];
  55. }
  56. function getDefaultPropsPath(componentDefinition) {
  57. let defaultPropsPath = getMemberValuePath(componentDefinition, 'defaultProps');
  58. if (!defaultPropsPath) {
  59. return null;
  60. }
  61. defaultPropsPath = resolveToValue(defaultPropsPath);
  62. if (!defaultPropsPath) {
  63. return null;
  64. }
  65. if (defaultPropsPath.isFunctionExpression() ||
  66. defaultPropsPath.isFunctionDeclaration() ||
  67. defaultPropsPath.isClassMethod() ||
  68. defaultPropsPath.isObjectMethod()) {
  69. // Find the value that is returned from the function and process it if it is
  70. // an object literal.
  71. const returnValue = resolveFunctionDefinitionToReturnValue(defaultPropsPath);
  72. if (returnValue && returnValue.isObjectExpression()) {
  73. defaultPropsPath = returnValue;
  74. }
  75. }
  76. return defaultPropsPath;
  77. }
  78. function getDefaultValuesFromProps(properties, documentation, isStateless) {
  79. properties.forEach((propertyPath) => {
  80. if (propertyPath.isObjectProperty()) {
  81. const propName = getPropertyName(propertyPath);
  82. if (!propName)
  83. return;
  84. let valuePath = propertyPath.get('value');
  85. if (isStateless) {
  86. if (valuePath.isAssignmentPattern()) {
  87. valuePath = valuePath.get('right');
  88. }
  89. else {
  90. // Don't evaluate property if component is functional and the node is not an AssignmentPattern
  91. return;
  92. }
  93. }
  94. // Initialize the prop descriptor here after the early return from above
  95. const propDescriptor = documentation.getPropDescriptor(propName);
  96. const defaultValue = getDefaultValue(valuePath);
  97. if (defaultValue) {
  98. propDescriptor.defaultValue = defaultValue;
  99. }
  100. }
  101. else if (propertyPath.isSpreadElement()) {
  102. const resolvedValuePath = resolveToValue(propertyPath.get('argument'));
  103. if (resolvedValuePath.isObjectExpression()) {
  104. getDefaultValuesFromProps(resolvedValuePath.get('properties'), documentation, isStateless);
  105. }
  106. }
  107. });
  108. }
  109. const defaultPropsHandler = function (documentation, componentDefinition) {
  110. let statelessProps;
  111. const defaultPropsPath = getDefaultPropsPath(componentDefinition);
  112. /**
  113. * function, lazy, memo, forwardRef etc components can resolve default props as well
  114. */
  115. if (!isReactComponentClass(componentDefinition)) {
  116. statelessProps = getStatelessPropsPath(componentDefinition);
  117. }
  118. // Do both statelessProps and defaultProps if both are available so defaultProps can override
  119. if (statelessProps && statelessProps.isObjectPattern()) {
  120. getDefaultValuesFromProps(statelessProps.get('properties'), documentation, true);
  121. }
  122. if (defaultPropsPath && defaultPropsPath.isObjectExpression()) {
  123. getDefaultValuesFromProps(defaultPropsPath.get('properties'), documentation, false);
  124. }
  125. };
  126. export default defaultPropsHandler;