'use strict'; const require_runtime = require('../_virtual/_rolldown/runtime.js'); const require_index = require('../utils/index.js'); //#region lib/rules/valid-v-slot.js /** * @author Toru Nagashima * See LICENSE file in root directory for full license. */ var require_valid_v_slot = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => { const utils = require_index.default; /** * @typedef { { expr: VForExpression, variables: VVariable[] } } VSlotVForVariables */ /** * Get all `v-slot` directives on a given element. * @param {VElement} node The VElement node to check. * @returns {VDirective[]} The array of `v-slot` directives. */ function getSlotDirectivesOnElement(node) { return utils.getDirectives(node, "slot"); } /** * Get all `v-slot` directives on the children of a given element. * @param {VElement} node The VElement node to check. * @returns {VDirective[][]} * The array of the group of `v-slot` directives. * The group bundles `v-slot` directives of element sequence which is connected * by `v-if`/`v-else-if`/`v-else`. */ function getSlotDirectivesOnChildren(node) { /** @type {VDirective[][]} */ const groups = []; for (const group of utils.iterateChildElementsChains(node)) { const slotDirs = group.map((childElement) => childElement.name === "template" ? utils.getDirective(childElement, "slot") : null).filter(utils.isDef); if (slotDirs.length > 0) groups.push(slotDirs); } return groups; } /** * Get the normalized name of a given `v-slot` directive node with modifiers after `v-slot:` directive. * @param {VDirective} node The `v-slot` directive node. * @param {SourceCode} sourceCode The source code. * @returns {string} The normalized name. */ function getNormalizedName(node, sourceCode) { if (node.key.argument == null) return "default"; return node.key.modifiers.length === 0 ? sourceCode.getText(node.key.argument) : sourceCode.text.slice(node.key.argument.range[0], node.key.range[1]); } /** * Get all `v-slot` directives which are distributed to the same slot as a given `v-slot` directive node. * @param {VDirective[][]} vSlotGroups The result of `getAllNamedSlotElements()`. * @param {VDirective} currentVSlot The current `v-slot` directive node. * @param {VSlotVForVariables | null} currentVSlotVForVars The current `v-for` variables. * @param {SourceCode} sourceCode The source code. * @param {ParserServices.TokenStore} tokenStore The token store. * @returns {VDirective[][]} The array of the group of `v-slot` directives. */ function filterSameSlot(vSlotGroups, currentVSlot, currentVSlotVForVars, sourceCode, tokenStore) { const currentName = getNormalizedName(currentVSlot, sourceCode); return vSlotGroups.map((vSlots) => vSlots.filter((vSlot) => { if (getNormalizedName(vSlot, sourceCode) !== currentName) return false; const vForExpr = getVSlotVForVariableIfUsingIterationVars(vSlot, utils.getDirective(vSlot.parent.parent, "for")); if (!currentVSlotVForVars || !vForExpr) return !currentVSlotVForVars && !vForExpr; if (!equalVSlotVForVariables(currentVSlotVForVars, vForExpr, tokenStore)) return false; return true; })).filter((slots) => slots.length > 0); } /** * Determines whether the two given `v-slot` variables are considered to be equal. * @param {VSlotVForVariables} a First element. * @param {VSlotVForVariables} b Second element. * @param {ParserServices.TokenStore} tokenStore The token store. * @returns {boolean} `true` if the elements are considered to be equal. */ function equalVSlotVForVariables(a, b, tokenStore) { if (a.variables.length !== b.variables.length) return false; if (!equal(a.expr.right, b.expr.right)) return false; const checkedVarNames = /* @__PURE__ */ new Set(); const len = Math.min(a.expr.left.length, b.expr.left.length); for (let index = 0; index < len; index++) { const aPtn = a.expr.left[index]; const bPtn = b.expr.left[index]; const aVar = a.variables.find((v) => aPtn.range[0] <= v.id.range[0] && v.id.range[1] <= aPtn.range[1]); const bVar = b.variables.find((v) => bPtn.range[0] <= v.id.range[0] && v.id.range[1] <= bPtn.range[1]); if (aVar && bVar) { if (aVar.id.name !== bVar.id.name) return false; if (!equal(aPtn, bPtn)) return false; checkedVarNames.add(aVar.id.name); } else if (aVar || bVar) return false; } return a.variables.every((v) => checkedVarNames.has(v.id.name) || b.variables.some((bv) => v.id.name === bv.id.name)); /** * Determines whether the two given nodes are considered to be equal. * @param {ASTNode} a First node. * @param {ASTNode} b Second node. * @returns {boolean} `true` if the nodes are considered to be equal. */ function equal(a, b) { if (a.type !== b.type) return false; return utils.equalTokens(a, b, tokenStore); } } /** * Gets the `v-for` directive and variable that provide the variables used by the given` v-slot` directive. * @param {VDirective} vSlot The current `v-slot` directive node. * @param {VDirective | null} [vFor] The current `v-for` directive node. * @returns { VSlotVForVariables | null } The VSlotVForVariable. */ function getVSlotVForVariableIfUsingIterationVars(vSlot, vFor) { const expr = vFor && vFor.value && vFor.value.expression; const variables = expr && getUsingIterationVars(vSlot.key.argument, vSlot.parent.parent); return expr && variables && variables.length > 0 ? { expr, variables } : null; } /** * Gets iterative variables if a given argument node is using iterative variables that the element defined. * @param {VExpressionContainer|VIdentifier|null} argument The argument node to check. * @param {VElement} element The element node which has the argument. * @returns {VVariable[]} The argument node is using iteration variables. */ function getUsingIterationVars(argument, element) { const vars = []; if (argument && argument.type === "VExpressionContainer") { for (const { variable } of argument.references) if (variable != null && variable.kind === "v-for" && variable.id.range[0] > element.startTag.range[0] && variable.id.range[1] < element.startTag.range[1]) vars.push(variable); } return vars; } /** * Check whether a given argument node is using an scope variable that the directive defined. * @param {VDirective} vSlot The `v-slot` directive to check. * @returns {boolean} `true` if that argument node is using a scope variable the directive defined. */ function isUsingScopeVar(vSlot) { const argument = vSlot.key.argument; const value = vSlot.value; if (argument && value && argument.type === "VExpressionContainer") { for (const { variable } of argument.references) if (variable != null && variable.kind === "scope" && variable.id.range[0] > value.range[0] && variable.id.range[1] < value.range[1]) return true; } return false; } /** * If `allowModifiers` option is set to `true`, check whether a given argument node has invalid modifiers like `v-slot.foo`. * Otherwise, check whether a given argument node has at least one modifier. * @param {VDirective} vSlot The `v-slot` directive to check. * @param {boolean} allowModifiers `allowModifiers` option in context. * @return {boolean} `true` if that argument node has invalid modifiers like `v-slot.foo`. */ function hasInvalidModifiers(vSlot, allowModifiers) { return allowModifiers ? vSlot.key.argument == null && vSlot.key.modifiers.length > 0 : vSlot.key.modifiers.length > 0; } module.exports = { meta: { type: "problem", docs: { description: "enforce valid `v-slot` directives", categories: ["vue3-essential", "vue2-essential"], url: "https://eslint.vuejs.org/rules/valid-v-slot.html" }, fixable: null, schema: [{ type: "object", properties: { allowModifiers: { type: "boolean" } }, additionalProperties: false }], messages: { ownerMustBeCustomElement: "'v-slot' directive must be owned by a custom element, but '{{name}}' is not.", namedSlotMustBeOnTemplate: "Named slots must use '