require-default-prop.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /**
  2. * @fileoverview Require default value for props
  3. * @author Michał Sajnóg <msajnog93@gmail.com> (https://github.com/michalsnik)
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. const NATIVE_TYPES = new Set([
  8. 'String',
  9. 'Number',
  10. 'Boolean',
  11. 'Function',
  12. 'Object',
  13. 'Array',
  14. 'Symbol'
  15. ])
  16. // ------------------------------------------------------------------------------
  17. // Rule Definition
  18. // ------------------------------------------------------------------------------
  19. module.exports = {
  20. meta: {
  21. type: 'suggestion',
  22. docs: {
  23. description: 'require default value for props',
  24. category: 'strongly-recommended',
  25. url: 'https://eslint.vuejs.org/rules/require-default-prop.html'
  26. },
  27. fixable: null, // or "code" or "whitespace"
  28. schema: []
  29. },
  30. create (context) {
  31. // ----------------------------------------------------------------------
  32. // Helpers
  33. // ----------------------------------------------------------------------
  34. /**
  35. * Checks if the passed prop is required
  36. * @param {Property} prop - Property AST node for a single prop
  37. * @return {boolean}
  38. */
  39. function propIsRequired (prop) {
  40. const propRequiredNode = prop.value.properties
  41. .find(p =>
  42. p.type === 'Property' &&
  43. p.key.name === 'required' &&
  44. p.value.type === 'Literal' &&
  45. p.value.value === true
  46. )
  47. return Boolean(propRequiredNode)
  48. }
  49. /**
  50. * Checks if the passed prop has a default value
  51. * @param {Property} prop - Property AST node for a single prop
  52. * @return {boolean}
  53. */
  54. function propHasDefault (prop) {
  55. const propDefaultNode = prop.value.properties
  56. .find(p =>
  57. p.key &&
  58. (p.key.name === 'default' || p.key.value === 'default')
  59. )
  60. return Boolean(propDefaultNode)
  61. }
  62. /**
  63. * Finds all props that don't have a default value set
  64. * @param {Array} props - Vue component's "props" node
  65. * @return {Array} Array of props without "default" value
  66. */
  67. function findPropsWithoutDefaultValue (props) {
  68. return props
  69. .filter(prop => {
  70. if (prop.value.type !== 'ObjectExpression') {
  71. return (prop.value.type !== 'CallExpression' && prop.value.type !== 'Identifier') || NATIVE_TYPES.has(prop.value.name)
  72. }
  73. return !propIsRequired(prop) && !propHasDefault(prop)
  74. })
  75. }
  76. /**
  77. * Detects whether given value node is a Boolean type
  78. * @param {Node} value
  79. * @return {Boolean}
  80. */
  81. function isValueNodeOfBooleanType (value) {
  82. return (
  83. value.type === 'Identifier' &&
  84. value.name === 'Boolean'
  85. ) || (
  86. value.type === 'ArrayExpression' &&
  87. value.elements.length === 1 &&
  88. value.elements[0].type === 'Identifier' &&
  89. value.elements[0].name === 'Boolean'
  90. )
  91. }
  92. /**
  93. * Detects whether given prop node is a Boolean
  94. * @param {Node} prop
  95. * @return {Boolean}
  96. */
  97. function isBooleanProp (prop) {
  98. const value = utils.unwrapTypes(prop.value)
  99. return isValueNodeOfBooleanType(value) || (
  100. value.type === 'ObjectExpression' &&
  101. value.properties.find(p =>
  102. p.key.type === 'Identifier' &&
  103. p.key.name === 'type' &&
  104. isValueNodeOfBooleanType(p.value)
  105. )
  106. )
  107. }
  108. /**
  109. * Excludes purely Boolean props from the Array
  110. * @param {Array} props - Array with props
  111. * @return {Array}
  112. */
  113. function excludeBooleanProps (props) {
  114. return props.filter(prop => !isBooleanProp(prop))
  115. }
  116. // ----------------------------------------------------------------------
  117. // Public
  118. // ----------------------------------------------------------------------
  119. return utils.executeOnVue(context, (obj) => {
  120. const props = utils.getComponentProps(obj)
  121. .filter(prop => prop.key && prop.value && !prop.node.shorthand)
  122. const propsWithoutDefault = findPropsWithoutDefaultValue(props)
  123. const propsToReport = excludeBooleanProps(propsWithoutDefault)
  124. for (const prop of propsToReport) {
  125. const propName = prop.propName != null ? prop.propName : `[${context.getSourceCode().getText(prop.key)}]`
  126. context.report({
  127. node: prop.node,
  128. message: `Prop '{{propName}}' requires default value to be set.`,
  129. data: {
  130. propName
  131. }
  132. })
  133. }
  134. })
  135. }
  136. }