no-required-prop-with-default.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /**
  2. * @author @neferqiqi
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. /**
  8. * @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
  9. * @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
  10. * @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
  11. * @typedef {import('../utils').ComponentUnknownProp} ComponentUnknownProp
  12. * @typedef {import('../utils').ComponentProp} ComponentProp
  13. */
  14. module.exports = {
  15. meta: {
  16. type: 'problem',
  17. docs: {
  18. description: 'enforce props with default values to be optional',
  19. categories: undefined,
  20. url: 'https://eslint.vuejs.org/rules/no-required-prop-with-default.html'
  21. },
  22. fixable: 'code',
  23. hasSuggestions: true,
  24. schema: [
  25. {
  26. type: 'object',
  27. properties: {
  28. autofix: {
  29. type: 'boolean'
  30. }
  31. },
  32. additionalProperties: false
  33. }
  34. ],
  35. messages: {
  36. requireOptional: `Prop "{{ key }}" should be optional.`,
  37. fixRequiredProp: `Change this prop to be optional.`
  38. }
  39. },
  40. /** @param {RuleContext} context */
  41. create(context) {
  42. let canAutoFix = false
  43. const option = context.options[0]
  44. if (option) {
  45. canAutoFix = option.autofix
  46. }
  47. /**
  48. * @param {ComponentProp} prop
  49. * @param {Set<string>} [defaultProps]
  50. **/
  51. const handleObjectProp = (prop, defaultProps) => {
  52. if (
  53. prop.type === 'object' &&
  54. prop.propName &&
  55. prop.value.type === 'ObjectExpression' &&
  56. (utils.findProperty(prop.value, 'default') ||
  57. defaultProps?.has(prop.propName))
  58. ) {
  59. const requiredProperty = utils.findProperty(prop.value, 'required')
  60. if (!requiredProperty) return
  61. const requiredNode = requiredProperty.value
  62. if (
  63. requiredNode &&
  64. requiredNode.type === 'Literal' &&
  65. !!requiredNode.value
  66. ) {
  67. context.report({
  68. node: prop.node,
  69. loc: prop.node.loc,
  70. data: {
  71. key: prop.propName
  72. },
  73. messageId: 'requireOptional',
  74. fix: canAutoFix
  75. ? (fixer) => fixer.replaceText(requiredNode, 'false')
  76. : null,
  77. suggest: canAutoFix
  78. ? null
  79. : [
  80. {
  81. messageId: 'fixRequiredProp',
  82. fix: (fixer) => fixer.replaceText(requiredNode, 'false')
  83. }
  84. ]
  85. })
  86. }
  87. } else if (
  88. prop.type === 'type' &&
  89. defaultProps?.has(prop.propName) &&
  90. prop.required
  91. ) {
  92. // skip setter & getter case
  93. if (
  94. prop.node.type === 'TSMethodSignature' &&
  95. (prop.node.kind === 'get' || prop.node.kind === 'set')
  96. ) {
  97. return
  98. }
  99. // skip computed
  100. if (prop.node.computed) {
  101. return
  102. }
  103. context.report({
  104. node: prop.node,
  105. loc: prop.node.loc,
  106. data: {
  107. key: prop.propName
  108. },
  109. messageId: 'requireOptional',
  110. fix: canAutoFix
  111. ? (fixer) => fixer.insertTextAfter(prop.key, '?')
  112. : null,
  113. suggest: canAutoFix
  114. ? null
  115. : [
  116. {
  117. messageId: 'fixRequiredProp',
  118. fix: (fixer) => fixer.insertTextAfter(prop.key, '?')
  119. }
  120. ]
  121. })
  122. }
  123. }
  124. return utils.compositingVisitors(
  125. utils.defineVueVisitor(context, {
  126. onVueObjectEnter(node) {
  127. utils
  128. .getComponentPropsFromOptions(node)
  129. .map((prop) => handleObjectProp(prop))
  130. }
  131. }),
  132. utils.defineScriptSetupVisitor(context, {
  133. onDefinePropsEnter(node, props) {
  134. const defaultProps = new Set([
  135. ...Object.keys(utils.getWithDefaultsPropExpressions(node)),
  136. ...Object.keys(
  137. utils.getDefaultPropExpressionsForPropsDestructure(node)
  138. )
  139. ])
  140. props.map((prop) => handleObjectProp(prop, defaultProps))
  141. }
  142. })
  143. )
  144. }
  145. }