packages/casl-ability/src/matchers/field.ts
import { FieldMatcher } from '../types';
const REGEXP_SPECIAL_CHARS = /[-/\\^$+?.()|[\]{}]/g;
const REGEXP_ANY = /\.?\*+\.?/g;
const REGEXP_STARS = /\*+/;
const REGEXP_DOT = /\./g;
function detectRegexpPattern(match: string, index: number, string: string): string {
const quantifier = string[0] === '*' || match[0] === '.' && match[match.length - 1] === '.'
? '+'
: '*';
const matcher = match.indexOf('**') === -1 ? '[^.]' : '.';
const pattern = match.replace(REGEXP_DOT, '\\$&')
.replace(REGEXP_STARS, matcher + quantifier);
return index + match.length === string.length ? `(?:${pattern})?` : pattern;
}
function escapeRegexp(match: string, index: number, string: string): string {
if (match === '.' && (string[index - 1] === '*' || string[index + 1] === '*')) {
return match;
}
return `\\${match}`;
}
function createPattern(fields: string[]) {
const patterns = fields.map(field => field
.replace(REGEXP_SPECIAL_CHARS, escapeRegexp)
.replace(REGEXP_ANY, detectRegexpPattern));
const pattern = patterns.length > 1 ? `(?:${patterns.join('|')})` : patterns[0];
return new RegExp(`^${pattern}$`);
}
export const fieldPatternMatcher: FieldMatcher = (fields) => {
let pattern: RegExp | null;
return (field) => {
if (typeof pattern === 'undefined') {
pattern = fields.every(f => f.indexOf('*') === -1)
? null
: createPattern(fields);
}
return pattern === null
? fields.indexOf(field) !== -1
: pattern.test(field);
};
};