UNPKG

3.18 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to forbid control characters from regular expressions.
3 * @author Nicholas C. Zakas
4 */
5
6"use strict";
7
8const RegExpValidator = require("regexpp").RegExpValidator;
9const collector = new (class {
10 constructor() {
11 this._source = "";
12 this._controlChars = [];
13 this._validator = new RegExpValidator(this);
14 }
15
16 onPatternEnter() {
17 this._controlChars = [];
18 }
19
20 onCharacter(start, end, cp) {
21 if (cp >= 0x00 &&
22 cp <= 0x1F &&
23 (
24 this._source.codePointAt(start) === cp ||
25 this._source.slice(start, end).startsWith("\\x") ||
26 this._source.slice(start, end).startsWith("\\u")
27 )
28 ) {
29 this._controlChars.push(`\\x${`0${cp.toString(16)}`.slice(-2)}`);
30 }
31 }
32
33 collectControlChars(regexpStr) {
34 try {
35 this._source = regexpStr;
36 this._validator.validatePattern(regexpStr); // Call onCharacter hook
37 } catch {
38
39 // Ignore syntax errors in RegExp.
40 }
41 return this._controlChars;
42 }
43})();
44
45//------------------------------------------------------------------------------
46// Rule Definition
47//------------------------------------------------------------------------------
48
49module.exports = {
50 meta: {
51 type: "problem",
52
53 docs: {
54 description: "disallow control characters in regular expressions",
55 category: "Possible Errors",
56 recommended: true,
57 url: "https://eslint.org/docs/rules/no-control-regex"
58 },
59
60 schema: [],
61
62 messages: {
63 unexpected: "Unexpected control character(s) in regular expression: {{controlChars}}."
64 }
65 },
66
67 create(context) {
68
69 /**
70 * Get the regex expression
71 * @param {ASTNode} node node to evaluate
72 * @returns {RegExp|null} Regex if found else null
73 * @private
74 */
75 function getRegExpPattern(node) {
76 if (node.regex) {
77 return node.regex.pattern;
78 }
79 if (typeof node.value === "string" &&
80 (node.parent.type === "NewExpression" || node.parent.type === "CallExpression") &&
81 node.parent.callee.type === "Identifier" &&
82 node.parent.callee.name === "RegExp" &&
83 node.parent.arguments[0] === node
84 ) {
85 return node.value;
86 }
87
88 return null;
89 }
90
91 return {
92 Literal(node) {
93 const pattern = getRegExpPattern(node);
94
95 if (pattern) {
96 const controlCharacters = collector.collectControlChars(pattern);
97
98 if (controlCharacters.length > 0) {
99 context.report({
100 node,
101 messageId: "unexpected",
102 data: {
103 controlChars: controlCharacters.join(", ")
104 }
105 });
106 }
107 }
108 }
109 };
110
111 }
112};