UNPKG

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