eslint-plugin-vue-scoped-css
Version:
ESLint plugin for Scoped CSS in Vue.js
1,649 lines (1,648 loc) • 191 kB
JavaScript
import { createRequire } from "node:module";
import * as postcss from "postcss";
import postcssSafeParser from "postcss-safe-parser";
import selectorParser from "postcss-selector-parser";
import { get, set, sortedLastIndex } from "es-toolkit/compat";
import * as vueParser from "vue-eslint-parser";
import { AST } from "vue-eslint-parser";
import eslintUtils from "@eslint-community/eslint-utils";
//#region lib/styles/ast.ts
/**
* The node
*/
var Node = class {
/**
* constructor.
* @param {PostCSSNode | PostCSSSPNode} node PostCSS node.
* @param {string} type The token type.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, type, loc, start, end, lang) {
this.type = type;
this.loc = loc;
this.start = start;
this.end = end;
this.range = [start, end];
this.node = node;
this.lang = lang;
}
};
/**
* The has parent node
*/
var HasParentNode = class extends Node {
constructor(node, type, loc, start, end, props) {
super(node, type, loc, start, end, props.parent.lang);
this.parent = props.parent;
}
};
/**
* The CSS Parsing Error.
*/
var VCSSParsingError = class extends Node {
/**
* constructor.
* @param {PostCSSDeclaration} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSParsingError", loc, start, end, props.lang);
this.message = props.message;
}
};
/**
* The CSS root node.
*/
var VCSSStyleSheet = class extends Node {
/**
* constructor.
* @param {PostCSSRoot} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSStyleSheet", loc, start, end, props.lang);
this.nodes = props.nodes ?? [];
this.comments = props.comments ?? [];
this.errors = props.errors ?? [];
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSStyleSheet} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Rule node.
*/
var VCSSStyleRule = class extends HasParentNode {
/**
* constructor.
* @param {AtRule} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSStyleRule", loc, start, end, props);
this.selectorText = props.selectorText ?? node.selector;
if (props.rawSelectorText != null) this.rawSelectorText = props.rawSelectorText;
else {
const raws = node.raws;
this.rawSelectorText = raws.selector ? raws.selector.raw : node.selector;
}
this.selectors = props.selectors ?? [];
this.nodes = props.nodes ?? [];
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSStyleRule} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Declaration Property node.
*/
var VCSSDeclarationProperty = class extends HasParentNode {
/**
* constructor.
* @param {PostCSSDeclaration} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSDeclarationProperty", loc, start, end, props);
this.property = props.property ?? node.prop;
this.value = getProp(props, node, "value");
this.important = props.important ?? Boolean(node.important);
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSDeclarationProperty} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS At(@) Rule node.
*/
var VCSSAtRule = class extends HasParentNode {
/**
* constructor.
* @param {PostCSSAtRule} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSAtRule", loc, start, end, props);
this.node = node;
this.name = getProp(props, node, "name");
this.identifier = props.identifier;
this.paramsText = props.paramsText ?? node.params;
if (props.rawParamsText != null) this.rawParamsText = props.rawParamsText;
else {
const raws = node.raws;
this.rawParamsText = raws.params?.raw ?? node.params;
}
this.selectors = props.selectors ?? void 0;
this.nodes = props.nodes ?? [];
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSAtRule} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Unknown.
*/
var VCSSUnknown = class extends HasParentNode {
/**
* constructor.
* @param {PostCSSNode} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSUnknown", loc, start, end, props);
this.nodes = props.nodes ?? [];
this.unknownType = props.unknownType;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSUnknown} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Selector node.
*/
var VCSSSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSSelector", loc, start, end, props);
this.nodes = props.nodes ?? [];
this.parent = props.parent;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
get selector() {
return this.nodes.map((n) => n.selector).join("");
}
};
/**
* The CSS Type Selector node.
*/
var VCSSTypeSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSTypeSelector", loc, start, end, props);
this.value = getProp(props, node, "value");
this.selector = this.value;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSTypeSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS ID Selector node.
*/
var VCSSIDSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSIDSelector", loc, start, end, props);
this.value = getProp(props, node, "value");
this.selector = `#${this.value}`;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSIDSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Class Selector node.
*/
var VCSSClassSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSClassSelector", loc, start, end, props);
this.value = getProp(props, node, "value");
this.selector = `.${this.value}`;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSClassSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Nesting Selector node.
*/
var VCSSNestingSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSNestingSelector", loc, start, end, props);
this.value = getProp(props, node, "value");
this.selector = this.value;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSNestingSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Universal Selector node.
*/
var VCSSUniversalSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSUniversalSelector", loc, start, end, props);
this.value = getProp(props, node, "value");
this.selector = this.value;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSUniversalSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Attribuute Selector node.
*/
var VCSSAttributeSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSAttributeSelector", loc, start, end, props);
this.attribute = getProp(props, node, "attribute");
const operator = getProp(props, node, "operator");
this.operator = operator ?? null;
const value = getProp(props, node, "value");
this.value = value ?? null;
const quoteMark = getProp(props, node, "quoteMark");
this.quoteMark = quoteMark ?? null;
const raws = node.raws;
if (props.insensitiveFlag != null) this.insensitiveFlag = props.insensitiveFlag;
else if (raws.insensitiveFlag != null) this.insensitiveFlag = raws.insensitiveFlag;
else if (node.insensitive) this.insensitiveFlag = "i";
else this.insensitiveFlag = null;
this.selector = this.refreshSelector();
}
refreshSelector() {
let selector = `[${this.attribute}`;
if (this.operator != null) {
selector += this.operator;
if (this.value != null) selector += this.quoteMark ? this.quoteMark + this.value + this.quoteMark : this.value;
}
if (this.insensitiveFlag != null) selector += ` ${this.insensitiveFlag}`;
selector += "]";
return selector;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSAttributeSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Pseudo node.
*/
var VCSSSelectorPseudo = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSSelectorPseudo", loc, start, end, props);
this.value = getProp(props, node, "value");
this.nodes = props.nodes ?? [];
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSSelectorPseudo} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
get selector() {
if (!this.nodes.length) return this.value;
const params = this.nodes.map((n) => n.selector).join(",");
return `${this.value}(${params})`;
}
};
/**
* The CSS Selector Combinator node.
*/
var VCSSSelectorCombinator = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSSelectorCombinator", loc, start, end, props);
this.value = getProp(props, node, "value");
this.selector = this.value;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSSelectorCombinator} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Unknown Selector node.
*/
var VCSSUnknownSelector = class extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, loc, start, end, props) {
super(node, "VCSSUnknownSelector", loc, start, end, props);
this.value = getProp(props, node, "value") || "";
this.selector = this.value;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSUnknownSelector} copy node
*/
copy(props) {
return copyStdNode(this, props);
}
};
/**
* The CSS Comment node.
*/
var VCSSComment = class VCSSComment extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {string} text The contents.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, text, loc, start, end, props) {
super(node, "VCSSComment", loc, start, end, props);
this.node = node;
this.text = text;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSComment} copy node
*/
copy(props) {
const parent = props?.parent ?? this.parent;
return new VCSSComment(props?.node ?? this.node, props?.text ?? this.text, props?.loc ?? this.loc, props?.start ?? this.start, props?.end ?? this.end, {
...this,
...props,
parent
});
}
};
/**
* The CSS Inline Comment node.
*/
var VCSSInlineComment = class VCSSInlineComment extends HasParentNode {
/**
* constructor.
* @param {object} node The node.
* @param {string} text The contents.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {object} props The optional property.
* @returns {void}
*/
constructor(node, text, loc, start, end, props) {
super(node, "VCSSInlineComment", loc, start, end, props);
this.node = node;
this.text = text;
}
/**
* Copy
* @param {object} props The optional change property.
* @returns {VCSSInlineComment} copy node
*/
copy(props) {
const parent = props?.parent ?? this.parent;
return new VCSSInlineComment(props?.node ?? this.node, props?.text ?? this.text, props?.loc ?? this.loc, props?.start ?? this.start, props?.end ?? this.end, {
...this,
...props,
parent
});
}
};
/**
* Get property from given props or node
* @param {object} props The optional property.
* @param {object} node The node.
* @param {string} name name of property
*/
function getProp(props, node, name) {
if (props?.[name] != null) return props[name];
return node[name];
}
/**
* Copy the given ASTNode.
* @param astNode ASTNode
* @param props The optional property.
*/
function copyStdNode(astNode, props) {
const C = astNode.constructor;
return new C(props?.node ?? astNode.node, props?.loc ?? astNode.loc, props?.start ?? astNode.start, props?.end ?? astNode.end, {
...astNode,
...props
});
}
//#endregion
//#region lib/styles/utils/css-nodes.ts
/**
* Checks whether the given node is VCSSAtRule
* @param node node to check
*/
function isVCSSAtRule(node) {
return node?.type === "VCSSAtRule";
}
/**
* Checks whether the given node is VCSSStyleRule
* @param node node to check
*/
function isVCSSStyleRule(node) {
return node?.type === "VCSSStyleRule";
}
/**
* Checks whether the given node is VCSSStyleSheet
* @param node node to check
*/
function isVCSSStyleSheet(node) {
return node?.type === "VCSSStyleSheet";
}
/**
* Checks whether the given node is VCSSDeclarationProperty
* @param node node to check
*/
function isVCSSDeclarationProperty(node) {
return node?.type === "VCSSDeclarationProperty";
}
/**
* Checks whether the given node is VCSSComment
* @param node node to check
*/
function isVCSSComment(node) {
return node?.type === "VCSSComment" || node?.type === "VCSSInlineComment";
}
/**
* Checks whether the given node has nodes node
* @param node node to check
*/
function isVCSSContainerNode(node) {
return isVCSSAtRule(node) || isVCSSStyleRule(node) || isVCSSStyleSheet(node) || node?.type === "VCSSUnknown";
}
/**
* Checks whether the given node has selectors.
*/
function hasSelectorNodes(node) {
if (isVCSSStyleRule(node) || isNestingAtRule(node)) return true;
return false;
}
//#endregion
//#region lib/styles/utils/selectors.ts
/**
* Checks whether the given node is VCSSTypeSelector
* @param node node to check
*/
function hasNodesSelector(node) {
return node != null && (node.type === "VCSSSelector" || node.type === "VCSSSelectorPseudo");
}
/**
* Returns the normalized result of Pseudo params.
*/
function normalizePseudoParams(pseudo, nodes) {
const results = [];
let buffer = [];
for (const node of nodes) if (node.type === "VCSSSelector") {
if (buffer.length) {
const startNode = buffer[0];
const endNode = buffer[buffer.length - 1];
const loc = {
start: startNode.loc.start,
end: endNode.loc.end
};
results.push(new VCSSSelector(buffer[0], loc, startNode.start, endNode.end, {
parent: pseudo,
nodes: buffer
}));
buffer = [];
}
results.push(node);
} else buffer.push(node);
if (buffer.length) {
const startNode = buffer[0];
const endNode = buffer[buffer.length - 1];
const loc = {
start: startNode.loc.start,
end: endNode.loc.end
};
results.push(new VCSSSelector(buffer[0], loc, startNode.start, endNode.end, {
parent: pseudo,
nodes: buffer
}));
buffer = [];
}
return results;
}
/**
* Checks whether the given node is ::v-deep or ::v-slotted or ::v-global pseudo
* @param node node to check
*/
function isVueSpecialPseudo(node) {
return isVDeepPseudo(node) || isVSlottedPseudo(node) || isVGlobalPseudo(node);
}
/**
* Checks whether the given node is ::v-deep pseudo for Vue.js v2
* @param node node to check
*/
function isVDeepPseudoV2(node) {
if (isVDeepPseudo(node)) return node.nodes.length === 0;
return false;
}
/**
* Checks whether the given node is ::v-deep pseudo
* @param node node to check
*/
function isVDeepPseudo(node) {
if (isPseudo(node)) {
const val = node.value.trim();
return val === "::v-deep" || val === ":deep";
}
return false;
}
/**
* Checks whether the given node is ::v-slotted pseudo
* @param node node to check
*/
function isVSlottedPseudo(node) {
if (isPseudo(node)) {
const val = node.value.trim();
return val === "::v-slotted" || val === ":slotted";
}
return false;
}
/**
* Checks whether the given node is ::v-global pseudo
* @param node node to check
*/
function isVGlobalPseudo(node) {
if (isPseudo(node)) {
const val = node.value.trim();
return val === "::v-global" || val === ":global";
}
return false;
}
/**
* Checks whether the given pseudo node is empty arguments
* @param node node to check
*/
function isPseudoEmptyArguments(node) {
return node.nodes.length === 0 || node.nodes.length === 1 && node.nodes[0].nodes.length === 0;
}
/**
* Checks whether the given node is VCSSTypeSelector
* @param node node to check
*/
function isTypeSelector(node) {
return node?.type === "VCSSTypeSelector";
}
/**
* Checks whether the given node is VCSSIDSelector
* @param node node to check
*/
function isIDSelector(node) {
return node?.type === "VCSSIDSelector";
}
/**
* Checks whether the given node is VCSSSelectorNode
* @param node node to check
*/
function isClassSelector(node) {
return node?.type === "VCSSClassSelector";
}
/**
* Checks whether the given node is VCSSUniversalSelector
* @param node node to check
*/
function isUniversalSelector(node) {
return node?.type === "VCSSUniversalSelector";
}
/**
* Checks whether the given node is VCSSNestingSelector
* @param node node to check
*/
function isNestingSelector(node) {
return node?.type === "VCSSNestingSelector";
}
/**
* Checks whether the given node is VCSSNestingSelector
* @param node node to check
*/
function isPseudo(node) {
return node?.type === "VCSSSelectorPseudo";
}
/**
* Checks whether the given node is VCSSSelectorCombinator
* @param node node to check
*/
function isSelectorCombinator(node) {
return node?.type === "VCSSSelectorCombinator";
}
/**
* Checks whether the given node is descendant combinator
* @param node node to check
*/
function isDescendantCombinator(node) {
return isSelectorCombinator(node) && node.value.trim() === "";
}
/**
* Checks whether the given node is child combinator
* @param node node to check
*/
function isChildCombinator(node) {
return isSelectorCombinator(node) && node.value.trim() === ">";
}
/**
* Checks whether the given node is adjacent sibling combinator
* @param node node to check
*/
function isAdjacentSiblingCombinator(node) {
return isSelectorCombinator(node) && node.value.trim() === "+";
}
/**
* Checks whether the given node is general sibling combinator
* @param node node to check
*/
function isGeneralSiblingCombinator(node) {
return isSelectorCombinator(node) && node.value.trim() === "~";
}
/**
* Checks whether the given node is deep combinator
* @param node node to check
*/
function isDeepCombinator(node) {
if (isSelectorCombinator(node)) {
const val = node.value.trim();
return val === ">>>" || val === "/deep/";
}
return false;
}
/**
* Checks whether the given node is nesting atrule
* @param node node to check
*/
function isNestingAtRule(node) {
if (node == null) return false;
return isVCSSAtRule(node) && node.name === "nest" && node.identifier === "@";
}
/**
* Find nesting selectors
* @param {Node[]} nodes selector nodes
* @returns {IterableIterator<NestingInfo>} nesting selectors info
*/
function* findNestingSelectors(nodes) {
for (const node of nodes) {
if (isNestingSelector(node)) yield {
nestingIndex: nodes.indexOf(node),
node,
nodes
};
if (hasNodesSelector(node)) yield* findNestingSelectors(node.nodes);
}
}
/**
* Find nesting selector
*/
function findNestingSelector(nodes) {
for (const nest of findNestingSelectors(nodes)) return nest;
return null;
}
//#endregion
//#region lib/styles/parser/utils.ts
/**
* Checks if the given node has nodes property.
*/
function isPostCSSContainer(node) {
return node.nodes != null;
}
/**
* Checks if the given node has nodes property.
*/
function isPostCSSSPContainer(node) {
return node.nodes != null;
}
//#endregion
//#region lib/utils/utils.ts
/**
* Checks whether the given context has template block
*/
function hasTemplateBlock(context) {
const { ast } = context.sourceCode;
return Boolean(ast.templateBody);
}
/**
* Checks whether the given node has defined
*/
function isDefined(item) {
return item !== null && item !== void 0;
}
//#endregion
//#region lib/styles/parser/selector/css-selector-parser.ts
var CSSSelectorParser = class {
/**
* constructor.
* @param {SourceCode} sourceCode the SourceCode object that you can use to work with the source that was passed to ESLint.
* @param {Node[]} commentContainer comment nodes container
*/
constructor(sourceCode, commentContainer) {
this.lang = "css";
this.sourceCode = sourceCode;
this.commentContainer = commentContainer;
}
/**
* Parse CSS selector.
* @param {string} rawSelector `<style>` node
* @param {LineAndColumnData} offsetLocation start location of selector.
* @param {Node} parent parent node
* @return {Node[]} parsed result
*/
parse(rawSelector, offsetLocation, parent) {
const selectorParserRoot = this.parseInternal(rawSelector);
return this._postcssSelectorParserNodeChiildrenToASTNodes(offsetLocation, selectorParserRoot, parent);
}
parseInternal(selector) {
return selectorParser().astSync(selector);
}
parseCommentsInternal(selector) {
return selectorParser().astSync(selector);
}
_postcssSelectorParserNodeChiildrenToASTNodes(offsetLocation, node, parent) {
const astNodes = removeInvalidDescendantCombinator(node.nodes.map((child) => this._postcssSelectorParserNodeToASTNode(offsetLocation, child, parent)).filter(isDefined));
if (astNodes.length !== node.nodes.length) {
if (node.type === "selector") {
const firstAstNode = astNodes[0];
parent.loc.start = { ...firstAstNode.loc.start };
parent.start = firstAstNode.start;
parent.range = [firstAstNode.start, parent.range[1]];
}
if (node.type === "selector") {
const lastAstNode = astNodes[astNodes.length - 1];
parent.loc.end = { ...lastAstNode.loc.end };
parent.end = lastAstNode.end;
parent.range = [parent.range[0], lastAstNode.end];
}
}
return astNodes;
}
/**
* Convert `postcss-selector-parser` node to node that can be handled by ESLint.
* @param {LineAndColumnData} offsetLocation start location of selector.
* @param {object} node the `postcss-selector-parser` node to convert
* @param {Node} parent parent node
* @return {Node} converted node.
*/
_postcssSelectorParserNodeToASTNode(offsetLocation, node, parent) {
const { sourceCode } = this;
/**
* Get index from location
*/
function getIndexFromLoc(loc) {
if (loc.column >= 0) return sourceCode.getIndexFromLoc(loc);
return sourceCode.getIndexFromLoc({
line: loc.line,
column: 0
}) + loc.column;
}
const loc = {
start: getESLintLineAndColumnFromPostCSSSelectorParserNode(offsetLocation, node, "start"),
end: getESLintLineAndColumnFromPostCSSSelectorParserNode(offsetLocation, node, "end")
};
const start = getIndexFromLoc(loc.start);
const end = getIndexFromLoc(loc.end);
const astNode = this[typeToConvertMethodName$1(node.type)](node, loc, start, end, parent);
if (astNode == null) return null;
this.parseRawsSpaces(astNode, node, parent);
if (isPostCSSSPContainer(node)) {
if (astNode.type === "VCSSSelectorPseudo") astNode.nodes = normalizePseudoParams(astNode, this._postcssSelectorParserNodeChiildrenToASTNodes(offsetLocation, node, astNode));
else if (astNode.type === "VCSSSelector") astNode.nodes = this._postcssSelectorParserNodeChiildrenToASTNodes(offsetLocation, node, astNode);
}
return astNode;
}
parseRawsSpaces(astNode, node, parent) {
if (!hasRaws$1(node) || !node.raws.spaces) return;
const { after, before } = node.raws.spaces;
if (after?.trim()) this.parseCommentsInternal(after).walkComments((comment) => {
this._postcssSelectorParserNodeToASTNode(astNode.loc.end, comment, parent);
});
if (before?.trim()) {
const startLoc = this.sourceCode.getLocFromIndex(astNode.range[0] - before.length);
this.parseCommentsInternal(before).walkComments((comment) => {
this._postcssSelectorParserNodeToASTNode(startLoc, comment, parent);
});
}
}
/**
* Convert selector Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSSelector}
*/
convertSelectorNode(node, loc, start, end, parent) {
const sourceCode = this.sourceCode;
const code = sourceCode.text;
let source = code.slice(start, end);
const beforeSpaces = /^\s+/u.exec(source);
if (beforeSpaces?.[0]) {
start = start + beforeSpaces[0].length;
loc.start = this.sourceCode.getLocFromIndex(start);
source = source.slice(beforeSpaces[0].length);
}
const afterSpaces = /\s+$/u.exec(source);
if (afterSpaces?.[0]) {
end = end - afterSpaces[0].length;
loc.end = this.sourceCode.getLocFromIndex(end);
source = source.slice(0, -afterSpaces[0].length);
}
adjustBeforeParenLocation();
adjustAfterParenLocation();
return new VCSSSelector(node, loc, start, end, { parent });
/**
* Adjust before paren token
* `:not(.bar)`
* ^
*/
function adjustBeforeParenLocation() {
const beforeTrivials = /^\(\s*/u.exec(source);
if (!beforeTrivials?.[0]) return;
let withinParen = false;
for (let index = end - 1; index < code.length; index++) {
const ch = code[index];
if (ch === ")") {
withinParen = true;
break;
} else if (ch?.trim() && index !== end - 1) return;
}
if (!withinParen) return;
start = start + beforeTrivials[0].length;
loc.start = sourceCode.getLocFromIndex(start);
source = source.slice(beforeTrivials[0].length);
}
/**
* Adjust after paren token
* `:not(.bar)`
* ^
*/
function adjustAfterParenLocation() {
const afterTrivials = /\s*\)$/u.exec(source);
if (!afterTrivials?.[0]) return;
let withinParen = false;
for (let index = start - 1; index >= 0; index--) {
const ch = code[index];
if (ch === "(") {
withinParen = true;
break;
} else if (ch?.trim()) return;
}
if (!withinParen) return;
end = end - afterTrivials[0].length;
loc.end = sourceCode.getLocFromIndex(end);
source = source.slice(0, -afterTrivials[0].length);
}
}
/**
* Convert tag Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSTypeSelector}
*/
convertTagNode(node, loc, start, end, parent) {
return new VCSSTypeSelector(node, loc, start, end, { parent });
}
/**
* Convert id Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSIDSelector}
*/
convertIdNode(node, loc, start, end, parent) {
return new VCSSIDSelector(node, loc, start, end, { parent });
}
/**
* Convert class Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSClassSelector}
*/
convertClassNode(node, loc, start, end, parent) {
return new VCSSClassSelector(node, loc, start, end, { parent });
}
/**
* Convert nesting Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSNestingSelector}
*/
convertNestingNode(node, loc, start, end, parent) {
return new VCSSNestingSelector(node, loc, start, end, { parent });
}
/**
* Convert universal Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSUniversalSelector}
*/
convertUniversalNode(node, loc, start, end, parent) {
return new VCSSUniversalSelector(node, loc, start, end, { parent });
}
/**
* Convert attribute Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSAttributeSelector}
*/
convertAttributeNode(node, loc, start, end, parent) {
return new VCSSAttributeSelector(node, loc, start, end, { parent });
}
/**
* Convert pseudo Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSSelectorPseudo}
*/
convertPseudoNode(node, loc, start, end, parent) {
return new VCSSSelectorPseudo(node, loc, start, end, { parent });
}
/**
* Convert combinator Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSSelectorCombinator}
*/
convertCombinatorNode(node, loc, start, end, parent) {
const astNode = new VCSSSelectorCombinator(node, loc, start, end, { parent });
adjustEndLocation(astNode, start + astNode.value.length, this.sourceCode);
return astNode;
}
/**
* Convert string Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSSelectorCombinator}
*/
convertStringNode(node, loc, start, end, parent) {
const astNode = new VCSSUnknownSelector(node, loc, start, end, { parent });
adjustEndLocation(astNode, start + astNode.value.length, this.sourceCode);
return astNode;
}
/**
* Convert comment Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {null}
*/
convertCommentNode(node, loc, start, end, parent) {
const text = node.value.replace(/^\s*\/\*/u, "").replace(/\*\/\s*$/u, "");
this.commentContainer.push(new VCSSComment(node, text, loc, start, end, { parent }));
return null;
}
/**
* Convert unknown Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {Node}
*/
convertUnknownTypeNode(node, loc, start, end, parent) {
return new VCSSUnknownSelector(node, loc, start, end, { parent });
}
};
/**
* Convert `postcss-selector-parser` location to ESLint location.
* @param {LineAndColumnData} offsetLocation start location of selector.
* @param {object} node the `postcss-selector-parser` node to comvert
* @param {"start"|"end"} locName the name of location
* @return {LineAndColumnData} converted location.
*/
function getESLintLineAndColumnFromPostCSSSelectorParserNode(offsetLocation, node, locName) {
const sourceLoc = node.source && node.source[locName] || {
line: 0,
column: 1
};
let { line } = sourceLoc;
let column = sourceLoc.column - 1;
if (line === 1) {
line = offsetLocation.line;
column = offsetLocation.column + column;
} else line = offsetLocation.line + line - 1;
if (locName === "end") column++;
return {
line,
column
};
}
/**
* Adjust end location
*/
function adjustEndLocation(astNode, endIndex, sourceCode) {
if (astNode.range[1] === endIndex) return;
astNode.range[1] = endIndex;
astNode.end = endIndex;
astNode.loc.end = sourceCode.getLocFromIndex(endIndex);
let p = astNode.parent;
while (p && p.end < endIndex) {
p.end = endIndex;
p.range[1] = endIndex;
p.loc.end = { ...astNode.loc.end };
p = p.parent;
}
}
/**
* Remove invalid descendant combinators
*/
function removeInvalidDescendantCombinator(nodes) {
const results = [];
let prev = null;
for (let index = 0; index < nodes.length; index++) {
const node = nodes[index];
if (isDescendantCombinator(node)) {
if (results.length === 0) continue;
if (isSelectorCombinator(prev) || isVDeepPseudoV2(prev)) continue;
const next = nodes[index + 1];
if (isSelectorCombinator(next)) continue;
} else if (isVueSpecialPseudo(node)) {
if (prev && !isSelectorCombinator(prev)) results.push(new VCSSSelectorCombinator(node.node, node.loc, node.start, node.end, {
parent: node.parent,
value: " "
}));
}
results.push(node);
prev = node;
}
return results;
}
const convertNodeTypes$1 = {
tag: "convertTagNode",
string: "convertStringNode",
selector: "convertSelectorNode",
pseudo: "convertPseudoNode",
nesting: "convertNestingNode",
id: "convertIdNode",
comment: "convertCommentNode",
combinator: "convertCombinatorNode",
class: "convertClassNode",
attribute: "convertAttributeNode",
universal: "convertUniversalNode"
};
/**
* Get convert method name from given type
*/
function typeToConvertMethodName$1(type) {
if (type === "root") return "convertUnknownTypeNode";
return convertNodeTypes$1[type] || "convertUnknownTypeNode";
}
/**
* Checks whether has raws
*/
function hasRaws$1(node) {
return node.raws != null;
}
//#endregion
//#region lib/styles/parser/css-parser.ts
/**
* CSS Parser
*/
var CSSParser = class {
/**
* constructor.
* @param {SourceCode} sourceCode the SourceCode object that you can use to work with the source that was passed to ESLint.
*/
constructor(sourceCode, lang) {
this._selectorParser = null;
this.anyErrors = [];
this.sourceCode = sourceCode;
this.commentContainer = [];
this.lang = lang;
}
/**
* Parse the CSS.
* @param {string} css the CSS to parse
* @param {LineAndColumnData} offsetLocation start location of css.
* @return {VCSSStyleSheet} parsed result
*/
parse(css, offsetLocation) {
const { sourceCode } = this;
this.commentContainer = [];
this._selectorParser = this.createSelectorParser();
this.anyErrors = [];
try {
const postcssRoot = this.parseInternal(css);
const rootNode = this._postcssNodeToASTNode(offsetLocation, postcssRoot);
rootNode.comments = this.commentContainer;
rootNode.errors.push(...this.collectErrors(this.anyErrors, offsetLocation));
return rootNode;
} catch (e) {
const startIndex = sourceCode.getIndexFromLoc(offsetLocation);
const endIndex = startIndex + css.length;
return new VCSSStyleSheet(null, {
start: offsetLocation,
end: sourceCode.getLocFromIndex(endIndex)
}, startIndex, endIndex, {
errors: this.collectErrors([...this.anyErrors, e], offsetLocation),
lang: this.lang
});
}
}
addError(error) {
this.anyErrors.push(error);
}
collectErrors(errors, offsetLocation) {
const errorNodes = [];
const duplicate = /* @__PURE__ */ new Set();
for (const error of errors) {
const errorLoc = error.line != null && error.column != null ? getESLintLineAndColumnFromPostCSSPosition(offsetLocation, error) : offsetLocation;
const message = error.reason || error.message;
const key = `[${errorLoc.line}:${errorLoc.column}]: ${message}`;
if (duplicate.has(key)) continue;
duplicate.add(key);
const errorIndex = this.sourceCode.getIndexFromLoc(errorLoc);
errorNodes.push(new VCSSParsingError(null, {
start: errorLoc,
end: errorLoc
}, errorIndex, errorIndex, {
lang: this.lang,
message
}));
}
return errorNodes;
}
get selectorParser() {
return this._selectorParser || (this._selectorParser = this.createSelectorParser());
}
_postcssNodeToASTNode(offsetLocation, node, parent) {
const { sourceCode } = this;
const startLoc = getESLintLineAndColumnFromPostCSSNode(offsetLocation, node, "start") || {
line: 0,
column: 1
};
const start = sourceCode.getIndexFromLoc(startLoc);
const endLoc = getESLintLineAndColumnFromPostCSSNode(offsetLocation, node, "end") || sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(offsetLocation) + node.source.input.css.length);
const end = sourceCode.getIndexFromLoc(endLoc);
const loc = {
start: startLoc,
end: endLoc
};
const astNode = this[typeToConvertMethodName(node.type)](node, loc, start, end, parent);
if (astNode == null) return null;
if (isPostCSSContainer(node) && isVCSSContainerNode(astNode)) astNode.nodes = node.nodes.map((n) => this._postcssNodeToASTNode(offsetLocation, n, astNode)).filter(isDefined);
return astNode;
}
parseInternal(css) {
try {
return postcss.parse(css);
} catch (e) {
this.addError(e);
return postcssSafeParser(css);
}
}
createSelectorParser() {
return new CSSSelectorParser(this.sourceCode, this.commentContainer);
}
/**
* Convert root Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSStyleSheet}
*/
convertRootNode(node, loc, start, end) {
return new VCSSStyleSheet(node, loc, start, end, { lang: this.lang });
}
/**
* Convert rule Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSStyleRule}
*/
convertRuleNode(node, loc, start, end, parent) {
const astNode = new VCSSStyleRule(node, loc, start, end, {
parent,
rawSelectorText: this.getRaw(node, "selector")?.raw ?? null
});
astNode.selectors = this.selectorParser.parse(astNode.rawSelectorText, astNode.loc.start, astNode);
if (this.getRaw(node, "between")?.trim()) this.parseRuleRawsBetween(node, astNode);
return astNode;
}
parseRuleRawsBetween(node, astNode) {
const between = this.getRaw(node, "between");
const rawSelector = this.getRaw(node, "selector")?.raw ?? node.selector;
const betweenStart = astNode.range[0] + rawSelector.length;
const postcssRoot = this.parseInternal(between || "");
this._postcssNodeToASTNode(this.sourceCode.getLocFromIndex(betweenStart), postcssRoot);
}
/**
* Convert atrule Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSAtRule}
*/
convertAtruleNode(node, loc, start, end, parent) {
const astNode = new VCSSAtRule(node, loc, start, end, {
parent,
rawParamsText: this.getRaw(node, "params")?.raw ?? null,
identifier: this.getRaw(node, "identifier") ?? "@"
});
if (node.name === "nest") {
const paramsStartIndex = astNode.range[0] + astNode.identifier.length + astNode.name.length + (this.getRaw(node, "afterName") || "").length;
astNode.selectors = this.selectorParser.parse(astNode.rawParamsText, this.sourceCode.getLocFromIndex(paramsStartIndex), astNode);
}
if (this.getRaw(node, "afterName")?.trim()) this.parseAtruleRawsAfterName(node, astNode);
if (this.getRaw(node, "between")?.trim()) this.parseAtruleRawsBetween(node, astNode);
return astNode;
}
parseAtruleRawsAfterName(node, astNode) {
const afterName = this.getRaw(node, "afterName");
const afterNameStart = astNode.range[0] + astNode.identifier.length + astNode.name.length;
const postcssRoot = this.parseInternal(afterName || "");
this._postcssNodeToASTNode(this.sourceCode.getLocFromIndex(afterNameStart), postcssRoot);
}
parseAtruleRawsBetween(node, astNode) {
const between = this.getRaw(node, "between");
const rawParams = this.getRaw(node, "params")?.raw ?? node.params;
const betweenStart = astNode.range[0] + astNode.identifier.length + astNode.name.length + (this.getRaw(node, "afterName") || "").length + rawParams.length;
const postcssRoot = this.parseInternal(between || "");
this._postcssNodeToASTNode(this.sourceCode.getLocFromIndex(betweenStart), postcssRoot);
}
/**
* Convert decl Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {VCSSDeclarationProperty}
*/
convertDeclNode(node, loc, start, end, parent) {
let property = node.prop;
let starLength = 1;
let textProp = this.sourceCode.text.slice(start, start + property.length);
while (property !== textProp) {
property = textProp.slice(0, starLength) + node.prop;
starLength++;
textProp = this.sourceCode.text.slice(start, start + property.length);
}
return new VCSSDeclarationProperty(node, loc, start, end, {
parent,
property
});
}
/**
* Convert comment Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {void}
*/
convertCommentNode(node, loc, start, end, parent) {
this.commentContainer.push(new VCSSComment(node, node.text, loc, start, end, { parent }));
return null;
}
/**
* Convert unknown Node
* @param {object} node The node.
* @param {SourceLocation} loc The location.
* @param {number} start The index of start.
* @param {number} end The index of end.
* @param {Node} parent The parent node.
* @returns {Node}
*/
convertUnknownTypeNode(node, loc, start, end, parent) {
return new VCSSUnknown(node, loc, start, end, {
parent,
unknownType: node.type
});
}
getRaw(node, keyName) {
return node.raws[keyName];
}
};
/**
* Convert PostCSS location to ESLint location.
* @param {LineAndColumnData} offsetLocation start location of selector.
* @param {object} loc the PostCSS location to convert
* @return {LineAndColumnData} converted location.
*/
function getESLintLineAndColumnFromPostCSSPosition(offsetLocation, loc) {
let { line } = loc;
let column = loc.column - 1;
if (line === 1) {
line = offsetLocation.line;
column = offsetLocation.column + column;
} else line = offsetLocation.line + line - 1;
return {
line,
column
};
}
/**
* Convert PostCSS location to ESLint location.
* @param {LineAndColumnData} offsetLocation location of inside the `<style>` node.
* @param {object} node the PostCSS node to convert
* @param {"start"|"end"} locName the name of location
* @return {LineAndColumnData} converted location.
*/
function getESLintLineAndColumnFromPostCSSNode(offsetLocation, node, locName) {
const sourceLoc = node.source[locName];
if (!sourceLoc) return null;
const { line, column } = getESLintLineAndColumnFromPostCSSPosition(offsetLocation, sourceLoc);
if (locName === "end" && node.type !== "root") return {
line,
column: column + 1
};
return {
line,
column
};
}
const convertNodeTypes = {
root: "convertRootNode",
atrule: "convertAtruleNode",
rule: "convertRuleNode",
decl: "convertDeclNode",
comment: "convertCommentNode"
};
/**
* Get convert method name from given type
*/
function typeToConvertMethodName(type) {
return convertNodeTypes[type] || "convertUnknownTypeNode";
}
//#endregion
//#region lib/styles/parser/load-optional-dep.ts
const _require = createRequire(import.meta.url);
/**
* Attempts to load an optional peer dependency by name.
* Returns the loaded module, or `null` if the dependency is not installed.
* Re-throws any error that is NOT a MODULE_NOT_FOUND for the named package.
*/
function loadOptionalDep(depName) {
try {
return _require(depName);
} catch (e) {
const err = e;
const message = typeof err.message === "string" ? err.message : "";
if (err.code !== "MODULE_NOT_FOUND" || !message.includes(depName)) throw err;
return null;
}
}
//#endregion
//#region lib/styles/parser/selector/replace-utils.ts
var SourceCodeLocationResolver = class {
/**
* constructor
*/
constructor(code) {
const lineStartIndices = [0];
let match = void 0;
const lineEndingPattern = /\r\n|[\n\r\u2028\u2029]/gu;
while (match = lineEndingPattern.exec(code)) lineStartIndices.push(match.index + match[0].length);
this.text = code;
this.lineStartIndices = lineStartIndices;
}
/**
* Converts a source text index into a (line, column) pair.
* @param {number} index The index of a character in a file
* @returns {object} A {line, column} location object with a 0-indexed column
*/
getLocFromIndex(index) {
const code = this.text;
const lineStartIndices = this.lineStartIndices;
if (index === code.length) return {
line: lineStartIndices.length,
column: index - lineStartIndices[lineStartIndices.length - 1]
};
const lineNumber = sortedLastIndex(lineStartIndices, index);
return {
line: lineNumber,
column: index - lineStartIndices[lineNumber - 1]
};
}
/**
* Converts a (line, column) pair into a range index.
* @param {Object} loc A line/column location
* @param {number} loc.line The line number of the location (1-indexed)
* @param {number} loc.column The column number of the location (0-indexed)
* @returns {number} The range index of the location in the file.
* @public
*/
getIndexFromLoc(loc) {
return this.lineStartIndices[loc.line - 1] + loc.column;
}
};
var RemapIndexContext = class {
constructor() {
this.mappers = [];
this.orgIndex = 0;
this.newIndex = 0;
this.batchLengthOrg = 0;
this.batchLengthNew = 0;
}
applyEq(length) {
if (length <= 0) return;
this.flush();
const newEnd = this.newIndex + length;
const orgEnd = this.orgIndex + length;