<commit_before>var fs = require('fs'),
    PATH = require('path'),
    inherit = require('inherit'),
    _ = require('lodash'),
    q = require('q'),
    qfs = require('q-io/fs'),
    resolve = require('resolve'),
    defaults = require('./defaults'),
    Plugin = require('./plugin'),
    utils = require('./utils');

/**
 * @class
 * @name Config
 */
var Config = inherit({
    /**
     * @typedef {Object} ConfigFile
     * @property {String[]} levels
     * @property {String[]} excludePaths
     * @property {String[]} plugins
     */

    /**
     * @constructor
     * @param {String[]} targets
     * @param {Object} opts
     * @param {String} opts.configPath
     * @param {String[]} opts.reporters
     * @param {ConfigFile} opts.config
     */
    __constructor: function(targets, opts) {
        opts = defaults(opts);

        var configPath = this._configPath = opts.configPath;
        this._configDir = PATH.dirname(configPath);

        this._targets = this._formatTargets(targets);
        this._levels = opts.config.levels;

        this._excludePaths = this._formatExcludedPaths(opts.config.excludePaths);

        this._plugins = opts.config.plugins;

        this.reporters = opts.reporters;
    },

    /**
     * @param {String[]} targets
     * @returns {String[]}
     * @private
     *
     * @example
     * /some.blocks/some-block/some-block.ext –> /some.block/some-block/some-block*
     * /some.blocks/some-block/__some-elem –> /some.blocks/some-block/__some-elem/**
     */
    _formatTargets: function(targets) {
        targets = targets || [];

        return _(targets)
            .map(formatTarget_)
            .uniq()
            .value();

        ///
        function formatTarget_(target) {
            return fs.statSync(target).isFile()
                ? PATH.join(PATH.dirname(target), PATH.basename(target).split('.').shift()) + '*'
                : PATH.join(target, '**');
        }
    },

    /**
     * @param {String[]} excludePaths
     * @returns {String[]}
     * @private
     */
    _formatExcludedPaths: function(excludePaths) {
        return _.map(excludePaths, this._resolvePath, this);
    },

    /**
     * @returns {Promise<String[]>}
     * @public
     */
    getLevels: function() {
        return _(this._targets)
            .map(getLevelsFromTarget_.bind(this))
            .thru(q.all).value()
            .then(_.flatten)
            .then(_.compact)
            .then(_.uniq);

        ///
        function getLevelsFromTarget_(target) {
            var lastLevelInTarget = this._getLastLevelInTarget(target);

            if(!utils.hasGlobstarEnding(target)) {
                return [lastLevelInTarget];
            }

            return this._getLevelsInTargetTree(target)
                .then(Array.prototype.concat.bind([lastLevelInTarget]));
        }
    },

    /**
     * @param {String} target
     * @returns {String|undefined}
     * @private
     *
     * @example
     * /some.blocks/block –> /some.blocks
     * /some.blocks/block/block.examples/blocks/example –> /some.blocks/block/block.examples/blocks
     */
    _getLastLevelInTarget: function(target) {
        var splitted = target.split(PATH.sep),
            lastLevelIndex = _.findLastIndex(splitted, this._isLevel.bind(this));

        if(lastLevelIndex === -1) {
            return;
        }

        var lastLevel = _.take(splitted, ++lastLevelIndex).join(PATH.sep);
        return !this.isExcludedPath(lastLevel) && lastLevel;
    },

    /**
     * @param {String} target
     * @returns {Promise<String[]>}
     * @private
     */
    _getLevelsInTargetTree: function(target) {
        return qfs.listTree(PATH.dirname(target), function(path, stat) {
            if(this.isExcludedPath(path)) {
                return null;
            }

            return stat.isDirectory() && this._isLevel(path);
        }.bind(this));
    },

    /**
     * @param {String} path
     * @returns {Boolean}
     * @private
     */
    _isLevel: function(path) {
        return utils.someMinimatch(this._levels, PATH.basename(path));
    },

    /**
     * @param {String} path
     * @returns {Boolean}
     * @public
     */
    isTargetPath: function(path) {
        return utils.someMinimatch(this._targets, path);
    },

    /**
     * @param {String} path
     * @returns {Boolean}
     * @public
     */
    isExcludedPath: function(path) {
        return utils.someMinimatch(this._excludePaths, path);
    },

    /**
     * @returns {Function[]}
     * @public
     */
    requirePlugins: function() {
        return _(this._plugins)
            .map(function(config, path) {
                path = resolve.sync(path, {basedir: this._configDir});

                return config && new Plugin(path, config);
            }, this)
            .compact()
            .value();
    },

    /**
     * @param {String} path
     * @returns {String}<commit_msg>fix searching of exluded paths<commit_after>var fs = require('fs'),
    PATH = require('path'),
    inherit = require('inherit'),
    _ = require('lodash'),
    q = require('q'),
    qfs = require('q-io/fs'),
    resolve = require('resolve'),
    defaults = require('./defaults'),
    Plugin = require('./plugin'),
    utils = require('./utils');

/**
 * @class
 * @name Config
 */
var Config = inherit({
    /**
     * @typedef {Object} ConfigFile
     * @property {String[]} levels
     * @property {String[]} excludePaths
     * @property {String[]} plugins
     */

    /**
     * @constructor
     * @param {String[]} targets
     * @param {Object} opts
     * @param {String} opts.configPath
     * @param {String[]} opts.reporters
     * @param {ConfigFile} opts.config
     */
    __constructor: function(targets, opts) {
        opts = defaults(opts);

        var configPath = this._configPath = opts.configPath;
        this._configDir = PATH.dirname(configPath);

        this._targets = this._formatTargets(targets);
        this._levels = opts.config.levels;

        this._excludePaths = this._formatExcludedPaths(opts.config.excludePaths);

        this._plugins = opts.config.plugins;

        this.reporters = opts.reporters;
    },

    /**
     * @param {String[]} targets
     * @returns {String[]}
     * @private
     *
     * @example
     * /some.blocks/some-block/some-block.ext –> /some.block/some-block/some-block*
     * /some.blocks/some-block/__some-elem –> /some.blocks/some-block/__some-elem/**
     */
    _formatTargets: function(targets) {
        targets = targets || [];

        return _(targets)
            .map(formatTarget_)
            .uniq()
            .value();

        ///
        function formatTarget_(target) {
            return fs.statSync(target).isFile()
                ? PATH.join(PATH.dirname(target), PATH.basename(target).split('.').shift()) + '*'
                : PATH.join(target, '**');
        }
    },

    /**
     * @param {String[]} excludePaths
     * @returns {String[]}
     * @private
     */
    _formatExcludedPaths: function(excludePaths) {
        return _.map(excludePaths, this._resolvePath, this);
    },

    /**
     * @returns {Promise<String[]>}
     * @public
     */
    getLevels: function() {
        return _(this._targets)
            .map(getLevelsFromTarget_.bind(this))
            .thru(q.all).value()
            .then(_.flatten)
            .then(_.compact)
            .then(_.uniq);

        ///
        function getLevelsFromTarget_(target) {
            var lastLevelInTarget = this._getLastLevelInTarget(target);

            if(!utils.hasGlobstarEnding(target)) {
                return [lastLevelInTarget];
            }

            return this._getLevelsInTargetTree(target)
                .then(Array.prototype.concat.bind([lastLevelInTarget]));
        }
    },

    /**
     * @param {String} target
     * @returns {String|undefined}
     * @private
     *
     * @example
     * /some.blocks/block –> /some.blocks
     * /some.blocks/block/block.examples/blocks/example –> /some.blocks/block/block.examples/blocks
     */
    _getLastLevelInTarget: function(target) {
        var splitted = target.split(PATH.sep),
            lastLevelIndex = _.findLastIndex(splitted, this._isLevel.bind(this));

        if(lastLevelIndex === -1) {
            return;
        }

        var lastLevel = _.take(splitted, ++lastLevelIndex).join(PATH.sep);
        return !this.isExcludedPath(lastLevel) && lastLevel;
    },

    /**
     * @param {String} target
     * @returns {Promise<String[]>}
     * @private
     */
    _getLevelsInTargetTree: function(target) {
        return qfs.listTree(PATH.dirname(target), function(path, stat) {
            if(this.isExcludedPath(path)) {
                return null;
            }

            return stat.isDirectory() && this._isLevel(path);
        }.bind(this));
    },

    /**
     * @param {String} path
     * @returns {Boolean}
     * @private
     */
    _isLevel: function(path) {
        return utils.someMinimatch(this._levels, PATH.basename(path));
    },

    /**
     * @param {String} path
     * @returns {Boolean}
     * @public
     */
    isTargetPath: function(path) {
        return utils.someMinimatch(this._targets, path);
    },

    /**
     * @param {String} path
     * @returns {Boolean}
     * @public
     */
    isExcludedPath: function(path) {
        return utils.someMinimatch(this._excludePaths, PATH.resolve(path));
    },

    /**
     * @returns {Function[]}
     * @public
     */
    requirePlugins: function() {
        return _(this._plugins)
            .map(function(config, path) {
                path = resolve.sync(path, {basedir: this._configDir});

                return config && new Plugin(path, config);
            }, this)
            .compact()
            .value();
    },

    /**
     * @param {String} path
     * @returns {String}
{'fraction_non_alphanumeric': 0.1507767286018885, 'fraction_numerical': 0.0002030663011473246, 'mean_word_length': 1.9993909866017052, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2040831', 'n_tokens_mistral': 3034, 'n_tokens_neox': 2893, 'n_words': 499}