2 * @fileoverview Rule to disallow use of the `RegExp` constructor in favor of regular expression literals
3 * @author Milos Djermanovic
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
13 const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("eslint-utils");
15 //------------------------------------------------------------------------------
17 //------------------------------------------------------------------------------
20 * Determines whether the given node is a string literal.
21 * @param {ASTNode} node Node to check.
22 * @returns {boolean} True if the node is a string literal.
24 function isStringLiteral(node) {
25 return node.type === "Literal" && typeof node.value === "string";
29 * Determines whether the given node is a template literal without expressions.
30 * @param {ASTNode} node Node to check.
31 * @returns {boolean} True if the node is a template literal without expressions.
33 function isStaticTemplateLiteral(node) {
34 return node.type === "TemplateLiteral" && node.expressions.length === 0;
38 //------------------------------------------------------------------------------
40 //------------------------------------------------------------------------------
47 description: "disallow use of the `RegExp` constructor in favor of regular expression literals",
48 category: "Best Practices",
50 url: "https://eslint.org/docs/rules/prefer-regex-literals"
56 unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor."
63 * Determines whether the given identifier node is a reference to a global variable.
64 * @param {ASTNode} node `Identifier` node to check.
65 * @returns {boolean} True if the identifier is a reference to a global variable.
67 function isGlobalReference(node) {
68 const scope = context.getScope();
69 const variable = findVariable(scope, node);
71 return variable !== null && variable.scope.type === "global" && variable.defs.length === 0;
75 * Determines whether the given node is a String.raw`` tagged template expression
76 * with a static template literal.
77 * @param {ASTNode} node Node to check.
78 * @returns {boolean} True if the node is String.raw`` with a static template.
80 function isStringRawTaggedStaticTemplateLiteral(node) {
81 return node.type === "TaggedTemplateExpression" &&
82 node.tag.type === "MemberExpression" &&
83 node.tag.object.type === "Identifier" &&
84 node.tag.object.name === "String" &&
85 isGlobalReference(node.tag.object) &&
86 astUtils.getStaticPropertyName(node.tag) === "raw" &&
87 isStaticTemplateLiteral(node.quasi);
91 * Determines whether the given node is considered to be a static string by the logic of this rule.
92 * @param {ASTNode} node Node to check.
93 * @returns {boolean} True if the node is a static string.
95 function isStaticString(node) {
96 return isStringLiteral(node) ||
97 isStaticTemplateLiteral(node) ||
98 isStringRawTaggedStaticTemplateLiteral(node);
103 const scope = context.getScope();
104 const tracker = new ReferenceTracker(scope);
112 for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
113 const args = node.arguments;
116 (args.length === 1 || args.length === 2) &&
117 args.every(isStaticString)
119 context.report({ node, messageId: "unexpectedRegExp" });