@putout/plugin-regexp
Version:
🐊Putout plugin helps with regexp
105 lines (78 loc) • 2.36 kB
JavaScript
import regexpTree from 'regexp-tree';
import {operator} from 'putout';
import {
isDisjunction,
isParentDisjunction,
} from '../types.js';
const {compare} = operator;
export const report = ({from, to}) => `Remove useless group from RegExp ${from}, use ${to}`;
export const exclude = () => [
'__.match(__)',
'__.split(__)',
'__.exec(__)',
'__.replace(__, __)',
'__.replaceAll(__, __)',
'const __a = /__b/',
];
export const fix = ({path, to}) => {
const [, pattern] = to.split('/');
path.node.pattern = pattern;
path.node.raw = to;
path.node.extra.raw = to;
};
export const traverse = ({push}) => ({
RegExpLiteral(path) {
if (!includes(path))
return;
const from = path.node.extra.raw;
const [is, to] = removeUselessGroup(from);
if (is)
push({
path,
from,
to,
});
},
});
function removeUselessGroup(str) {
const ast = regexpTree.parse(str);
let is = false;
regexpTree.traverse(ast, {
Group(path) {
const {type} = path.parent;
if (!/RegExp|Alternative/.test(type))
return;
const nextNode = getNextSibling(path);
if (nextNode?.type === 'Repetition')
return;
const {node} = path;
if (node.name)
return;
if (!node.expression)
return;
if (isParentDisjunction(path))
return;
if (isDisjunction(node.expression))
return;
is = true;
path.replace(node.expression);
},
});
return [is, regexpTree.generate(ast)];
}
function includes({parentPath}) {
if (compare(parentPath.parentPath, '/__a/.test(__b)'))
return true;
return compare(parentPath, '__.search(/__a/)');
}
function getNextSibling(path) {
let found = false;
const {expressions = []} = path.parent;
for (const current of expressions) {
if (found)
return current;
if (current === path.node)
found = true;
}
return null;
}