stylelint
Version:
Modern CSS linter
211 lines (182 loc) • 5.52 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (options, callback) {
var source = options.source;
var target = options.target;
var insideString = false;
var insideComment = false;
var insideSingleLineComment = false;
var insideParens = false;
var insideFunction = false;
var openingParenCount = 0;
var matchCount = 0;
var openingQuote = void 0;
var ignoreStrings = !options.checkStrings && !options.withinStrings;
var ignoreComments = !options.checkComments && !options.withinComments;
var targetIsArray = Array.isArray(target);
// If the target is just a string, it is easy to check whether
// some index of the source matches it.
// If the target is an array of strings, though, we have to
// check whether some index of the source matches *any* of
// those target strings (stopping after the first match).
var getMatch = function () {
if (!targetIsArray) {
return getMatchBase.bind(null, target);
}
return function (index) {
for (var ti = 0, tl = target.length; ti < tl; ti++) {
var checkResult = getMatchBase(target[ti], index);
if (checkResult) {
return checkResult;
}
}
return false;
};
}();
function getMatchBase(targetString, index) {
var targetStringLength = targetString.length;
// Target is a single character
if (targetStringLength === 1 && source[index] !== targetString) {
return false;
}
// Target is multiple characters
if (source.substr(index, targetStringLength) !== targetString) {
return false;
}
return {
insideParens: insideParens,
insideFunction: insideFunction,
insideComment: insideComment,
insideString: insideString,
startIndex: index,
endIndex: index + targetStringLength,
target: targetString
};
}
for (var i = 0, l = source.length; i < l; i++) {
var currentChar = source[i];
// Register the beginning of a comment
if (!insideString && !insideComment && currentChar === "/" && source[i - 1] !== "\\" // escaping
) {
// standard comments
if (source[i + 1] === "*") {
insideComment = true;
continue;
}
// single-line comments
if (source[i + 1] === "/") {
insideComment = true;
insideSingleLineComment = true;
continue;
}
}
if (insideComment) {
// Register the end of a standard comment
if (!insideSingleLineComment && currentChar === "*" && source[i - 1] !== "\\" // escaping
&& source[i + 1] === "/" && source[i - 1] !== "/" // don't end if it's /*/
) {
insideComment = false;
continue;
}
// Register the end of a single-line comment
if (insideSingleLineComment && currentChar === "\n") {
insideComment = false;
insideSingleLineComment = false;
}
if (ignoreComments) {
continue;
}
}
// Register the beginning of a string
if (!insideComment && !insideString && (currentChar === "\"" || currentChar === "'")) {
if (source[i - 1] === "\\") {
continue;
} // escaping
openingQuote = currentChar;
insideString = true;
// For string-quotes rule
if (target === currentChar) {
handleMatch(getMatch(i));
}
continue;
}
if (insideString) {
// Register the end of a string
if (currentChar === openingQuote) {
if (source[i - 1] === "\\") {
continue;
} // escaping
insideString = false;
continue;
}
if (ignoreStrings) {
continue;
}
}
// Register the beginning of parens/functions
if (!insideString && !insideComment && currentChar === "(") {
// Keep track of opening parentheses so that we
// know when the outermost function (possibly
// containing nested functions) is closing
openingParenCount++;
insideParens = true;
// Only inside a function if there is a function name
// before the opening paren
if (/[a-zA-Z]/.test(source[i - 1])) {
insideFunction = true;
}
if (target === "(") {
handleMatch(getMatch(i));
}
continue;
}
if (insideParens) {
// Register the end of a function
if (currentChar === ")") {
openingParenCount--;
// Do this here so it's still technically inside a function
if (target === ")") {
handleMatch(getMatch(i));
}
if (openingParenCount === 0) {
insideParens = false;
insideFunction = false;
}
continue;
}
}
// If this char is part of a function name, ignore it
if (!options.checkFunctionNames && /^[a-zA-Z]*\(/.test(source.slice(i))) {
continue;
}
var match = getMatch(i);
if (!match) {
continue;
}
handleMatch(match);
if (options.onlyOne) {
return;
}
}
function handleMatch(match) {
if (options.outsideParens && insideParens) {
return;
}
if (options.withinFunctionalNotation && !insideFunction) {
return;
}
if (options.outsideFunctionalNotation && insideFunction) {
return;
}
if (options.withinStrings && !insideString) {
return;
}
if (options.withinComments && !insideComment) {
return;
}
matchCount++;
callback(match, matchCount);
}
};