| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 | /** * @fileoverview Enforces props default values to be valid. * @author Armano */'use strict'const utils = require('../utils')const NATIVE_TYPES = new Set([  'String',  'Number',  'Boolean',  'Function',  'Object',  'Array',  'Symbol'])// ------------------------------------------------------------------------------// Rule Definition// ------------------------------------------------------------------------------module.exports = {  meta: {    docs: {      description: 'enforce props default values to be valid',      category: 'essential',      url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.1/docs/rules/require-valid-default-prop.md'    },    fixable: null,    schema: []  },  create (context) {    // ----------------------------------------------------------------------    // Helpers    // ----------------------------------------------------------------------    function isPropertyIdentifier (node) {      return node.type === 'Property' && node.key.type === 'Identifier'    }    function getPropertyNode (obj, name) {      return obj.properties.find(p =>        isPropertyIdentifier(p) &&        p.key.name === name      )    }    function getTypes (node) {      if (node.type === 'Identifier') {        return [node.name]      } else if (node.type === 'ArrayExpression') {        return node.elements          .filter(item => item.type === 'Identifier')          .map(item => item.name)      }      return []    }    function ucFirst (text) {      return text[0].toUpperCase() + text.slice(1)    }    function getValueType (node) {      if (node.type === 'CallExpression') { // Symbol(), Number() ...        if (node.callee.type === 'Identifier' && NATIVE_TYPES.has(node.callee.name)) {          return node.callee.name        }      } else if (node.type === 'TemplateLiteral') { // String        return 'String'      } else if (node.type === 'Literal') { // String, Boolean, Number        if (node.value === null) return null        const type = ucFirst(typeof node.value)        if (NATIVE_TYPES.has(type)) {          return type        }      } else if (node.type === 'ArrayExpression') { // Array        return 'Array'      } else if (node.type === 'ObjectExpression') { // Object        return 'Object'      }      // FunctionExpression, ArrowFunctionExpression      return null    }    // ----------------------------------------------------------------------    // Public    // ----------------------------------------------------------------------    return utils.executeOnVue(context, obj => {      const props = obj.properties.find(p =>        isPropertyIdentifier(p) &&        p.key.name === 'props' &&        p.value.type === 'ObjectExpression'      )      if (!props) return      const properties = props.value.properties.filter(p =>        isPropertyIdentifier(p) &&        p.value.type === 'ObjectExpression'      )      for (const prop of properties) {        const type = getPropertyNode(prop.value, 'type')        if (!type) continue        const typeNames = new Set(getTypes(type.value)          .map(item => item === 'Object' || item === 'Array' ? 'Function' : item) // Object and Array require function          .filter(item => NATIVE_TYPES.has(item)))        // There is no native types detected        if (typeNames.size === 0) continue        const def = getPropertyNode(prop.value, 'default')        if (!def) continue        const defType = getValueType(def.value)        if (!defType || typeNames.has(defType)) continue        context.report({          node: def,          message: "Type of the default value for '{{name}}' prop must be a {{types}}.",          data: {            name: prop.key.name,            types: Array.from(typeNames).join(' or ').toLowerCase()          }        })      }    })  }}
 |