| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 | 'use strict';const cleanRegexp = require('clean-regexp');const {optimize} = require('regexp-tree');const escapeString = require('./utils/escape-string.js');const {newExpressionSelector} = require('./selectors/index.js');const {isStringLiteral} = require('./ast/index.js');const MESSAGE_ID = 'better-regex';const MESSAGE_ID_PARSE_ERROR = 'better-regex/parse-error';const messages = {	[MESSAGE_ID]: '{{original}} can be optimized to {{optimized}}.',	[MESSAGE_ID_PARSE_ERROR]: 'Problem parsing {{original}}: {{error}}',};const newRegExp = newExpressionSelector({name: 'RegExp', minimumArguments: 1});/** @param {import('eslint').Rule.RuleContext} context */const create = context => {	const {sortCharacterClasses} = context.options[0] || {};	const ignoreList = [];	if (sortCharacterClasses === false) {		ignoreList.push('charClassClassrangesMerge');	}	return {		'Literal[regex]'(node) {			const {raw: original, regex} = node;			// Regular Expressions with `u` flag are not well handled by `regexp-tree`			// https://github.com/DmitrySoshnikov/regexp-tree/issues/162			if (regex.flags.includes('u')) {				return;			}			let optimized = original;			try {				optimized = optimize(original, undefined, {blacklist: ignoreList}).toString();			} catch (error) {				return {					node,					messageId: MESSAGE_ID_PARSE_ERROR,					data: {						original,						error: error.message,					},				};			}			if (original === optimized) {				return;			}			const problem = {				node,				messageId: MESSAGE_ID,				data: {					original,					optimized,				},			};			if (				node.parent.type === 'MemberExpression'				&& node.parent.object === node				&& !node.parent.optional				&& !node.parent.computed				&& node.parent.property.type === 'Identifier'				&& (					node.parent.property.name === 'toString'					|| node.parent.property.name === 'source'				)			) {				return problem;			}			return Object.assign(problem, {				fix: fixer => fixer.replaceText(node, optimized),			});		},		[newRegExp](node) {			const [patternNode, flagsNode] = node.arguments;			if (!isStringLiteral(patternNode)) {				return;			}			const oldPattern = patternNode.value;			const flags = isStringLiteral(flagsNode)				? flagsNode.value				: '';			const newPattern = cleanRegexp(oldPattern, flags);			if (oldPattern !== newPattern) {				return {					node,					messageId: MESSAGE_ID,					data: {						original: oldPattern,						optimized: newPattern,					},					fix: fixer => fixer.replaceText(						patternNode,						escapeString(newPattern, patternNode.raw.charAt(0)),					),				};			}		},	};};const schema = [	{		type: 'object',		additionalProperties: false,		properties: {			sortCharacterClasses: {				type: 'boolean',				default: true,			},		},	},];/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create,	meta: {		type: 'suggestion',		docs: {			description: 'Improve regexes by making them shorter, consistent, and safer.',		},		fixable: 'code',		schema,		messages,	},};
 |