in node/task.ts [1586:1762]
export function findMatch(defaultRoot: string, patterns: string[] | string, findOptions?: FindOptions, matchOptions?: MatchOptions): string[] {
// apply defaults for parameters and trace
defaultRoot = defaultRoot || this.getVariable('system.defaultWorkingDirectory') || process.cwd();
debug(`defaultRoot: '${defaultRoot}'`);
patterns = patterns || [];
patterns = typeof patterns == 'string' ? [patterns] as string[] : patterns;
findOptions = findOptions || _getDefaultFindOptions();
_debugFindOptions(findOptions);
matchOptions = matchOptions || _getDefaultMatchOptions();
_debugMatchOptions(matchOptions);
// normalize slashes for root dir
defaultRoot = im._normalizeSeparators(defaultRoot);
let results: { [key: string]: string } = {};
let originalMatchOptions = matchOptions;
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 matchOptions = im._cloneMatchOptions(originalMatchOptions);
// skip comments
if (!matchOptions.nocomment && im._startsWith(pattern, '#')) {
debug('skipping comment');
continue;
}
// set nocomment - brace expansion could result in a leading '#'
matchOptions.nocomment = true;
// determine whether pattern is include or exclude
let negateCount = 0;
if (!matchOptions.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 && !matchOptions.flipNegate) ||
(negateCount % 2 == 1 && matchOptions.flipNegate);
// set nonegate - brace expansion could result in a leading '!'
matchOptions.nonegate = true;
matchOptions.flipNegate = false;
// expand braces - required to accurately interpret findPath
let expanded: string[];
let preExpanded: string = pattern;
if (matchOptions.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
matchOptions.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;
}
if (isIncludePattern) {
// determine the findPath
let findInfo: im._PatternFindInfo = im._getFindInfoFromPattern(defaultRoot, pattern, matchOptions);
let findPath: string = findInfo.findPath;
debug(`findPath: '${findPath}'`);
if (!findPath) {
debug('skipping empty path');
continue;
}
// perform the find
debug(`statOnly: '${findInfo.statOnly}'`);
let findResults: string[] = [];
if (findInfo.statOnly) {
// simply stat the path - all path segments were used to build the path
try {
fs.statSync(findPath);
findResults.push(findPath);
}
catch (err) {
if (err.code != 'ENOENT') {
throw err;
}
debug('ENOENT');
}
}
else {
findResults = find(findPath, findOptions);
}
debug(`found ${findResults.length} paths`);
// apply the pattern
debug('applying include pattern');
if (findInfo.adjustedPattern != pattern) {
debug(`adjustedPattern: '${findInfo.adjustedPattern}'`);
pattern = findInfo.adjustedPattern;
}
let matchResults: string[] = minimatch.match(findResults, pattern, matchOptions);
debug(matchResults.length + ' matches');
// union the results
for (let matchResult of matchResults) {
let key = process.platform == 'win32' ? matchResult.toUpperCase() : matchResult;
results[key] = matchResult;
}
}
else {
// check if basename only and matchBase=true
if (matchOptions.matchBase &&
!im._isRooted(pattern) &&
(process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern).indexOf('/') < 0) {
// do not root the pattern
debug('matchBase and basename only');
}
else {
// root the exclude pattern
pattern = im._ensurePatternRooted(defaultRoot, pattern);
debug(`after ensurePatternRooted, pattern: '${pattern}'`);
}
// apply the pattern
debug('applying exclude pattern');
let matchResults: string[] = minimatch.match(
Object.keys(results).map((key: string) => results[key]),
pattern,
matchOptions);
debug(matchResults.length + ' matches');
// substract the results
for (let matchResult of matchResults) {
let key = process.platform == 'win32' ? matchResult.toUpperCase() : matchResult;
delete results[key];
}
}
}
}
let finalResult: string[] = Object.keys(results)
.map((key: string) => results[key])
.sort();
debug(finalResult.length + ' final results');
return finalResult;
}