eslint-plugin-vue-scoped-css
Version:
ESLint plugin for Scoped CSS in Vue.js
274 lines (273 loc) • 11.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CSSParser = void 0;
const postcss = __importStar(require("postcss"));
const postcss_safe_parser_1 = __importDefault(require("postcss-safe-parser"));
const css_selector_parser_1 = require("./selector/css-selector-parser");
const ast_1 = require("../ast");
const utils_1 = require("./utils");
const css_nodes_1 = require("../utils/css-nodes");
const utils_2 = require("../../utils/utils");
class CSSParser {
constructor(sourceCode, lang) {
this._selectorParser = null;
this.anyErrors = [];
this.sourceCode = sourceCode;
this.commentContainer = [];
this.lang = lang;
}
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;
const styleLoc = {
start: offsetLocation,
end: sourceCode.getLocFromIndex(endIndex),
};
return new ast_1.VCSSStyleSheet(null, styleLoc, 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 = 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 ast_1.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 ((0, utils_1.isPostCSSContainer)(node) && (0, css_nodes_1.isVCSSContainerNode)(astNode)) {
astNode.nodes = node.nodes
.map((n) => this._postcssNodeToASTNode(offsetLocation, n, astNode))
.filter(utils_2.isDefined);
}
return astNode;
}
parseInternal(css) {
try {
return postcss.parse(css);
}
catch (e) {
this.addError(e);
return (0, postcss_safe_parser_1.default)(css);
}
}
createSelectorParser() {
return new css_selector_parser_1.CSSSelectorParser(this.sourceCode, this.commentContainer);
}
convertRootNode(node, loc, start, end) {
return new ast_1.VCSSStyleSheet(node, loc, start, end, { lang: this.lang });
}
convertRuleNode(node, loc, start, end, parent) {
var _a, _b, _c;
const astNode = new ast_1.VCSSStyleRule(node, loc, start, end, {
parent,
rawSelectorText: (_b = (_a = this.getRaw(node, "selector")) === null || _a === void 0 ? void 0 : _a.raw) !== null && _b !== void 0 ? _b : null,
});
astNode.selectors = this.selectorParser.parse(astNode.rawSelectorText, astNode.loc.start, astNode);
if ((_c = this.getRaw(node, "between")) === null || _c === void 0 ? void 0 : _c.trim()) {
this.parseRuleRawsBetween(node, astNode);
}
return astNode;
}
parseRuleRawsBetween(node, astNode) {
var _a, _b;
const between = this.getRaw(node, "between");
const rawSelector = (_b = (_a = this.getRaw(node, "selector")) === null || _a === void 0 ? void 0 : _a.raw) !== null && _b !== void 0 ? _b : node.selector;
const betweenStart = astNode.range[0] + rawSelector.length;
const postcssRoot = this.parseInternal(between || "");
this._postcssNodeToASTNode(this.sourceCode.getLocFromIndex(betweenStart), postcssRoot);
}
convertAtruleNode(node, loc, start, end, parent) {
var _a, _b, _c, _d, _e;
const astNode = new ast_1.VCSSAtRule(node, loc, start, end, {
parent,
rawParamsText: (_b = (_a = this.getRaw(node, "params")) === null || _a === void 0 ? void 0 : _a.raw) !== null && _b !== void 0 ? _b : null,
identifier: (_c = this.getRaw(node, "identifier")) !== null && _c !== void 0 ? _c : "@",
});
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 ((_d = this.getRaw(node, "afterName")) === null || _d === void 0 ? void 0 : _d.trim()) {
this.parseAtruleRawsAfterName(node, astNode);
}
if ((_e = this.getRaw(node, "between")) === null || _e === void 0 ? void 0 : _e.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) {
var _a, _b;
const between = this.getRaw(node, "between");
const rawParams = (_b = (_a = this.getRaw(node, "params")) === null || _a === void 0 ? void 0 : _a.raw) !== null && _b !== void 0 ? _b : 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);
}
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 ast_1.VCSSDeclarationProperty(node, loc, start, end, {
parent,
property,
});
}
convertCommentNode(node, loc, start, end, parent) {
this.commentContainer.push(new ast_1.VCSSComment(node, node.text, loc, start, end, { parent }));
return null;
}
convertUnknownTypeNode(node, loc, start, end, parent) {
return new ast_1.VCSSUnknown(node, loc, start, end, {
parent,
unknownType: node.type,
});
}
getRaw(node, keyName) {
return node.raws[keyName];
}
}
exports.CSSParser = CSSParser;
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 };
}
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",
};
function typeToConvertMethodName(type) {
return convertNodeTypes[type] || "convertUnknownTypeNode";
}