eslint-plugin-vue
Version:
Official ESLint plugin for Vue.js
149 lines (146 loc) • 5.63 kB
JavaScript
;
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();
}
});