UNPKG

eslint-plugin-vue

Version:

Official ESLint plugin for Vue.js

149 lines (146 loc) 5.63 kB
'use strict'; const require_runtime = require('../_virtual/_rolldown/runtime.js'); const require_index = require('../utils/index.js'); //#region lib/rules/require-default-prop.js /** * @fileoverview Require default value for props * @author Michał Sajnóg <msajnog93@gmail.com> (https://github.com/michalsnik) */ var require_require_default_prop = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => { /** * @typedef {import('../utils').ComponentProp} ComponentProp * @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp * @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp * @typedef {ComponentObjectProp & { value: ObjectExpression} } ComponentObjectPropObject */ const utils = require_index.default; const { isDef } = require_index.default; const NATIVE_TYPES = new Set([ "String", "Number", "Boolean", "Function", "Object", "Array", "Symbol" ]); /** * Detects whether given value node is a Boolean type * @param {Expression} value * @return {boolean} */ function isValueNodeOfBooleanType(value) { if (value.type === "Identifier" && value.name === "Boolean") return true; if (value.type === "ArrayExpression") { const elements = value.elements.filter(isDef); return elements.length === 1 && elements[0].type === "Identifier" && elements[0].name === "Boolean"; } return false; } module.exports = { meta: { type: "suggestion", docs: { description: "require default value for props", categories: ["vue3-strongly-recommended", "vue2-strongly-recommended"], url: "https://eslint.vuejs.org/rules/require-default-prop.html" }, fixable: null, schema: [], messages: { missingDefault: `Prop '{{propName}}' requires default value to be set.` } }, create(context) { /** * Checks if the passed prop is required * @param {ObjectExpression} propValue - ObjectExpression AST node for a single prop * @return {boolean} */ function propIsRequired(propValue) { const propRequiredNode = propValue.properties.find((p) => p.type === "Property" && utils.getStaticPropertyName(p) === "required" && p.value.type === "Literal" && p.value.value === true); return Boolean(propRequiredNode); } /** * Checks if the passed prop has a default value * @param {ObjectExpression} propValue - ObjectExpression AST node for a single prop * @return {boolean} */ function propHasDefault(propValue) { const propDefaultNode = propValue.properties.find((p) => p.type === "Property" && utils.getStaticPropertyName(p) === "default"); return Boolean(propDefaultNode); } /** * Checks whether the given props that don't have a default value * @param {ComponentObjectProp} prop Vue component's "props" node * @return {boolean} */ function isWithoutDefaultValue(prop) { if (prop.value.type !== "ObjectExpression") { if (prop.value.type === "Identifier") return NATIVE_TYPES.has(prop.value.name); if (prop.value.type === "CallExpression" || prop.value.type === "MemberExpression") return false; return true; } return !propIsRequired(prop.value) && !propHasDefault(prop.value); } /** * Detects whether given prop node is a Boolean * @param {ComponentObjectProp} prop * @return {Boolean} */ function isBooleanProp(prop) { const value = utils.skipTSAsExpression(prop.value); return isValueNodeOfBooleanType(value) || value.type === "ObjectExpression" && value.properties.some((p) => p.type === "Property" && p.key.type === "Identifier" && p.key.name === "type" && isValueNodeOfBooleanType(p.value)); } /** * @param {ComponentProp[]} props * @param {(prop: ComponentObjectProp|ComponentTypeProp)=>boolean} [ignore] */ function processProps(props, ignore) { for (const prop of props) if (prop.type === "object") { if (prop.node.shorthand) continue; if (!isWithoutDefaultValue(prop)) continue; if (isBooleanProp(prop)) continue; if (ignore?.(prop)) continue; const propName = prop.propName == null ? `[${context.sourceCode.getText(prop.node.key)}]` : prop.propName; context.report({ node: prop.node, messageId: `missingDefault`, data: { propName } }); } else if (prop.type === "type") { if (prop.required) continue; if (prop.types.length === 1 && prop.types[0] === "Boolean") continue; if (ignore?.(prop)) continue; context.report({ node: prop.node, messageId: `missingDefault`, data: { propName: prop.propName } }); } } return utils.compositingVisitors(utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(node, props) { const hasWithDefaults = utils.hasWithDefaults(node); const defaultsByWithDefaults = utils.getWithDefaultsPropExpressions(node); const isUsingPropsDestructure = utils.isUsingPropsDestructure(node); const defaultsByAssignmentPatterns = utils.getDefaultPropExpressionsForPropsDestructure(node); processProps(props, (prop) => { if (prop.type === "type") { if (!hasWithDefaults && !isUsingPropsDestructure) return true; if (defaultsByWithDefaults[prop.propName]) return true; } if (!isUsingPropsDestructure) return false; if (prop.propName == null) return true; return Boolean(defaultsByAssignmentPatterns[prop.propName]); }); } }), utils.executeOnVue(context, (obj) => { processProps(utils.getComponentPropsFromOptions(obj)); })); } }; })); //#endregion Object.defineProperty(exports, 'default', { enumerable: true, get: function () { return require_require_default_prop(); } });