UNPKG

3.69 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to count multiple spaces in regular expressions
3 * @author Matt DuVall <http://www.mattduvall.com/>
4 */
5
6"use strict";
7
8const astUtils = require("../ast-utils");
9
10//------------------------------------------------------------------------------
11// Rule Definition
12//------------------------------------------------------------------------------
13
14module.exports = {
15 meta: {
16 docs: {
17 description: "disallow multiple spaces in regular expressions",
18 category: "Possible Errors",
19 recommended: true
20 },
21
22 schema: [],
23
24 fixable: "code"
25 },
26
27 create(context) {
28 const sourceCode = context.getSourceCode();
29
30 /**
31 * Validate regular expressions
32 * @param {ASTNode} node node to validate
33 * @param {string} value regular expression to validate
34 * @param {number} valueStart The start location of the regex/string literal. It will always be the case that
35 `sourceCode.getText().slice(valueStart, valueStart + value.length) === value`
36 * @returns {void}
37 * @private
38 */
39 function checkRegex(node, value, valueStart) {
40 const multipleSpacesRegex = /( {2,})+?/,
41 regexResults = multipleSpacesRegex.exec(value);
42
43 if (regexResults !== null) {
44 const count = regexResults[0].length;
45
46 context.report({
47 node,
48 message: "Spaces are hard to count. Use {{{count}}}.",
49 data: {count},
50 fix(fixer) {
51 return fixer.replaceTextRange(
52 [valueStart + regexResults.index, valueStart + regexResults.index + count],
53 ` {${count}}`
54 );
55 }
56 });
57
58 /*
59 * TODO: (platinumazure) Fix message to use rule message
60 * substitution when api.report is fixed in lib/eslint.js.
61 */
62 }
63 }
64
65 /**
66 * Validate regular expression literals
67 * @param {ASTNode} node node to validate
68 * @returns {void}
69 * @private
70 */
71 function checkLiteral(node) {
72 const token = sourceCode.getFirstToken(node),
73 nodeType = token.type,
74 nodeValue = token.value;
75
76 if (nodeType === "RegularExpression") {
77 checkRegex(node, nodeValue, token.start);
78 }
79 }
80
81 /**
82 * Check if node is a string
83 * @param {ASTNode} node node to evaluate
84 * @returns {boolean} True if its a string
85 * @private
86 */
87 function isString(node) {
88 return node && node.type === "Literal" && typeof node.value === "string";
89 }
90
91 /**
92 * Validate strings passed to the RegExp constructor
93 * @param {ASTNode} node node to validate
94 * @returns {void}
95 * @private
96 */
97 function checkFunction(node) {
98 const scope = context.getScope();
99 const regExpVar = astUtils.getVariableByName(scope, "RegExp");
100 const shadowed = regExpVar && regExpVar.defs.length > 0;
101
102 if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0]) && !shadowed) {
103 checkRegex(node, node.arguments[0].value, node.arguments[0].start + 1);
104 }
105 }
106
107 return {
108 Literal: checkLiteral,
109 CallExpression: checkFunction,
110 NewExpression: checkFunction
111 };
112
113 }
114};