/
better-regex.js
128 lines (111 loc) · 2.54 KB
/
better-regex.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
'use strict';
const cleanRegexp = require('clean-regexp');
const {optimize} = require('regexp-tree');
const quoteString = require('./utils/quote-string.js');
const {newExpressionSelector} = require('./selectors/index.js');
const MESSAGE_ID = 'better-regex';
const messages = {
[MESSAGE_ID]: '{{original}} can be optimized to {{optimized}}.',
};
const newRegExp = [
newExpressionSelector({name: 'RegExp', minimumArguments: 1}),
'[arguments.0.type="Literal"]',
].join('');
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,
data: {
original,
error: error.message,
},
message: 'Problem parsing {{original}}: {{error}}',
};
}
if (original === optimized) {
return;
}
return {
node,
messageId: MESSAGE_ID,
data: {
original,
optimized,
},
fix: fixer => fixer.replaceText(node, optimized),
};
},
[newRegExp]: node => {
const [patternNode, flagsNode] = node.arguments;
if (typeof patternNode.value !== 'string') {
return;
}
const oldPattern = patternNode.value;
const flags = (
flagsNode
&& flagsNode.type === 'Literal'
&& typeof flagsNode.value === 'string'
)
? 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,
quoteString(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,
},
};