Initial commit

This commit is contained in:
Patrick Marsceill
2017-03-09 13:16:08 -05:00
commit b7b0d0d7bf
4147 changed files with 401224 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (atRule) {
// Initial 1 is for the `@`
var index = 1 + atRule.name.length;
if (atRule.raw("afterName")) {
index += atRule.raw("afterName").length;
}
return index;
};

View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (statement) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
noRawBefore = _ref.noRawBefore;
var result = "";
if (statement.type !== "rule" && statement.type !== "atrule") {
return result;
}
if (!noRawBefore) {
result += statement.raws.before;
}
if (statement.type === "rule") {
result += statement.selector;
} else {
result += "@" + statement.name + statement.raws.afterName + statement.params;
}
var between = statement.raws.between;
if (between !== undefined) {
result += between;
}
return result;
};

26
node_modules/stylelint-scss/dist/utils/blockString.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (statement) {
if (!(0, _hasBlock2.default)(statement)) {
return;
}
return (0, _rawNodeString2.default)(statement).slice((0, _beforeBlockString2.default)(statement).length);
};
var _beforeBlockString = require("./beforeBlockString");
var _beforeBlockString2 = _interopRequireDefault(_beforeBlockString);
var _hasBlock = require("./hasBlock");
var _hasBlock2 = _interopRequireDefault(_hasBlock);
var _rawNodeString = require("./rawNodeString");
var _rawNodeString2 = _interopRequireDefault(_rawNodeString);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (text) {
var err = new Error(text);
err.code = 78;
return err;
};

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (decl) {
var beforeColon = decl.toString().indexOf(":");
var afterColon = decl.raw("between").length - decl.raw("between").indexOf(":");
return beforeColon + afterColon;
};

View File

