@atlaskit/eslint-plugin-no-lookahead-lookbehind-regexp
Version:
Fork of https://github.com/JonasBa/eslint-plugin-no-lookahead-lookbehind-regexp
111 lines (110 loc) • 3.86 kB
JavaScript
import { analyzeRegExpForLookaheadAndLookbehind } from '../helpers/analyzeRegExpForLookaheadAndLookbehind';
import { isBinaryExpression, isRegExpLiteral, isStringLiteralRegExp } from '../helpers/ast';
import { collectBrowserTargets, collectUnsupportedTargets } from '../helpers/caniuse';
import { createContextReport } from '../helpers/createReport';
export const DEFAULT_OPTIONS = {
'no-lookahead': 1,
'no-lookbehind': 1,
'no-negative-lookahead': 1,
'no-negative-lookbehind': 1
};
export const DEFAULT_CONF = {
browserslist: false
};
function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
export const getExpressionsToCheckFromConfiguration = options => {
if (!options.length) {
return {
rules: DEFAULT_OPTIONS,
config: DEFAULT_CONF
};
}
let rules = options;
let config = {};
if (isPlainObject(options[options.length - 1])) {
rules = options.slice(0, -1);
config = options[options.length - 1];
}
const validOptions = rules.filter(option => {
if (typeof option !== 'string') {
return false;
}
return DEFAULT_OPTIONS[option];
});
if (!validOptions.length) {
return {
rules: DEFAULT_OPTIONS,
config
};
}
const expressions = validOptions.reduce((acc, opt) => {
acc[opt] = 1;
return acc;
}, {
'no-lookahead': 0,
'no-lookbehind': 0,
'no-negative-lookahead': 0,
'no-negative-lookbehind': 0
});
return {
rules: expressions,
config
};
};
const noLookaheadLookbehindRegexp = {
meta: {
docs: {
description: 'disallow the use of lookahead and lookbehind regular expressions if unsupported by browser',
category: 'Compatibility',
recommended: true
},
type: 'problem'
},
create(context) {
const browsers = context.settings.browsers || context.settings.targets;
const {
targets,
hasConfig
} = collectBrowserTargets(context.getFilename(), browsers);
// Lookahead assertions are part of JavaScript's original regular expression support and are thus supported in all browsers.
const unsupportedTargets = collectUnsupportedTargets('js-regexp-lookbehind', targets);
const {
rules,
config
} = getExpressionsToCheckFromConfiguration(context.options);
// If there are no unsupported targets resolved from the browserlist config, then we can skip this rule
if (!unsupportedTargets.length && hasConfig) {
return {};
}
return {
TemplateLiteral(node) {
for (let quasi of node.quasis) {
if (typeof quasi.value.raw === 'string') {
const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(quasi.value.raw, rules // For string literals, we need to pass the raw value which includes escape characters.
);
if (unsupportedGroups.length > 0) {
createContextReport(node, context, unsupportedGroups, unsupportedTargets, config);
}
}
}
},
Literal(node) {
if ((isStringLiteralRegExp(node) || isBinaryExpression(node)) && typeof node.raw === 'string') {
const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(node.raw, rules // For string literals, we need to pass the raw value which includes escape characters.
);
if (unsupportedGroups.length > 0) {
createContextReport(node, context, unsupportedGroups, unsupportedTargets, config);
}
} else if (isRegExpLiteral(node)) {
const unsupportedGroups = analyzeRegExpForLookaheadAndLookbehind(node.regex.pattern, rules);
if (unsupportedGroups.length > 0) {
createContextReport(node, context, unsupportedGroups, unsupportedTargets, config);
}
}
}
};
}
};
export { noLookaheadLookbehindRegexp };