no-boolean-default.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /**
  2. * @fileoverview Prevents boolean defaults from being set
  3. * @author Hiroki Osame
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. /**
  8. * @typedef {import('../utils').ComponentProp} ComponentProp
  9. */
  10. /**
  11. * @param {Property | SpreadElement} prop
  12. */
  13. function isBooleanProp(prop) {
  14. return (
  15. prop.type === 'Property' &&
  16. prop.key.type === 'Identifier' &&
  17. prop.key.name === 'type' &&
  18. prop.value.type === 'Identifier' &&
  19. prop.value.name === 'Boolean'
  20. )
  21. }
  22. /**
  23. * @param {ObjectExpression} propDefValue
  24. */
  25. function getDefaultNode(propDefValue) {
  26. return utils.findProperty(propDefValue, 'default')
  27. }
  28. module.exports = {
  29. meta: {
  30. type: 'suggestion',
  31. docs: {
  32. description: 'disallow boolean defaults',
  33. categories: undefined,
  34. url: 'https://eslint.vuejs.org/rules/no-boolean-default.html'
  35. },
  36. fixable: 'code',
  37. schema: [
  38. {
  39. enum: ['default-false', 'no-default']
  40. }
  41. ]
  42. },
  43. /** @param {RuleContext} context */
  44. create(context) {
  45. const booleanType = context.options[0] || 'no-default'
  46. /**
  47. * @param {ComponentProp} prop
  48. * @param { { [key: string]: Expression | undefined } } [withDefaultsExpressions]
  49. */
  50. function processProp(prop, withDefaultsExpressions) {
  51. if (prop.type === 'object') {
  52. if (prop.value.type !== 'ObjectExpression') {
  53. return
  54. }
  55. if (!prop.value.properties.some(isBooleanProp)) {
  56. return
  57. }
  58. const defaultNode = getDefaultNode(prop.value)
  59. if (!defaultNode) {
  60. return
  61. }
  62. verifyDefaultExpression(defaultNode.value)
  63. } else if (prop.type === 'type') {
  64. if (prop.types.length !== 1 || prop.types[0] !== 'Boolean') {
  65. return
  66. }
  67. const defaultNode =
  68. withDefaultsExpressions && withDefaultsExpressions[prop.propName]
  69. if (!defaultNode) {
  70. return
  71. }
  72. verifyDefaultExpression(defaultNode)
  73. }
  74. }
  75. /**
  76. * @param {ComponentProp[]} props
  77. * @param { { [key: string]: Expression | undefined } } [withDefaultsExpressions]
  78. */
  79. function processProps(props, withDefaultsExpressions) {
  80. for (const prop of props) {
  81. processProp(prop, withDefaultsExpressions)
  82. }
  83. }
  84. /**
  85. * @param {Expression} defaultNode
  86. */
  87. function verifyDefaultExpression(defaultNode) {
  88. switch (booleanType) {
  89. case 'no-default':
  90. context.report({
  91. node: defaultNode,
  92. message:
  93. 'Boolean prop should not set a default (Vue defaults it to false).'
  94. })
  95. break
  96. case 'default-false':
  97. if (defaultNode.type !== 'Literal' || defaultNode.value !== false) {
  98. context.report({
  99. node: defaultNode,
  100. message: 'Boolean prop should only be defaulted to false.'
  101. })
  102. }
  103. break
  104. }
  105. }
  106. return utils.compositingVisitors(
  107. utils.executeOnVueComponent(context, (obj) => {
  108. processProps(utils.getComponentPropsFromOptions(obj))
  109. }),
  110. utils.defineScriptSetupVisitor(context, {
  111. onDefinePropsEnter(node, props) {
  112. processProps(props, utils.getWithDefaultsPropExpressions(node))
  113. }
  114. })
  115. )
  116. }
  117. }