@@ -0,0 +1,218 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = findCommentsInRaws;
var _lodash = require("lodash");
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Finds comments, both CSS comments and double slash ones, in a CSS string
* This helper exists because PostCSS drops some inline comments (those
* between seelctors, property values, etc.)
* https://github.com/postcss/postcss/issues/845#issuecomment-232306259
*
* @param [string] rawString -- the source raw CSS string
* @return [array] array of objects with these props:
* <20> type -- "css" or "double-slash"
* <20> source: { start, end }
* IMPORTANT: the function itself considers \r as a character, and counts
* it for `start` and `end`. But if their values are passed to PostCSS's
* result.warn(), than "\r\n" is consideren ONE CHAR (newline)!
* <20> raws
* raws.startToken -- `/*`, `/**`, `/**!`, etc.
* raws.left -- whitespace after the comment opening marker
* raws.text -- the full comment, including markers (//, /*)
* raws.right -- whitespace before the comment closing marker
* raws.endToken -- `*\/`, `**\/` for CSS comments
* <20> text -- the comment text only, excluding //, /*, trailing whitespaces
* <20> inlineAfter -- true, if there is something before the comment on
* the same line
* <20> inlineBefore -- true, if there is something after the comment on
* the same line
*/
function findCommentsInRaws(rawString) {
var result = [];
var comment = {};
// Keeps track of which structure the parser is inside (string, comment,
// url function, parens). E.g., /* comment */ inside a string doesn't
// constitute a comment, so as url(//path)
var modesEntered = [{
mode: "normal",
character: null
}];
var commentStart = null;
// postcss-scss transforms //-comments into CSS comments, like so:
// `// comment` -> `/* comment*/`. So to have a correct intex we need to
// keep track on the added `*/` sequences
var offset = 0;
for (var i = 0; i < rawString.length; i++) {
var character = rawString[i];
var prevChar = i > 0 ? rawString[i - 1] : null;
var nextChar = i + 1 < rawString.length ? rawString[i + 1] : null;
var lastModeIndex = modesEntered.length - 1;
var mode = modesEntered[lastModeIndex].mode;
switch (character) {
// If entering/exiting a string
case "\"":
case "'":
{
if (mode === "comment") {
break;
}
if (mode === "string" && modesEntered[lastModeIndex].character === character && prevChar !== "\\") {
// Exiting a string
modesEntered.pop();
} else {
// Entering a string
modesEntered.push({
mode: "string",
character: character
});
}
break;
}
// Entering url, other function or parens (only url matters)
case "(":
{
if (mode === "comment" || mode === "string") {
break;
}
var functionNameRegSearch = /(?:^|(?:\n)|(?:\r)|(?:\s-)|[:\s,.(){}*+/%])([a-zA-Z0-9_-]*)$/.exec(rawString.substring(0, i));
// A `\S(` can be in, say, `@media(`
if (!functionNameRegSearch) {
modesEntered.push({
mode: "parens",
character: "("
});
break;
}
var functionName = functionNameRegSearch[1];
modesEntered.push({
mode: functionName === "url" ? "url" : "parens",
character: "("
});
break;
}
// Exiting url, other function or parens
case ")":
{
if (mode === "comment" || mode === "string") {
break;
}
modesEntered.pop();
break;
}
// checking for comment
case "/":
{
// Break if the / is inside a comment because we leap over the second
// slash in // and in */, so the / is not from a marker. Also break
// if inside a string
if (mode === "comment" || mode === "string") {
break;
}
if (nextChar === "*") {
modesEntered.push({
mode: "comment",
character: "/*"
});
comment = {
type: "css",
source: { start: i + offset },
// If i is 0 then the file/the line starts with this comment
inlineAfter: i > 0 && rawString.substring(0, i).search(/\n\s*$/) === -1
};
commentStart = i;
// Skip the next iteration as the * is already checked
i++;
} else if (nextChar === "/") {
// `url(//path/to/file)` has no comment
if (mode === "url") {
break;
}
modesEntered.push({
mode: "comment",
character: "//"
});
comment = {
type: "double-slash",
source: { start: i + offset },
// If i is 0 then the file/the line starts with this comment
inlineAfter: i > 0 && rawString.substring(0, i).search(/\n\s*$/) === -1
};
commentStart = i;
// Skip the next iteration as the second slash in // is already checked
i++;
}
break;
}
// Might be a closing `*/`
case "*":
{
if (mode === "comment" && modesEntered[lastModeIndex].character === "/*" && nextChar === "/") {
comment.source.end = i + 1 + offset;
var commentRaw = rawString.substring(commentStart, i + 2);
var matches = /^(\/\*+[!#]{0,1})(\s*)([\s\S]*?)(\s*?)(\*+\/)$/.exec(commentRaw);
modesEntered.pop();
comment.raws = {
startToken: matches[1],
left: matches[2],
text: commentRaw,
right: matches[4],
endToken: matches[5]
};
comment.text = matches[3];
comment.inlineBefore = rawString.substring(i + 2).search(/^\s*?\S+\s*?\n/) !== -1;
result.push(_lodash2.default.assign({}, comment));
comment = {};
// Skip the next loop as the / in */ is already checked
i++;
}
break;
}
default:
{
var isNewline = character === "\r" && rawString[i + 1] === "\n" || character === "\n" && rawString[i - 1] !== "\r";
// //-comments end before newline and if the code string ends
if (isNewline || i === rawString.length - 1) {
if (mode === "comment" && modesEntered[lastModeIndex].character === "//") {
comment.source.end = (isNewline ? i - 1 : i) + offset;
var _commentRaw = rawString.substring(commentStart, isNewline ? i : i + 1);
var _matches = /^(\/+)(\s*)(.*?)(\s*?)$/.exec(_commentRaw);
modesEntered.pop();
comment.raws = {
startToken: _matches[1],
left: _matches[2],
text: _commentRaw,
right: _matches[4]
};
comment.text = _matches[3];
comment.inlineBefore = false;
result.push(_lodash2.default.assign({}, comment));
comment = {};
// Compensate for the `*/` added by postcss-scss
offset += 2;
}
}
break;
}
}
}
return result;
}

9
node_modules/stylelint-scss/dist/utils/hasBlock.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (statement) {
return statement.nodes !== undefined;
};

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (string) {
return string && (string.indexOf("\n\n") !== -1 || string.indexOf("\n\r\n") !== -1);
};

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (selector) {
for (var i = 0; i < selector.length; i++) {
if (selector[i] !== "&") {
continue;
}
if (!_lodash2.default.isUndefined(selector[i - 1]) && !isCombinator(selector[i - 1])) {
return true;
}
if (!_lodash2.default.isUndefined(selector[i + 1]) && !isCombinator(selector[i + 1])) {
return true;
}
}
return false;
};
var _lodash = require("lodash");
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function isCombinator(x) {
return (/[\s+>~]/.test(x)
);
}
/**
* Check whether a selector has an interpolating ampersand
* An "interpolating ampersand" is an "&" used to interpolate within another
* simple selector (e.g. `&-modifier`), rather than an "&" that stands
* on its own as a simple selector (e.g. `& .child`)
*
* @param {string} selector
* @return {boolean} If `true`, the selector has an interpolating ampersand
*/

205
node_modules/stylelint-scss/dist/utils/index.js generated vendored Normal file
View File

@@ -0,0 +1,205 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _atRuleParamIndex = require("./atRuleParamIndex");
Object.defineProperty(exports, "atRuleParamIndex", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_atRuleParamIndex).default;
}
});
var _beforeBlockString = require("./beforeBlockString");
Object.defineProperty(exports, "beforeBlockString", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_beforeBlockString).default;
}
});
var _blockString = require("./blockString");
Object.defineProperty(exports, "blockString", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_blockString).default;
}
});
var _declarationValueIndex = require("./declarationValueIndex");
Object.defineProperty(exports, "declarationValueIndex", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_declarationValueIndex).default;
}
});
var _findCommentsInRaws = require("./findCommentsInRaws");
Object.defineProperty(exports, "findCommentsInRaws", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_findCommentsInRaws).default;
}
});
var _hasInterpolatingAmpersand = require("./hasInterpolatingAmpersand");
Object.defineProperty(exports, "hasInterpolatingAmpersand", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_hasInterpolatingAmpersand).default;
}
});
var _hasBlock = require("./hasBlock");
Object.defineProperty(exports, "hasBlock", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_hasBlock).default;
}
});
var _hasEmptyLine = require("./hasEmptyLine");
Object.defineProperty(exports, "hasEmptyLine", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_hasEmptyLine).default;
}
});
var _isInlineComment = require("./isInlineComment");
Object.defineProperty(exports, "isInlineComment", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_isInlineComment).default;
}
});
var _isSingleLineString = require("./isSingleLineString");
Object.defineProperty(exports, "isSingleLineString", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_isSingleLineString).default;
}
});
var _isStandardRule = require("./isStandardRule");
Object.defineProperty(exports, "isStandardRule", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_isStandardRule).default;
}
});
var _isStandardSelector = require("./isStandardSelector");
Object.defineProperty(exports, "isStandardSelector", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_isStandardSelector).default;
}
});
var _isStandardSyntaxProperty = require("./isStandardSyntaxProperty");
Object.defineProperty(exports, "isStandardSyntaxProperty", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_isStandardSyntaxProperty).default;
}
});
var _isWhitespace = require("./isWhitespace");
Object.defineProperty(exports, "isWhitespace", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_isWhitespace).default;
}
});
var _namespace = require("./namespace");
Object.defineProperty(exports, "namespace", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_namespace).default;
}
});
var _optionsHaveException = require("./optionsHaveException");
Object.defineProperty(exports, "optionsHaveException", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_optionsHaveException).default;
}
});
var _optionsHaveIgnored = require("./optionsHaveIgnored");
Object.defineProperty(exports, "optionsHaveIgnored", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_optionsHaveIgnored).default;
}
});
var _parseNestedPropRoot = require("./parseNestedPropRoot");
Object.defineProperty(exports, "parseNestedPropRoot", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_parseNestedPropRoot).default;
}
});
var _parseSelector = require("./parseSelector");
Object.defineProperty(exports, "parseSelector", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_parseSelector).default;
}
});
var _sassValueParser = require("./sassValueParser");
Object.defineProperty(exports, "findOperators", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_sassValueParser).default;
}
});
var _rawNodeString = require("./rawNodeString");
Object.defineProperty(exports, "rawNodeString", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_rawNodeString).default;
}
});
var _whitespaceChecker = require("./whitespaceChecker");
Object.defineProperty(exports, "whitespaceChecker", {
enumerable: true,
get: function get() {
return _interopRequireDefault(_whitespaceChecker).default;
}
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = isInlineComment;
/**
* Check if a comment is inline one (i.e. on the same line as some non-comment
* code). Only works with comments that are not ignored by PostCSS. To work
* with those that are ignored use `findCommentInRaws`
*
* @param {Comment} comment - PostCSS comment node
* @return {boolean} true, if the comment is an inline one
*/
function isInlineComment(comment) {
var nextNode = comment.next();
var isBeforeSomething = !!nextNode && nextNode.type !== "comment" && comment.source.end.line === nextNode.source.start.line;
var isAfterSomething = comment.raws.before.search(/\n/) === -1;
return isAfterSomething || isBeforeSomething;
}

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (input) {
return !/[\n\r]/.test(input);
};

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (rule) {
// Get full selector
var selector = _lodash2.default.get(rule, "raws.selector.raw", rule.selector);
// Custom property set (e.g. --custom-property-set: {})
if (_lodash2.default.endsWith(selector, ":")) {
return false;
}
// Called Less mixin (e.g. a { .mixin() })
if (rule.ruleWithoutBody) {
return false;
}
// Non-outputting Less mixin definition (e.g. .mixin() {})
if (_lodash2.default.endsWith(selector, ")") && !_lodash2.default.includes(selector, ":")) {
return false;
}
return true;
};
var _lodash = require("lodash");
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (selector) {
// SCSS or Less interpolation
if (/#{.+?}|@{.+?}|\$\(.+?\)/.test(selector)) {
return false;
}
return true;
};

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (property) {
// SCSS var (e.g. $var: x), list (e.g. $list: (x)) or map (e.g. $map: (key:value))
if (property[0] === "$") {
return false;
}
// Less var (e.g. @var: x)
if (property[0] === "@") {
return false;
}
// SCSS or Less interpolation
if (/#{.+?}|@{.+?}|\$\(.+?\)/.test(property)) {
return false;
}
return true;
};

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (char) {
return [" ", "\n", "\t", "\r", "\f"].indexOf(char) !== -1;
};

11
node_modules/stylelint-scss/dist/utils/namespace.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = namespace;
var prefix = "scss";
function namespace(ruleName) {
return prefix + "/" + ruleName;
}

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (options, exceptionName) {
return options && options.except && options.except.indexOf(exceptionName) !== -1;
};

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (options, ignoredName) {
return options && options.ignore && options.ignore.indexOf(ignoredName) !== -1;
};

View File

@@ -0,0 +1,88 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = parseNestedPropRoot;
/**
* Attempts to parse a selector as if it"s a root for a group of nested props
* E.g.: `margin: {`, `font: 10px/1.1 Arial {` ("{" excluded)
*/
function parseNestedPropRoot(propString) {
var modesEntered = [{
mode: "normal",
character: null,
isCalculationEnabled: true
}];
var result = {};
var lastModeIndex = 0;
var propName = "";
for (var i = 0; i < propString.length; i++) {
var character = propString[i];
// If entering/exiting a string
if (character === "\"" || character === "'") {
if (modesEntered[lastModeIndex].isCalculationEnabled === true) {
modesEntered.push({
mode: "string",
isCalculationEnabled: false,
character: character
});
lastModeIndex++;
} else if (modesEntered[lastModeIndex].mode === "string" && modesEntered[lastModeIndex].character === character && propString[i - 1] !== "\\") {
modesEntered.pop();
lastModeIndex--;
}
}
// If entering/exiting interpolation
if (character === "{") {
modesEntered.push({
mode: "interpolation",
isCalculationEnabled: true
});
lastModeIndex++;
} else if (character === "}") {
modesEntered.pop();
lastModeIndex--;
}
// Check for : outside fn call, string or interpolation. It must be at the
// end of a string or have a whitespace between it and following value
if (modesEntered[lastModeIndex].mode === "normal" && character === ":") {
var propValueStr = propString.substring(i + 1);
if (propValueStr.length) {
var propValue = {
before: /^(\s*)/.exec(propValueStr)[1],
value: propValueStr.trim()
};
// It's a declaration if 1) there is a whitespace after :, or
// 2) the value is a number with/without a unit (starts with a number
// or a dot), or
// 3) the value is a variable (starts with $), or
// 4) the value a string, surprisingly
if (propValue.before === "" && !/^[0-9.$'"]/.test(propValue.value)) {
return null;
}
// +1 for the colon
propValue.sourceIndex = propValue.before.length + i + 1;
result.propValue = propValue;
}
result.propName = {
after: /(\s*)$/.exec(propName)[1],
value: propName.trim()
};
return result;
}
propName += character;
}
return null;
}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (selector, result, node, cb) {
try {
(0, _postcssSelectorParser2.default)(cb).process(selector);
} catch (e) {
result.warn("Cannot parse selector", { node: node });
}
};
var _postcssSelectorParser = require("postcss-selector-parser");
var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (node) {
var result = "";
if (node.raws.before) {
result += node.raws.before;
}
result += node.toString();
return result;
};

View File

@@ -0,0 +1,860 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = findOperators;
exports.mathOperatorCharType = mathOperatorCharType;
/**
* Processes a string and finds Sass operators in it
*
* @param {Object} args - Named arguments object
* @param {String} args.string - the input string
* @param {Number} args.index - the position of args.string from the start of the line
* @param {Boolean} args.isAfterColon - pass "true" if the string is
* a variable value, a mixin/function parameter default.
* In such cases + and / tend to be operations more often
* @param {Function} args.callback - will be called on every instance of
* an operator. Accepts parameters:
* • string - the default source string
* • globalIndex - the string's position in the outer input
* • startIndex - index in string, where the operator starts
* • endIndex - index in string, where the operator ends (for `==`, etc.)
*
* @return {Array} array of { symbol, globalIndex, startIndex, endIndex }
* for each operator found within a string
*/
function findOperators(_ref) {
var string = _ref.string,
globalIndex = _ref.globalIndex,
isAfterColon = _ref.isAfterColon,
callback = _ref.callback;
var mathOperators = ["+", "/", "-", "*", "%"];
// A stack of modes activated for the current char: string, interpolation
// Calculations inside strings are not processed, so spaces are not linted
var modesEntered = [{
mode: "normal",
isCalculationEnabled: true,
character: null
}];
var result = [];
var lastModeIndex = 0;
for (var i = 0; i < string.length; i++) {
var character = string[i];
var substringStartingWithIndex = string.substring(i);
// If entering/exiting a string
if (character === "\"" || character === "'") {
if (modesEntered[lastModeIndex].isCalculationEnabled === true) {
modesEntered.push({
mode: "string",
isCalculationEnabled: false,
character: character
});
lastModeIndex++;
} else if (modesEntered[lastModeIndex].mode === "string" && modesEntered[lastModeIndex].character === character && string[i - 1] !== "\\") {
modesEntered.pop();
lastModeIndex--;
}
}
// If entering/exiting interpolation (may be inside a string)
// Comparing with length-2 because `#{` at the very end doesnt matter
if (character === "#" && i + 1 < string.length - 2 && string[i + 1] === "{") {
modesEntered.push({
mode: "interpolation",
isCalculationEnabled: true
});
lastModeIndex++;
} else if (character === "}") {
modesEntered.pop();
lastModeIndex--;
}
// Don't lint if inside a string
if (modesEntered[lastModeIndex].isCalculationEnabled === false) {
continue;
}
// If it's a math operator
if (mathOperators.indexOf(character) !== -1 && mathOperatorCharType(string, i, isAfterColon) === "op" ||
// or is "<" or ">"
substringStartingWithIndex.search(/^[<>]([^=]|$)/) !== -1) {
result.push({
symbol: string[i],
globalIndex: globalIndex,
startIndex: i,
endIndex: i
});
if (callback) {
callback(string, globalIndex, i, i);
}
}
// "<=", ">=", "!=", "=="
if (substringStartingWithIndex.search(/^[><=!]=/) !== -1) {
result.push({
symbol: string[i],
globalIndex: globalIndex,
startIndex: i,
endIndex: i + 1
});
if (callback) {
callback(string, globalIndex, i, i + 1);
}
}
}
// result.length > 0 && console.log(string, result)
return result;
}
/**
* Checks if a character is an operator, a sign (+ or -), or part of a string
*
* @param {String} string - the source string
* @param {Number} index - the index of the character in string to check
* @param {Boolean} isAfterColon - if the value string a variable
* value, a mixin/function parameter default. In such cases + and / tend
* to be operations more often
* @return {String|false}
* • "op", if the character is a operator in a math/string operation
* • "sign" if it is a + or - before a numeric,
* • "char" if it is a part of a string,
* • false - if it is none from above (most likely an error)
*/
function mathOperatorCharType(string, index, isAfterColon) {
// !Checking here to prevent unnecessary calculations and deep recursion
// when calling isPrecedingOperator()
if (["+", "/", "-", "*", "%"].indexOf(string[index]) === -1) {
return "char";
}
var character = string[index];
// console.log(string)
// ---- Processing + characters
if (character === "+") {
// console.log('checking plus')
return checkPlus(string, index, isAfterColon);
}
// ---- Processing - characters
if (character === "-") {
return checkMinus(string, index);
}
// ---- Processing * character
if (character === "*") {
return "op";
}
// ---- Processing % character
if (character === "%") {
return checkPercent(string, index);
}
// ---- Processing / character
// http://sass-lang.com/documentation/file.SASS_REFERENCE.html#division-and-slash
if (character === "/") {
return checkSlash(string, index, isAfterColon);
}
// console.log("nothing applies")
return "char";
}
// --------------------------------------------------------------------------
// Functions for checking particular characterd (+, -, /)
// --------------------------------------------------------------------------
/**
* Checks the specified `+` char type: operator, sign (+ or -), part of string
*
* @param {String} string - the source string
* @param {Number} index - the index of the character in string to check
* @param {Boolean} isAftercolon - if the value string a variable
* value, a mixin/function parameter default. In such cases + is always an
* operator if surrounded by numbers/values with units
* @return {String|false}
* • "op", if the character is a operator in a math/string operation
* • "sign" if it is a sign before a positive number,
* • false - if it is none from above (most likely an error)
*/
function checkPlus(string, index, isAftercolon) {
var before = string.substring(0, index);
var after = string.substring(index + 1);
// If the character is at the beginning of the input
var isAtStart_ = isAtStart(string, index);
// If the character is at the end of the input
var isAtEnd_ = isAtEnd(string, index);
var isWhitespaceBefore = before.search(/\s$/) !== -1;
var isWhitespaceAfter = after.search(/^\s/) !== -1;
var isValueWithUnitAfter_ = isValueWithUnitAfter(after);
var isNumberAfter_ = isNumberAfter(after);
var isInterpolationAfter_ = isInterpolationAfter(after);
// The early check above helps prevent deep recursion here
var isPrecedingOperator_ = isPrecedingOperator(string, index);
if (isAtStart_) {
// console.log("+, `+<sth>` or `+ <sth>`")
return "sign";
}
// E.g. `1+1`, `string+#fff`
if (!isAtStart_ && !isWhitespaceBefore && !isAtEnd_ && !isWhitespaceAfter) {
// E.g. `1-+1`
if (isPrecedingOperator_) {
// console.log('1+1')
return "sign";
}
// console.log("+, no spaces")
return "op";
}
// e.g. `something +something`
if (!isAtEnd_ && !isWhitespaceAfter) {
// e.g. `+something`, ` ... , +something`, etc.
if (isNoOperandBefore(string, index)) {
// console.log("+, nothing before")
return "sign";
}
// e.g. `sth +10px`, `sth +1`
if (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween) {
if (isAftercolon === true) {
// console.log(": 10px +1")
return "op";
}
// e.g. `(sth +10px)`, `fun(sth +1)`
if (isInsideParens(string, index) || isInsideFunctionCall(string, index).is) {
// console.log("+10px or +1, inside function or parens")
return "op";
}
// e.g. `#{10px +1}`
if (isInsideInterpolation(string, index)) {
// console.log('+, #{10px +1}')
return "op";
}
// console.log('+, default')
return "sign";
}
// e.g. `sth +#fff`, `sth +string`, `sth +#{...}`, `sth +$var`
if (isStringAfter(after) || isHexColorAfter(after) || after[0] === "$" || isInterpolationAfter_.is && !isInterpolationAfter_.opsBefore) {
// e.g. `sth+ +string`
if (isPrecedingOperator_) {
// console.log("+10px or +1, before is an operator")
return "sign";
}
// console.log("+#000, +string, +#{sth}, +$var")
return "op";
}
// console.log('sth +sth, default')
return "op";
}
// If the + is after a value, e.g. `$var+`
if (!isAtStart_ && !isWhitespaceBefore) {
// It is always an operator. Prior to Sass 4, `#{...}+` was differernt,
// but that's not logical and had been fixed.
// console.log('1+ sth')
return "op";
}
// If it has whitespaces on both sides
// console.log('sth + sth')
return "op";
}
/**
* Checks the specified `-` character: operator, sign (+ or -), part of string
*
* @param {String} string - the source string
* @param {Number} index - the index of the character in string to check
* @return {String|false}
* • "op", if the character is a operator in a math/string operation
* • "sign" if it is a sign before a negative number,
* • "char" if it is a part of a string or identifier,
* • false - if it is none from above (most likely an error)
*/
function checkMinus(string, index) {
var before = string.substring(0, index);
var after = string.substring(index + 1);
// If the character is at the beginning of the input
var isAtStart_ = isAtStart(string, index);
// If the character is at the end of the input
var isAtEnd_ = isAtEnd(string, index);
var isWhitespaceBefore = before.search(/\s$/) !== -1;
var isWhitespaceAfter = after.search(/^\s/) !== -1;
var isValueWithUnitAfter_ = isValueWithUnitAfter(after);
var isValueWithUnitBefore_ = isValueWithUnitBefore(before);
var isNumberAfter_ = isNumberAfter(after);
var isNumberBefore_ = isNumberBefore(before);
var isInterpolationAfter_ = isInterpolationAfter(after);
var isParensAfter_ = isParensAfter(after);
var isParensBefore_ = isParensBefore(before);
// The early check above helps prevent deep recursion here
var isPrecedingOperator_ = isPrecedingOperator(string, index);
if (isAtStart_) {
// console.log("-, -<sth> or - <sth>")
return "sign";
}
// `10 - 11`
if (!isAtEnd_ && !isAtStart_ && isWhitespaceBefore && isWhitespaceAfter) {
// console.log("-, Op: 10px - 10px")
return "op";
}
// e.g. `something -10px`
if (!isAtEnd_ && !isAtStart_ && isWhitespaceBefore && !isWhitespaceAfter) {
if (isParensAfter_.is && !isParensAfter_.opsBefore) {
// console.log("-, Op: <sth> -(...)")
return "op";
}
// e.g. `sth -1px`, `sth -1`.
// Always a sign, even inside parens/function args
if (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween) {
// console.log("-, sign: -1px or -1")
return "sign";
}
// e.g. `sth --1`, `sth +-2px`
if (isValueWithUnitAfter_.is && isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && isNumberAfter_.opsBetween) {
// console.log("-, op: --1px or --1")
return "op";
}
// `<sth> -string`, `<sth> -#{...}`
if (isStringAfter(after) || isInterpolationAfter_.is && !isInterpolationAfter_.opsBefore) {
// console.log("-, char: -#{...}")
return "char";
}
// e.g. `#0af -#f0a`, and edge-cases can take a hike
if (isHexColorAfter(after) && isHexColorBefore(before.trim())) {
// console.log("-, op: #fff-, -#fff")
return "op";
}
// If the - is before a variable, than it's most likely an operator
if (after[0] === "$") {
if (isPrecedingOperator_) {
// console.log("-, sign: -$var, another operator before")
return "sign";
}
// console.log("-, op: -$var, NO other operator before")
return "op";
}
// By default let's make it an sign for now
// console.log('-, sign: default in <sth> -<sth>')
return "sign";
}
// No whitespace before,
// e.g. `10x- something`
if (!isAtEnd_ && !isAtStart_ && !isWhitespaceBefore && isWhitespaceAfter) {
if (isParensBefore_) {
// console.log('-, op: `(...)- <sth>`')
return "op";
}
if (isNumberBefore(before) || isHexColorBefore(before)) {
// console.log('`-, op: 10- <sth>, #aff- <sth>`')
return "op";
}
// console.log('-, char: default in <sth>- <sth>')
return "char";
}
// NO Whitespace,
// e.g. `10px-1`
if (!isAtEnd_ && !isAtStart_ && !isWhitespaceBefore && !isWhitespaceAfter) {
// console.log('no spaces')
// `<something>-1`, `<something>-10px`
if (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween) {
// `10px-1`, `1-10px`, `1-1`, `1x-1x`
if (isValueWithUnitBefore_ || isNumberBefore_) {
// console.log("-, op: 1-10px")
return "op";
}
// The - could be a "sign" here, but for now "char" does the job
}
// `1-$var`
if (isNumberBefore_ && after[0] === "$") {
// console.log("-, op: 1-$var")
return "op";
}
// `fn()-10px`
if (isFunctionBefore(before) && (isNumberAfter_.is && !isNumberAfter_.opsBetween || isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween)) {
// console.log("-, op: fn()-10px")
return "op";
}
}
// And in all the other cases it's a characher inside a string
// console.log("-, default: char")
return "char";
}
/**
* Checks the specified `/` character: operator, sign (+ or -), part of string
*
* @param {String} string - the source string
* @param {Number} index - the index of the character in string to check
* @param {Boolean} isAfterColon - if the value string a variable
* value, a mixin/function parameter default. In such cases / is always an
* operator if surrounded by numbers/values with units
* @return {String|false}
* • "op", if the character is a operator in a math/string operation
* • "char" if it gets compiled as-is, e.g. `font: 10px/1.2;`,
* • false - if it is none from above (most likely an error)
*/
function checkSlash(string, index, isAftercolon) {
// Trimming these, as spaces before/after a slash don't matter
var before = string.substring(0, index).trim();
var after = string.substring(index + 1).trim();
var isValueWithUnitAfter_ = isValueWithUnitAfter(after);
var isValueWithUnitBefore_ = isValueWithUnitBefore(before);
var isNumberAfter_ = isNumberAfter(after);
var isNumberBefore_ = isNumberBefore(before);
var isParensAfter_ = isParensAfter(after);
var isParensBefore_ = isParensBefore(before);
// FIRST OFF. Interpolation on any of the sides is a NO-GO for division op
if (isInterpolationBefore(before).is || isInterpolationAfter(after).is) {
// console.log("/, interpolation")
return "char";
}
// e.g. `10px/normal`
if (isStringBefore(before).is || isStringAfter(after)) {
// console.log("/, string")
return "char";
}
// For all other value options (numbers, value+unit, hex color)
// `$var/1`, `#fff/-$var`
// Here we don't care if there is a sign before the var
if (isVariableBefore(before) || isVariableAfter(after).is) {
// console.log("/, variable")
return "op";
}
if (isFunctionBefore(before) || isFunctionAfter(after).is) {
// console.log("/, function as operand")
return "op";
}
if (isParensBefore_ || isParensAfter_.is) {
// console.log("/, function as operand")
return "op";
}
// `$var: 10px/2; // 5px`
if (isAftercolon === true && (isValueWithUnitAfter_.is || isNumberAfter_.is) && (isValueWithUnitBefore_ || isNumberBefore_)) {
return "op";
}
// Quick check of the following operator symbol - if it is a math operator
if (
// +, *, % count as operators unless after interpolation or at the start
before.search(/[^{,(}\s]\s*[+*%]\s*[^(){},]+$/) !== -1 ||
// We consider minus as op only if surrounded by whitespaces (` - `);
before.search(/[^{,(}\s]\s+-\s+[^(){},]+$/) !== -1 ||
// `10/2 * 3`, `10/2 % 3`, with or without spaces
after.search(/^[^(){},]+[*%]/) !== -1 ||
// `10px/2px+1`, `10px/2px+ 1`
after.search(/^[^(){},\s]+[+]/) !== -1 ||
// Anything but `10px/2px +1`, `10px/2px +1px`
after.search(/^[^(){},\s]+\s+(\+\D)/) !== -1 ||
// Following ` -`: only if `$var` after (`10/10 -$var`)
after.search(/^[^(){},\s]+\s+-(\$|\s)/) !== -1 ||
// Following `-`: only if number after (`10s/10s-10`, `10s/10s-.1`)
after.search(/^[^(){},\s]+-(\.){0,1}\d/) !== -1 ||
// Or if there is a number before anything but string after (not `10s/1-str`,)
after.search(/^(\d*\.){0,1}\d+-\s*[^#a-zA-Z_\s]/) !== -1) {
// console.log("/, math op around")
return "op";
}
// e.g. `(1px/1)`, `fn(7 / 15)`, but not `url(8/11)`
var isInsideFn = isInsideFunctionCall(string, index);
if (isInsideParens(string, index) || isInsideFn.is && isInsideFn.fn !== "url") {
// console.log("/, parens or function arg")
return "op";
}
// console.log("/, default")
return "char";
}
/**
* Checks the specified `%` character: operator or part of value
*
* @param {String} string - the source string
* @param {Number} index - the index of the character in string to check
* @return {String|false}
* • "op", if the character is a operator in a math/string operation
* • "char" if it gets compiled as-is, e.g. `width: 10%`,
* • false - if it is none from above (most likely an error)
*/
function checkPercent(string, index) {
// Trimming these, as spaces before/after a slash don't matter
var before = string.substring(0, index);
var after = string.substring(index + 1);
// If the character is at the beginning of the input
var isAtStart_ = isAtStart(string, index);
// If the character is at the end of the input
var isAtEnd_ = isAtEnd(string, index);
var isWhitespaceBefore = before.search(/\s$/) !== -1;
var isWhitespaceAfter = after.search(/^\s/) !== -1;
var isParensBefore_ = isParensBefore(before);
// FIRST OFF. Interpolation on any of the sides is a NO-GO
if (isInterpolationBefore(before.trim()).is || isInterpolationAfter(after.trim()).is) {
// console.log("%, interpolation")
return "char";
}
if (isAtStart_ || isAtEnd_) {
// console.log("%, start/end")
return "char";
}
// In `<sth> %<sth>` it's most likely an operator (except for inteprolation
// checked above)
if (isWhitespaceBefore && !isWhitespaceAfter) {
// console.log("%, `<sth> %<sth>`")
return "op";
}
// `$var% 1`, `$var%1`, `$var%-1`
if (isVariableBefore(before) || isParensBefore_) {
// console.log("%, after a variable, function or parens")
return "op";
}
// in all other cases in `<sth>% <sth>` it is most likely a unit
if (!isWhitespaceBefore && isWhitespaceAfter) {
// console.log("%, `<sth>% <sth>`")
return "char";
}
// console.log("%, default")
return "char";
}
// --------------------------------------------------------------------------
// Lots of elementary helpers
// --------------------------------------------------------------------------
function isAtStart(string, index) {
var before = string.substring(0, index).trim();
return before.length === 0 || before.search(/[({,]$/) !== -1;
}
function isAtEnd(string, index) {
var after = string.substring(index + 1).trim();
return after.length === 0 || after.search(/^[,)}]/) !== -1;
}
function isInsideParens(string, index) {
var before = string.substring(0, index).trim();
var after = string.substring(index + 1).trim();
if (before.search(/(?:^|[,{]|\s)\(\s*[^(){},]+$/) !== -1 && after.search(/^[^(){},\s]+\s*\)/) !== -1) {
return true;
}
return false;
}
function isInsideInterpolation(string, index) {
var before = string.substring(0, index).trim();
if (before.search(/#\{[^}]*$/) !== -1) {
return true;
}
return false;
}
/**
* Checks if the character is inside a function agruments
*
* @param {String} string - the input string
* @param {Number} index - current character index
* @return {Object} return
* {Boolean} return.is - if inside a function arguments
* {String} return.fn - function name
*/
function isInsideFunctionCall(string, index) {
var result = { is: false, fn: null };
var before = string.substring(0, index).trim();
var after = string.substring(index + 1).trim();
var beforeMatch = before.match(/([a-zA-Z_-][a-zA-Z0-9_-]*)\([^(){},]+$/);
if (beforeMatch && beforeMatch[0] && after.search(/^[^({},]+\)/) !== -1) {
result.is = true;
result.fn = beforeMatch[1];
}
return result;
}
/**
* Checks if there is a string before the character.
* Also checks if there is a math operator in between
*
* @param {String} before - the input string that preceses the character
* @return {Object} return
* {Boolean} return.is - if there is a string
* {String} return.opsBetween - if there are operators in between
*/
function isStringBefore(before) {
var result = { is: false, opsBetween: false };
var stringOpsClipped = before.replace(/(\s*[+/*%]|\s+-)+$/, "");
if (stringOpsClipped !== before) {
result.opsBetween = true;
}
// If it is quoted
if (stringOpsClipped[stringOpsClipped.length - 1] == "\"" || stringOpsClipped[stringOpsClipped.length - 1] == "'") {
result.is = true;
} else if (stringOpsClipped.search(/(?:^|[/(){},: ])([a-zA-Z_][a-zA-Z_0-9-]*|-+[a-zA-Z_]+[a-zA-Z_0-9-]*)$/) !== -1) {
// First pattern: a1, a1a, a-1,
result.is = true;
}
return result;
}
function isStringAfter(after) {
var stringTrimmed = after.trim();
// If it is quoted
if (stringTrimmed[0] == "\"" || stringTrimmed[0] == "'") return true;
// e.g. `a1`, `a1a`, `a-1`, and even `--s323`
if (stringTrimmed.search(/^([a-zA-Z_][a-zA-Z_0-9-]*|-+[a-zA-Z_]+[a-zA-Z_0-9-]*)(?:$|[)}, ])/) !== -1) return true;
return false;
}
function isInterpolationAfter(after) {
var result = { is: false, opsBetween: false };
var matches = after.match(/^\s*([+/*%-]\s*)*#{/);
if (matches) {
if (matches[0]) {
result.is = true;
}
if (matches[1]) {
result.opsBetween = true;
}
}
return result;
}
function isParensAfter(after) {
var result = { is: false, opsBetween: false };
var matches = after.match(/^\s*([+/*%-]\s*)*\(/);
if (matches) {
if (matches[0]) {
result.is = true;
}
if (matches[1]) {
result.opsBetween = true;
}
}
return result;
}
function isParensBefore(before) {
return before.search(/\)\s*$/) !== -1;
}
/**
* Checks if there is an interpolation before the character.
* Also checks if there is a math operator in between
*
* @param {String} before - the input string that preceses the character
* @return {Object} return
* {Boolean} return.is - if there is an interpolation
* {String} return.opsBetween - if there are operators in between
*/
function isInterpolationBefore(before) {
var result = { is: false, opsBetween: false };
// Removing preceding operators if any
var beforeOpsClipped = before.replace(/(\s*[+/*%-])+$/, "");
if (beforeOpsClipped !== before) {
result.opsBetween = true;
}
if (beforeOpsClipped[beforeOpsClipped.length - 1] === "}") {
result.is = true;
}
return result;
}
function isValueWithUnitBefore(before) {
// 1px, 0.1p-x, .2p-, 11.2pdf-df1df_
// Surprisingly, ` d.10px` - .10px is separated from a sequence
// and is considered a value with a unit
if (before.trim().search(/(^|[/(, ]|\.)\d[a-zA-Z_0-9-]+$/) !== -1) {
return true;
}
return false;
}
function isValueWithUnitAfter(after) {
var result = { is: false, opsBetween: false };
// 1px, 0.1p-x, .2p-, 11.2pdf-dfd1f_
// Again, ` d.10px` - .10px is separated from a sequence
// and is considered a value with a unit
var matches = after.match(/^\s*([+/*%-]\s*)*(\d+(\.\d+){0,1}|\.\d+)[a-zA-Z_0-9-]+(?:$|[)}, ])/);
if (matches) {
if (matches[0]) {
result.is = true;
}
if (matches[1]) {
result.opsBetween = true;
}
}
return result;
}
function isNumberAfter(after) {
var result = { is: false, opsBetween: false };
var matches = after.match(/^\s*([+/*%-]\s*)*(\d+(\.\d+){0,1}|\.\d+)(?:$|[)}, ])/);
if (matches) {
if (matches[0]) {
result.is = true;
}
if (matches[1]) {
result.opsBetween = true;
}
}
return result;
}
function isNumberBefore(before) {
if (before.trim().search(/(?:^|[/(){},\s])(\d+(\.\d+){0,1}|\.\d+)$/) !== -1) {
return true;
}
return false;
}
function isVariableBefore(before) {
return before.trim().search(/\$[a-zA-Z_0-9-]+$/) !== -1;
}
function isVariableAfter(after) {
var result = { is: false, opsBetween: false };
var matches = after.match(/^\s*([+/*%-]\s*)*\$/);
if (matches) {
if (matches[0]) {
result.is = true;
}
if (matches[1]) {
result.opsBetween = true;
}
}
return result;
}
function isFunctionBefore(before) {
return before.trim().search(/[a-zA-Z0-9_-]\(.*?\)\s*$/) !== -1;
}
function isFunctionAfter(after) {
var result = { is: false, opsBetween: false };
// `-fn()` is a valid function name, so if a - should be a sign/operator,
// it must have a space after
var matches = after.match(/^\s*(-\s+|[+/*%]\s*)*[a-zA_Z_-][a-zA-Z_0-9-]*\(/);
if (matches) {
if (matches[0]) {
result.is = true;
}
if (matches[1]) {
result.opsBetween = true;
}
}
return result;
}
/**
* Checks if the input string is a hex color value
*
* @param {String} string - the input
* @return {Boolean} true, if the input is a hex color
*/
function isHexColor(string) {
return string.trim().search(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/) !== -1;
}
function isHexColorAfter(after) {
var afterTrimmed = after.match(/(.*?)(?:[)},+/*%-]|\s|$)/)[1].trim();
return isHexColor(afterTrimmed);
}
function isHexColorBefore(before) {
if (before.search(/(?:[/(){},+/*%-\s]|^)#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/) !== -1) {
return true;
}
return false;
}
/**
* Checks if there is no operand before the currenc char
* In other words, the current char is at the start of a possible operation,
* e.g. at the string start, after the opening paren or after a comma
*
* @param {String} string - the input string
* @param {Number} index - current char's position in string
* @return {Boolean}
*/
function isNoOperandBefore(string, index) {
var before = string.substring(0, index).trim();
return before.length === 0 || before.search(/[({,]&/) !== -1;
}
function isPrecedingOperator(string, index) {
var prevCharIndex = -1;
for (var i = index - 1; i >= 0; i--) {
if (string[i].search(/\s/) === -1) {
prevCharIndex = i;
break;
}
}
if (prevCharIndex === -1) {
return false;
}
if (mathOperatorCharType(string, prevCharIndex) === "op") {
return true;
}
return false;
}

View File

@@ -0,0 +1,339 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (targetWhitespace, expectation, messages) {
// Keep track of active arguments in order to avoid passing
// too much stuff around, making signatures long and confusing.
// This variable gets reset anytime a checking function is called.
var activeArgs = void 0;
/**
* Check for whitespace *before* a character.
*
* @param {object} args - Named arguments object
* @param {string} args.source - The source string
* @param {number} args.index - The index of the character to check before
* @param {function} args.err - If a violation is found, this callback
* will be invoked with the relevant warning message.
* Typically this callback will report() the violation.
* @param {function} args.errTarget - If a violation is found, this string
* will be sent to the relevant warning message.
* @param {string} [args.lineCheckStr] - Single- and multi-line checkers
* will use this string to determine whether they should proceed,
* i.e. if this string is one line only, single-line checkers will check,
* multi-line checkers will ignore.
* If none is passed, they will use `source`.
* @param {boolean} [args.onlyOneChar=false] - Only check *one* character before.
* By default, "always-*" checks will look for the `targetWhitespace` one
* before and then ensure there is no whitespace two before. This option
* bypasses that second check.
* @param {boolean} [args.allowIndentation=false] - Allow arbitrary indentation
* between the `targetWhitespace` (almost definitely a newline) and the `index`.
* With this option, the checker will see if a newline *begins* the whitespace before
* the `index`.
*/
function before(_ref) {
var source = _ref.source,
index = _ref.index,
err = _ref.err,
errTarget = _ref.errTarget,
lineCheckStr = _ref.lineCheckStr,
_ref$onlyOneChar = _ref.onlyOneChar,
onlyOneChar = _ref$onlyOneChar === undefined ? false : _ref$onlyOneChar,
_ref$allowIndentation = _ref.allowIndentation,
allowIndentation = _ref$allowIndentation === undefined ? false : _ref$allowIndentation;
activeArgs = { source: source, index: index, err: err, errTarget: errTarget, onlyOneChar: onlyOneChar, allowIndentation: allowIndentation };
switch (expectation) {
case "always":
expectBefore();
break;
case "never":
rejectBefore();
break;
case "always-single-line":
if (!(0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
expectBefore(messages.expectedBeforeSingleLine);
break;
case "never-single-line":
if (!(0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
rejectBefore(messages.rejectedBeforeSingleLine);
break;
case "always-multi-line":
if ((0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
expectBefore(messages.expectedBeforeMultiLine);
break;
case "never-multi-line":
if ((0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
rejectBefore(messages.rejectedBeforeMultiLine);
break;
default:
throw (0, _configurationError2.default)("Unknown expectation \"" + expectation + "\"");
}
}
/**
* Check for whitespace *after* a character.
*
* Parameters are pretty much the same as for `before()`, above, just substitute
* the word "after" for "before".
*/
function after(_ref2) {
var source = _ref2.source,
index = _ref2.index,
err = _ref2.err,
errTarget = _ref2.errTarget,
lineCheckStr = _ref2.lineCheckStr,
_ref2$onlyOneChar = _ref2.onlyOneChar,
onlyOneChar = _ref2$onlyOneChar === undefined ? false : _ref2$onlyOneChar;
activeArgs = { source: source, index: index, err: err, errTarget: errTarget, onlyOneChar: onlyOneChar };
switch (expectation) {
case "always":
expectAfter();
break;
case "never":
rejectAfter();
break;
case "always-single-line":
if (!(0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
expectAfter(messages.expectedAfterSingleLine);
break;
case "never-single-line":
if (!(0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
rejectAfter(messages.rejectedAfterSingleLine);
break;
case "always-multi-line":
if ((0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
expectAfter(messages.expectedAfterMultiLine);
break;
case "never-multi-line":
if ((0, _isSingleLineString2.default)(lineCheckStr || source)) {
return;
}
rejectAfter(messages.rejectedAfterMultiLine);
break;
default:
throw (0, _configurationError2.default)("Unknown expectation \"" + expectation + "\"");
}
}
function beforeAllowingIndentation(obj) {
before((0, _lodash.assign)({}, obj, { allowIndentation: true }));
}
function expectBefore() {
var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedBefore;
if (activeArgs.allowIndentation) {
expectBeforeAllowingIndentation(messageFunc);
return;
}
var _activeArgs = activeArgs,
source = _activeArgs.source,
index = _activeArgs.index;
var oneCharBefore = source[index - 1];
var twoCharsBefore = source[index - 2];
if (!isValue(oneCharBefore)) {
return;
}
if (targetWhitespace === "newline") {
// If index is preceeded by a Windows CR-LF ...
if (oneCharBefore === "\n" && twoCharsBefore === "\r") {
if (activeArgs.onlyOneChar || !(0, _isWhitespace2.default)(source[index - 3])) {
return;
}
}
// If index is followed by a Unix LF ...
if (oneCharBefore === "\n" && twoCharsBefore !== "\r") {
if (activeArgs.onlyOneChar || !(0, _isWhitespace2.default)(twoCharsBefore)) {
return;
}
}
}
if (targetWhitespace === "space" && oneCharBefore === " ") {
if (activeArgs.onlyOneChar || !(0, _isWhitespace2.default)(twoCharsBefore)) {
return;
}
}
activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index]));
}
function expectBeforeAllowingIndentation() {
var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedBefore;
var _activeArgs2 = activeArgs,
source = _activeArgs2.source,
index = _activeArgs2.index,
err = _activeArgs2.err;
var expectedChar = function () {
if (targetWhitespace === "newline") {
return "\n";
}
if (targetWhitespace === "space") {
return " ";
}
}();
var i = index - 1;
while (source[i] !== expectedChar) {
if (source[i] === "\t" || source[i] === " ") {
i--;
continue;
}
err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index]));
return;
}
}
function rejectBefore() {
var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.rejectedBefore;
var _activeArgs3 = activeArgs,
source = _activeArgs3.source,
index = _activeArgs3.index;
var oneCharBefore = source[index - 1];
if (isValue(oneCharBefore) && (0, _isWhitespace2.default)(oneCharBefore)) {
activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index]));
}
}
function afterOneOnly(obj) {
after((0, _lodash.assign)({}, obj, { onlyOneChar: true }));
}
function expectAfter() {
var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedAfter;
var _activeArgs4 = activeArgs,
source = _activeArgs4.source,
index = _activeArgs4.index;
var oneCharAfter = index + 1 < source.length ? source[index + 1] : "";
var twoCharsAfter = index + 2 < source.length ? source[index + 2] : "";
if (!isValue(oneCharAfter)) {
return;
}
if (targetWhitespace === "newline") {
// If index is followed by a Windows CR-LF ...
if (oneCharAfter === "\r" && twoCharsAfter === "\n") {
var threeCharsAfter = index + 3 < source.length ? source[index + 3] : "";
if (activeArgs.onlyOneChar || !(0, _isWhitespace2.default)(threeCharsAfter)) {
return;
}
}
// If index is followed by a Unix LF ...
if (oneCharAfter === "\n") {
if (activeArgs.onlyOneChar || !(0, _isWhitespace2.default)(twoCharsAfter)) {
return;
}
}
}
if (targetWhitespace === "space" && oneCharAfter === " ") {
if (activeArgs.onlyOneChar || !(0, _isWhitespace2.default)(twoCharsAfter)) {
return;
}
}
activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index]));
}
function rejectAfter() {
var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.rejectedAfter;
var _activeArgs5 = activeArgs,
source = _activeArgs5.source,
index = _activeArgs5.index;
var oneCharAfter = index + 1 < source.length ? source[index + 1] : "";
if (isValue(oneCharAfter) && (0, _isWhitespace2.default)(oneCharAfter)) {
activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index]));
}
}
return {
before: before,
beforeAllowingIndentation: beforeAllowingIndentation,
after: after,
afterOneOnly: afterOneOnly
};
};
var _lodash = require("lodash");
var _isWhitespace = require("./isWhitespace");
var _isWhitespace2 = _interopRequireDefault(_isWhitespace);
var _isSingleLineString = require("./isSingleLineString");
var _isSingleLineString2 = _interopRequireDefault(_isSingleLineString);
var _configurationError = require("./configurationError");
var _configurationError2 = _interopRequireDefault(_configurationError);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function isValue(x) {
return x !== undefined && x !== null;
}
/**
* Create a whitespaceChecker, which exposes the following functions:
* - `before()`
* - `beforeAllowingIndentation()`
* - `after()`
* - `afterOneOnly()`
*
* @param {"space"|"newline"} targetWhitespace - This is a keyword instead
* of the actual character (e.g. " ") in order to accommodate
* different styles of newline ("\n" vs "\r\n")
* @param {
* "always"|"never"
* |"always-single-line"|"always-multi-line"
* | "never-single-line"|"never-multi-line"
* } expectation
* @param {object} messages - An object of message functions;
* calling `before*()` or `after*()` and the `expectation` that is passed
* determines which message functions are required
* @param {function} [messages.exectedBefore]
* @param {function} [messages.rejectedBefore]
* @param {function} [messages.expectedAfter]
* @param {function} [messages.rejectedAfter]
* @param {function} [messages.expectedBeforeSingleLine]
* @param {function} [messages.rejectedBeforeSingleLine]
* @param {function} [messages.expectedBeforeMultiLine]
* @param {function} [messages.rejectedBeforeMultiLine]
* @return {object} The checker, with its exposed checking functions
*/