UNPKG

eslint-plugin-vue-scoped-css

Version:
649 lines (648 loc) 22.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.QueryContext = void 0; exports.createQueryContext = createQueryContext; const selectors_1 = require("../../utils/selectors"); const elements_1 = require("./elements"); const attribute_tracker_1 = require("./attribute-tracker"); const context_1 = require("../../context"); const nodes_1 = require("../../utils/nodes"); const template_1 = require("../../template"); const templates_1 = require("../../../utils/templates"); const style_1 = require("../../context/style"); const reference_expression_1 = require("./reference-expression"); const compat_1 = require("../../../utils/compat"); const TRANSITION_CLASS_BASES = [ "enter", "enter-from", "enter-active", "enter-to", "leave", "leave-from", "leave-active", "leave-to", ]; const TRANSITION_GROUP_CLASS_BASES = [...TRANSITION_CLASS_BASES, "move"]; class QueryContext { constructor(document) { this.elements = []; this.document = document || this; } queryStep(selectorNode) { return new ElementsQueryContext(queryStep(this.elements, selectorNode, this.document), this.document); } reverseQueryStep(selectorNode) { return new ElementsQueryContext(reverseQueryStep(this.elements, selectorNode, this.document), this.document); } filter(predicate) { return new ElementsQueryContext(this.elements.filter(predicate), this.document); } split() { return this.elements.map((e) => new ElementsQueryContext([e], this.document)); } } exports.QueryContext = QueryContext; class VueDocumentQueryContext extends QueryContext { constructor(context, options) { super(); const sourceCode = (0, compat_1.getSourceCode)(context); const { ast } = sourceCode; this.elements = ast.templateBody ? [...genDescendantElements([ast.templateBody])] : []; this.context = context; this.options = options; if (options.captureClassesFromDoc.length > 0) { this.docsModifiers = (0, context_1.getStyleContexts)(context) .filter(style_1.isValidStyleContext) .filter((style) => style.scoped) .map((style) => extractClassesFromDoc(style, options.captureClassesFromDoc)) .reduce((r, a) => r.concat(a), []); } else { this.docsModifiers = []; } } } function extractClassesFromDoc(style, captureClassesFromDoc) { const results = new Set(); for (const comment of style.cssNode.comments) { for (const regexp of captureClassesFromDoc) { regexp.lastIndex = 0; let re; while ((re = regexp.exec(comment.text))) { if (re.length > 1) { for (const s of re.slice(1)) { results.add(s); } } else { results.add(re[0]); } } } } return [...results]; } class ElementsQueryContext extends QueryContext { constructor(elements, document) { super(document); this.elements = [...elements]; } } function createQueryContext(context, options) { return new VueDocumentQueryContext(context, options); } function* queryStep(elements, selectorNode, document) { if ((0, selectors_1.isSelectorCombinator)(selectorNode)) { if ((0, selectors_1.isChildCombinator)(selectorNode)) { yield* genChildElements(elements); return; } else if ((0, selectors_1.isDescendantCombinator)(selectorNode) || (0, selectors_1.isDeepCombinator)(selectorNode)) { yield* genDescendantElements(elements); return; } else if ((0, selectors_1.isAdjacentSiblingCombinator)(selectorNode)) { yield* genAdjacentSiblingElements(elements); return; } else if ((0, selectors_1.isGeneralSiblingCombinator)(selectorNode)) { yield* genGeneralSiblingElements(elements); return; } } else if ((0, selectors_1.isVDeepPseudo)(selectorNode)) { yield* genVDeepElements(elements, (0, selectors_1.normalizePseudoParams)(selectorNode, selectorNode.nodes), query); return; } else if ((0, selectors_1.isVSlottedPseudo)(selectorNode)) { yield* genVSlottedElements(elements, (0, selectors_1.normalizePseudoParams)(selectorNode, selectorNode.nodes), query); return; } else if ((0, selectors_1.isVGlobalPseudo)(selectorNode)) { yield* genVGlobalElements(elements, (0, selectors_1.normalizePseudoParams)(selectorNode, selectorNode.nodes), document, query); return; } if ((0, selectors_1.isTypeSelector)(selectorNode)) { yield* genElementsByTagName(elements, template_1.Template.ofSelector(selectorNode)); return; } else if ((0, selectors_1.isIDSelector)(selectorNode)) { yield* genElementsById(elements, template_1.Template.ofSelector(selectorNode), document); return; } else if ((0, selectors_1.isClassSelector)(selectorNode)) { yield* genElementsByClassName(elements, template_1.Template.ofSelector(selectorNode), document); return; } else if ((0, selectors_1.isUniversalSelector)(selectorNode)) { yield* elements; return; } yield* elements; function query(els, selList) { return selList.reduce((res, sel) => [ ...queryStep(res, sel, document), ], els); } } function* reverseQueryStep(elements, selectorNode, document) { if ((0, selectors_1.isSelectorCombinator)(selectorNode)) { if ((0, selectors_1.isChildCombinator)(selectorNode)) { yield* genParentElements(elements); return; } else if ((0, selectors_1.isDescendantCombinator)(selectorNode) || (0, selectors_1.isDeepCombinator)(selectorNode)) { yield* genAncestorElements(elements); return; } else if ((0, selectors_1.isAdjacentSiblingCombinator)(selectorNode)) { yield* genPrevAdjacentSiblingElements(elements); return; } else if ((0, selectors_1.isGeneralSiblingCombinator)(selectorNode)) { yield* genPrevGeneralSiblingElements(elements); return; } } else if ((0, selectors_1.isVDeepPseudo)(selectorNode)) { yield* genVDeepElements(elements, (0, selectors_1.normalizePseudoParams)(selectorNode, selectorNode.nodes), query); return; } else if ((0, selectors_1.isVSlottedPseudo)(selectorNode)) { yield* genVSlottedElements(elements, (0, selectors_1.normalizePseudoParams)(selectorNode, selectorNode.nodes), query); return; } else if ((0, selectors_1.isVGlobalPseudo)(selectorNode)) { yield* genVGlobalElements(elements, (0, selectors_1.normalizePseudoParams)(selectorNode, selectorNode.nodes), document, query); return; } yield* queryStep(elements, selectorNode, document); function query(els, selList) { return selList.reduceRight((res, sel) => [ ...reverseQueryStep(res, sel, document), ], els); } } function* genDescendantElements(elements) { const found = new Set(); for (const e of genChildElements(elements)) { if (!found.has(e)) { yield e; found.add(e); for (const p of genDescendantElements([e])) { if (!found.has(p)) { yield p; found.add(p); } } } } } function* genAncestorElements(elements) { const found = new Set(); for (const e of genParentElements(elements)) { yield e; found.add(e); for (const a of genAncestorElements([e])) { if (!found.has(a)) { yield a; found.add(a); } } } } function* genChildElements(elements) { function* genChildren(elm) { for (const e of elm.children.filter(templates_1.isVElement)) { if ((0, elements_1.isSkipElement)(e)) { yield* genChildren(e); } else if ((0, elements_1.isSlotElement)(e)) { yield e; yield* genChildren(e); } else { yield e; } } } for (const element of elements) { if ((0, elements_1.isSlotElement)(element)) { continue; } yield* genChildren(element); } } function genParentElements(elements) { return iterateUnique(function* () { for (const element of elements) { yield (0, elements_1.getParentElement)(element); } }); } function genAdjacentSiblingElements(elements) { return iterateUnique(function* () { for (const element of elements) { if (hasVFor(element)) { yield element; } const vForTemplate = getVForTemplate(element); if (vForTemplate) { const children = [...genChildElements([vForTemplate])]; const index = children.indexOf(element); yield children[index + 1] || children[0]; } const parent = (0, elements_1.getParentElement)(element); if (parent) { const children = [...genChildElements([parent])]; const index = children.indexOf(element); yield children[index + 1]; } } }); } function genPrevAdjacentSiblingElements(elements) { return iterateUnique(function* () { for (const element of elements) { if (hasVFor(element)) { yield element; } const vForTemplate = getVForTemplate(element); if (vForTemplate) { const children = [...genChildElements([vForTemplate])]; const index = children.indexOf(element); yield children[index - 1] || children[children.length - 1]; } const parent = (0, elements_1.getParentElement)(element); if (parent) { const children = [...genChildElements([parent])]; const index = children.indexOf(element); yield children[index - 1]; } } }); } function genGeneralSiblingElements(elements) { return iterateUnique(function* () { for (const element of elements) { if (hasVFor(element)) { yield element; } const vForTemplate = getVForTemplate(element); if (vForTemplate) { yield* genChildElements([vForTemplate]); } const parent = (0, elements_1.getParentElement)(element); if (parent) { const children = [...genChildElements([parent])]; const index = children.indexOf(element); yield* children.slice(index + 1); } } }); } function genPrevGeneralSiblingElements(elements) { return iterateUnique(function* () { for (const element of elements) { if (hasVFor(element)) { yield element; } const vForTemplate = getVForTemplate(element); if (vForTemplate) { yield* genChildElements([vForTemplate]); } const parent = (0, elements_1.getParentElement)(element); if (parent) { const children = [...genChildElements([parent])]; const index = children.indexOf(element); yield* children.slice(0, index); } } }); } function* genVDeepElements(elements, params, query) { if (params.length) { yield* iterateUnique(function* () { for (const node of params) { yield* query(elements, node.nodes); } }); } else { yield* elements; } } function genVSlottedElements(elements, params, query) { return iterateUnique(function* () { for (const element of elements) { if ((0, elements_1.isSlotElement)(element)) { yield element; } } for (const node of params) { const els = query(elements, node.nodes); for (const e of els) { if (inSlot(e)) { yield e; } } } }); function inSlot(e) { if ((0, elements_1.isSlotElement)(e)) { return true; } return Boolean(e && e.parent && inSlot(e.parent)); } } function genVGlobalElements(_elements, params, document, query) { return iterateUnique(function* () { for (const node of params) { yield* query(document.elements, node.nodes); } }); } function* genElementsByTagName(elements, tagName) { for (const element of elements) { if (element.name === "component" || element.name === "slot") { yield element; } else if (tagName.toLowerCase().matchString(element.name)) { yield element; } } } function* genElementsById(elements, id, document) { for (const element of elements) { if (matchId(element, id, document)) { yield element; } } } function* genElementsByClassName(elements, className, document) { let removeModifierClassName = null; if (document.options.ignoreBEMModifier) { if (className.hasString("--")) { const list = className.divide("--"); list.pop(); if (list.length) { removeModifierClassName = list.reduce((r, a) => r.concat(a)); } } } for (const docMod of document.docsModifiers) { if (docMod.startsWith(":")) { continue; } const modClassName = docMod.startsWith(".") ? docMod.slice(1) : docMod; if (className.matchString(modClassName)) { yield* elements; return; } else if (removeModifierClassName) { if (removeModifierClassName.matchString(modClassName)) { yield* elements; return; } } } for (const element of elements) { if (matchClassName(element, className, document)) { yield element; } else if (removeModifierClassName && matchClassName(element, removeModifierClassName, document)) { yield element; } } } function matchId(element, id, document) { const nodes = (0, attribute_tracker_1.getAttributeValueNodes)(element, "id", document.context); if (nodes == null) { return true; } for (const node of nodes) { const value = template_1.Template.ofNode(node); if (value == null) { return true; } if (value.match(id)) { return true; } } return false; } function matchClassName(element, className, document) { if ((0, elements_1.isElementWrappedInTransition)(element)) { const transition = (0, elements_1.getWrapperTransition)(element); if (transition != null && matchTransitionClassName(transition, className, document)) { return true; } } const nodes = (0, attribute_tracker_1.getAttributeValueNodes)(element, "class", document.context); if (nodes == null) { return true; } for (const node of nodes) { if (node.type === "VLiteral") { if (includesClassName(node.value, className)) { return true; } } else if (matchClassNameExpression(node, className, document)) { return true; } } const refNames = getRefNames(element, document); const vueComponent = (0, context_1.getVueComponentContext)(document.context); if (vueComponent && vueComponent .getClassesOperatedByClassList(refNames, (0, elements_1.isRootElement)(element)) .filter(((n) => n.type === "Literal")) .some((n) => matchClassNameExpression(n, className, document))) { return true; } return false; } function getRefNames(element, document) { const refNameNodes = (0, attribute_tracker_1.getAttributeValueNodes)(element, "ref", document.context); if (refNameNodes == null) { return null; } const refNames = []; for (const refNameNode of refNameNodes) { const refName = template_1.Template.ofNode(refNameNode); if (refName == null) { return null; } refNames.push(refName); } return refNames; } function matchTransitionClassName(element, className, document) { const classBases = (0, templates_1.isTransitionElement)(element) ? TRANSITION_CLASS_BASES : TRANSITION_GROUP_CLASS_BASES; const nameNodes = (0, attribute_tracker_1.getAttributeValueNodes)(element, "name", document.context); for (const classBase of classBases) { const classNameNodes = (0, attribute_tracker_1.getAttributeValueNodes)(element, `${classBase}-class`, document.context); if (classNameNodes == null) { return true; } if (classNameNodes.length) { for (const classNameNode of classNameNodes) { const value = template_1.Template.ofNode(classNameNode); if (value == null) { return true; } if (value.match(className)) { return true; } } } else if (nameNodes == null) { if (className.endsWith(`-${classBase}`)) { return true; } } else if (nameNodes.length === 0) { if (className.matchString(`v-${classBase}`)) { return true; } } else { for (const nameNode of nameNodes) { const name = template_1.Template.ofNode(nameNode); if (name == null) { return true; } if (className.match(name.concat(`-${classBase}`))) { return true; } } } } return false; } function matchClassNameExpression(expression, className, document) { const literal = template_1.Template.ofNode(expression); if (literal != null) { if (includesClassName(literal, className)) { return true; } } else if (expression.type === "Identifier") { const string = (0, nodes_1.getStringFromNode)(expression, document.context); if (string == null) { return true; } if (includesClassName(string, className)) { return true; } } else if (expression.type === "ArrayExpression") { if (matchClassNameForArrayExpression(expression, className, document)) { return true; } } else if (expression.type === "ObjectExpression") { if (matchClassNameForObjectExpression(expression, className, document)) { return true; } } else { return true; } return false; } function matchClassNameForArrayExpression(expression, className, document) { for (const e of expression.elements) { if (e.type === "SpreadElement") { if (matchClassNameExpression(e.argument, className, document)) { return true; } } else { const expressions = (0, reference_expression_1.getReferenceExpressions)(e, document.context); if (expressions) { for (const e2 of expressions) { if (matchClassNameExpression(e2, className, document)) { return true; } } } } } return false; } function matchClassNameForObjectExpression(expression, className, document) { for (const prop of expression.properties) { if (prop.type !== "Property") { return true; } if (prop.computed) { if (prop.key.type === "Identifier" || prop.key.type === "Literal" || prop.key.type === "TemplateLiteral" || prop.key.type === "BinaryExpression") { if (matchClassNameExpression(prop.key, className, document)) { return true; } } else { return true; } } else { if (prop.key.type === "Identifier") { if (includesClassName(prop.key.name, className)) { return true; } } else if (prop.key.type === "Literal") { if (includesClassName(`${prop.key.value}`, className)) { return true; } } } } return false; } function includesClassName(value, className) { if (typeof value === "string") { return value.split(/\s+/u).some((s) => className.matchString(s)); } return value.divide(/\s+/u).some((s) => className.match(s)); } function* iterateUnique(gen) { const found = new Set(); for (const e of gen()) { if (e != null && !found.has(e)) { yield e; found.add(e); } } } function hasVFor(element) { return element.startTag.attributes.some((attr) => attr.directive && attr.key.name.name === "for"); } function getVForTemplate(element) { let parent = element.parent; while (parent) { if (parent.type !== "VElement" || parent.name !== "template") { return null; } if (hasVFor(parent)) { return parent; } parent = parent.parent; } return null; }