export function findMatch()

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;
}