in node/task.ts [1406:1532]
export function match(list: string[], patterns: string[] | string, patternRoot?: string, options?: MatchOptions): string[] {
// trace parameters
debug(`patternRoot: '${patternRoot}'`);
options = options || _getDefaultMatchOptions(); // default match options
_debugMatchOptions(options);
// convert pattern to an array
if (typeof patterns == 'string') {
patterns = [patterns as string];
}
// hashtable to keep track of matches
let map: { [item: string]: boolean } = {};
let originalOptions = options;
for (let pattern of patterns) {
debug(`pattern: '${pattern}'`);
// trim and skip empty
pattern = (pattern || '').trim();
if (!pattern) {
debug('skipping empty pattern');
continue;
}
// clone match options
let options = im._cloneMatchOptions(originalOptions);
// skip comments
if (!options.nocomment && im._startsWith(pattern, '#')) {
debug('skipping comment');
continue;
}
// set nocomment - brace expansion could result in a leading '#'
options.nocomment = true;
// determine whether pattern is include or exclude
let negateCount = 0;
if (!options.nonegate) {
while (pattern.charAt(negateCount) == '!') {
negateCount++;
}
pattern = pattern.substring(negateCount); // trim leading '!'
if (negateCount) {
debug(`trimmed leading '!'. pattern: '${pattern}'`);
}
}
let isIncludePattern = negateCount == 0 ||
(negateCount % 2 == 0 && !options.flipNegate) ||
(negateCount % 2 == 1 && options.flipNegate);
// set nonegate - brace expansion could result in a leading '!'
options.nonegate = true;
options.flipNegate = false;
// expand braces - required to accurately root patterns
let expanded: string[];
let preExpanded: string = pattern;
if (options.nobrace) {
expanded = [pattern];
}
else {
// convert slashes on Windows before calling braceExpand(). unfortunately this means braces cannot
// be escaped on Windows, this limitation is consistent with current limitations of minimatch (3.0.3).
debug('expanding braces');
let convertedPattern = process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern;
expanded = (minimatch as any).braceExpand(convertedPattern);
}
// set nobrace
options.nobrace = true;
for (let pattern of expanded) {
if (expanded.length != 1 || pattern != preExpanded) {
debug(`pattern: '${pattern}'`);
}
// trim and skip empty
pattern = (pattern || '').trim();
if (!pattern) {
debug('skipping empty pattern');
continue;
}
// root the pattern when all of the following conditions are true:
if (patternRoot && // patternRoot supplied
!im._isRooted(pattern) && // AND pattern not rooted
// AND matchBase:false or not basename only
(!options.matchBase || (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern).indexOf('/') >= 0)) {
pattern = im._ensureRooted(patternRoot, pattern);
debug(`rooted pattern: '${pattern}'`);
}
if (isIncludePattern) {
// apply the pattern
debug('applying include pattern against original list');
let matchResults: string[] = minimatch.match(list, pattern, options);
debug(matchResults.length + ' matches');
// union the results
for (let matchResult of matchResults) {
map[matchResult] = true;
}
}
else {
// apply the pattern
debug('applying exclude pattern against original list');
let matchResults: string[] = minimatch.match(list, pattern, options);
debug(matchResults.length + ' matches');
// substract the results
for (let matchResult of matchResults) {
delete map[matchResult];
}
}
}
}
// return a filtered version of the original list (preserves order and prevents duplication)
let result: string[] = list.filter((item: string) => map.hasOwnProperty(item));
debug(result.length + ' final results');
return result;
}