.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-regex-spaces.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/no-regex-spaces.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/no-regex-spaces.js
new file mode 100644 (file)
index 0000000..e6d4c9e
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * @fileoverview Rule to count multiple spaces in regular expressions
+ * @author Matt DuVall <http://www.mattduvall.com/>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+const regexpp = require("regexpp");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+const regExpParser = new regexpp.RegExpParser();
+const DOUBLE_SPACE = / {2}/u;
+
+/**
+ * Check if node is a string
+ * @param {ASTNode} node node to evaluate
+ * @returns {boolean} True if its a string
+ * @private
+ */
+function isString(node) {
+    return node && node.type === "Literal" && typeof node.value === "string";
+}
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        type: "suggestion",
+
+        docs: {
+            description: "disallow multiple spaces in regular expressions",
+            category: "Possible Errors",
+            recommended: true,
+            url: "https://eslint.org/docs/rules/no-regex-spaces"
+        },
+
+        schema: [],
+        fixable: "code",
+
+        messages: {
+            multipleSpaces: "Spaces are hard to count. Use {{{length}}}."
+        }
+    },
+
+    create(context) {
+
+        /**
+         * Validate regular expression
+         * @param {ASTNode} nodeToReport Node to report.
+         * @param {string} pattern Regular expression pattern to validate.
+         * @param {string} rawPattern Raw representation of the pattern in the source code.
+         * @param {number} rawPatternStartRange Start range of the pattern in the source code.
+         * @param {string} flags Regular expression flags.
+         * @returns {void}
+         * @private
+         */
+        function checkRegex(nodeToReport, pattern, rawPattern, rawPatternStartRange, flags) {
+
+            // Skip if there are no consecutive spaces in the source code, to avoid reporting e.g., RegExp(' \ ').
+            if (!DOUBLE_SPACE.test(rawPattern)) {
+                return;
+            }
+
+            const characterClassNodes = [];
+            let regExpAST;
+
+            try {
+                regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
+            } catch {
+
+                // Ignore regular expressions with syntax errors
+                return;
+            }
+
+            regexpp.visitRegExpAST(regExpAST, {
+                onCharacterClassEnter(ccNode) {
+                    characterClassNodes.push(ccNode);
+                }
+            });
+
+            const spacesPattern = /( {2,})(?: [+*{?]|[^+*{?]|$)/gu;
+            let match;
+
+            while ((match = spacesPattern.exec(pattern))) {
+                const { 1: { length }, index } = match;
+
+                // Report only consecutive spaces that are not in character classes.
+                if (
+                    characterClassNodes.every(({ start, end }) => index < start || end <= index)
+                ) {
+                    context.report({
+                        node: nodeToReport,
+                        messageId: "multipleSpaces",
+                        data: { length },
+                        fix(fixer) {
+                            if (pattern !== rawPattern) {
+                                return null;
+                            }
+                            return fixer.replaceTextRange(
+                                [rawPatternStartRange + index, rawPatternStartRange + index + length],
+                                ` {${length}}`
+                            );
+                        }
+                    });
+
+                    // Report only the first occurrence of consecutive spaces
+                    return;
+                }
+            }
+        }
+
+        /**
+         * Validate regular expression literals
+         * @param {ASTNode} node node to validate
+         * @returns {void}
+         * @private
+         */
+        function checkLiteral(node) {
+            if (node.regex) {
+                const pattern = node.regex.pattern;
+                const rawPattern = node.raw.slice(1, node.raw.lastIndexOf("/"));
+                const rawPatternStartRange = node.range[0] + 1;
+                const flags = node.regex.flags;
+
+                checkRegex(
+                    node,
+                    pattern,
+                    rawPattern,
+                    rawPatternStartRange,
+                    flags
+                );
+            }
+        }
+
+        /**
+         * Validate strings passed to the RegExp constructor
+         * @param {ASTNode} node node to validate
+         * @returns {void}
+         * @private
+         */
+        function checkFunction(node) {
+            const scope = context.getScope();
+            const regExpVar = astUtils.getVariableByName(scope, "RegExp");
+            const shadowed = regExpVar && regExpVar.defs.length > 0;
+            const patternNode = node.arguments[0];
+            const flagsNode = node.arguments[1];
+
+            if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
+                const pattern = patternNode.value;
+                const rawPattern = patternNode.raw.slice(1, -1);
+                const rawPatternStartRange = patternNode.range[0] + 1;
+                const flags = isString(flagsNode) ? flagsNode.value : "";
+
+                checkRegex(
+                    node,
+                    pattern,
+                    rawPattern,
+                    rawPatternStartRange,
+                    flags
+                );
+            }
+        }
+
+        return {
+            Literal: checkLiteral,
+            CallExpression: checkFunction,
+            NewExpression: checkFunction
+        };
+    }
+};