source: imaps-frontend/node_modules/eslint/lib/rules/no-regex-spaces.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 6.4 KB
Line 
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
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13const regexpp = require("@eslint-community/regexpp");
14
15//------------------------------------------------------------------------------
16// Helpers
17//------------------------------------------------------------------------------
18
19const regExpParser = new regexpp.RegExpParser();
20const DOUBLE_SPACE = / {2}/u;
21
22/**
23 * Check if node is a string
24 * @param {ASTNode} node node to evaluate
25 * @returns {boolean} True if its a string
26 * @private
27 */
28function isString(node) {
29 return node && node.type === "Literal" && typeof node.value === "string";
30}
31
32//------------------------------------------------------------------------------
33// Rule Definition
34//------------------------------------------------------------------------------
35
36/** @type {import('../shared/types').Rule} */
37module.exports = {
38 meta: {
39 type: "suggestion",
40
41 docs: {
42 description: "Disallow multiple spaces in regular expressions",
43 recommended: true,
44 url: "https://eslint.org/docs/latest/rules/no-regex-spaces"
45 },
46
47 schema: [],
48 fixable: "code",
49
50 messages: {
51 multipleSpaces: "Spaces are hard to count. Use {{{length}}}."
52 }
53 },
54
55 create(context) {
56
57 const sourceCode = context.sourceCode;
58
59 /**
60 * Validate regular expression
61 * @param {ASTNode} nodeToReport Node to report.
62 * @param {string} pattern Regular expression pattern to validate.
63 * @param {string} rawPattern Raw representation of the pattern in the source code.
64 * @param {number} rawPatternStartRange Start range of the pattern in the source code.
65 * @param {string} flags Regular expression flags.
66 * @returns {void}
67 * @private
68 */
69 function checkRegex(nodeToReport, pattern, rawPattern, rawPatternStartRange, flags) {
70
71 // Skip if there are no consecutive spaces in the source code, to avoid reporting e.g., RegExp(' \ ').
72 if (!DOUBLE_SPACE.test(rawPattern)) {
73 return;
74 }
75
76 const characterClassNodes = [];
77 let regExpAST;
78
79 try {
80 regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
81 } catch {
82
83 // Ignore regular expressions with syntax errors
84 return;
85 }
86
87 regexpp.visitRegExpAST(regExpAST, {
88 onCharacterClassEnter(ccNode) {
89 characterClassNodes.push(ccNode);
90 }
91 });
92
93 const spacesPattern = /( {2,})(?: [+*{?]|[^+*{?]|$)/gu;
94 let match;
95
96 while ((match = spacesPattern.exec(pattern))) {
97 const { 1: { length }, index } = match;
98
99 // Report only consecutive spaces that are not in character classes.
100 if (
101 characterClassNodes.every(({ start, end }) => index < start || end <= index)
102 ) {
103 context.report({
104 node: nodeToReport,
105 messageId: "multipleSpaces",
106 data: { length },
107 fix(fixer) {
108 if (pattern !== rawPattern) {
109 return null;
110 }
111 return fixer.replaceTextRange(
112 [rawPatternStartRange + index, rawPatternStartRange + index + length],
113 ` {${length}}`
114 );
115 }
116 });
117
118 // Report only the first occurrence of consecutive spaces
119 return;
120 }
121 }
122 }
123
124 /**
125 * Validate regular expression literals
126 * @param {ASTNode} node node to validate
127 * @returns {void}
128 * @private
129 */
130 function checkLiteral(node) {
131 if (node.regex) {
132 const pattern = node.regex.pattern;
133 const rawPattern = node.raw.slice(1, node.raw.lastIndexOf("/"));
134 const rawPatternStartRange = node.range[0] + 1;
135 const flags = node.regex.flags;
136
137 checkRegex(
138 node,
139 pattern,
140 rawPattern,
141 rawPatternStartRange,
142 flags
143 );
144 }
145 }
146
147 /**
148 * Validate strings passed to the RegExp constructor
149 * @param {ASTNode} node node to validate
150 * @returns {void}
151 * @private
152 */
153 function checkFunction(node) {
154 const scope = sourceCode.getScope(node);
155 const regExpVar = astUtils.getVariableByName(scope, "RegExp");
156 const shadowed = regExpVar && regExpVar.defs.length > 0;
157 const patternNode = node.arguments[0];
158
159 if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
160 const pattern = patternNode.value;
161 const rawPattern = patternNode.raw.slice(1, -1);
162 const rawPatternStartRange = patternNode.range[0] + 1;
163 let flags;
164
165 if (node.arguments.length < 2) {
166
167 // It has no flags.
168 flags = "";
169 } else {
170 const flagsNode = node.arguments[1];
171
172 if (isString(flagsNode)) {
173 flags = flagsNode.value;
174 } else {
175
176 // The flags cannot be determined.
177 return;
178 }
179 }
180
181 checkRegex(
182 node,
183 pattern,
184 rawPattern,
185 rawPatternStartRange,
186 flags
187 );
188 }
189 }
190
191 return {
192 Literal: checkLiteral,
193 CallExpression: checkFunction,
194 NewExpression: checkFunction
195 };
196 }
197};
Note: See TracBrowser for help on using the repository browser.