<commit_before>// core modules
var path = require('path'),
    fs = require('fs');
// dep modules
var _ = require('lodash'),
    Promise = require('bluebird'),
    glob = require('glob');
// own modules
var helper = require('./lib/helper'),
    utils = require('./lib/utils');

module.exports = (function () {

    var jsdocx = {};
    glob = Promise.promisify(glob);

    // if the dependent project has jsdoc dependency, ours will not be
    // installed. and since we don't use the jsdoc module via require but a
    // specific file (jsdoc.js), we cannot be sure of its location. this method
    // will try to find it if exists.
    var jsdocjs = 'jsdoc/jsdoc.js';
    try {
        var local = path.join('..', 'node_modules', jsdocjs);
        if (fs.existsSync(local)) {
            jsdocx.path = local;
        } else {
            // Use the internal require() machinery to look up the location
            // of a module, but rather than loading the module, just return
            // the resolved filename.
            jsdocx.path = require.resolve(jsdocjs);
            // jsdoc module does not expose a main file. so instead of
            // require.resolve('jsdoc') - which will fail; we'll look for
            // 'jsdoc/jsdoc.js'.

            // for example, if the parent project has it, we'll get:
            // /path/to/parent-project/node_modules/jsdoc/jsdoc.js
        }
    } catch (e) {
        throw new Error('Could not find jsdoc module.');
    }

    // ---------------------------
    // HELPERS
    // ---------------------------

    function identity(o) {
        return o;
    }

    // http://usejsdoc.org/about-commandline.html
    // http://usejsdoc.org/about-configuring-jsdoc.html
    function buildArgs(options) {
        // access, private, readme, template, etc.. These CLI options do not
        // have affect when run with --explain (-X) flag. They are for
        // jsdoc-genrated docs. access is handled within the filter method.
        var opts = _.defaults(options, {
            // files: ... // non-jsdoc option
            encoding: 'utf8',
            package: null, // path
            recurse: false,
            pedantic: false,
            query: null
            // debug: false
            // verbose: false
        });

        var args = ['-X'];
        args = args.concat(helper.ensureArray(opts.files));
        args.push('-e', opts.encoding);
        if (_.isString(opts.package)) {
            args.push('-P', opts.package);
        }
        if (opts.recurse) args.push('-r');
        if (opts.pedantic) args.push('--pedantic');
        if (opts.query) args.push('-q', opts.query);
        // if (opts.debug) args.push('--debug');
        // if (opts.verbose) args.push('--verbose');
        return args;
    }

    function relativePath(symbol, rPath) {
        if (!symbol || !rPath) return;
        var p = symbol.meta && helper.getStr(symbol.meta.path);
        if (p) {
            symbol.meta.path = helper.normalizePath(path.relative(p, rPath));
        }
    }

    function normalizeAccess(access) {
        // ['public', 'protected', 'private'];
        if (access === 'all') return access; // all
        if (!_.isString(access) && !_.isArray(access)) {
            return ['public', 'protected'];
        }
        return helper.ensureArray(access);
    }

    // if group'ed, symbols are sorted with operators (#.~) intact. Otherwise
    // operators are not taken into account.
    function getSorter(sortType) {
        var re = /[^#\.~]/g,
            group = sortType === 'grouped';
        return function symbolSorter(a, b) {
            var A = group ? a.longname : a.longname.replace(re, '_'),
                B = group ? b.longname : b.longname.replace(re, '_');
            return A.toLocaleUpperCase().localeCompare(B.toLocaleUpperCase());
        };
    }

    function hierarchy(docs, sortType) {
        var parent,
            sorter = sortType ? getSorter(sortType) : null;
        _.eachRight(docs, function (symbol, index) {
            // Move constructor (method definition) to class declaration symbol
            if (utils.isConstructor(symbol)) {
                parent = _.find(docs, function (o) {
                    return o.longname === symbol.longname && utils.isClass(o);
                });
                if (parent) {
                    parent.$constructor = symbol;
                    docs.splice(index, 1);
                }
            // otherwise, move symbols with memberof property to corresponding parent member.
            } else if (symbol.memberof) {
                parent = _.find(docs, { longname: symbol.memberof });
                if (parent) {
                    parent.$members = parent.$members || [];
                    parent.$members.push(symbol);
                    if (sorter) {
                        parent.$members.sort(sorter);
                    } else {
                        // reverse bec. we used eachRight
                        parent.$members.reverse();
                    }
                    docs.splice(index, 1);
                }
            }
        });
        if (sorter) {
            docs.sort(sorter);
        }
        return docs;
    }

    function promiseGlobFiles(globs) {
        globs = helper.ensureArray(globs);
        return Promise.reduce(globs, function (memo, pattern) {
            return glob(pattern).then(function (paths) {
                memo.concat(paths);
                return paths;
            });
        }, []);
    }

    // ---------------------------
    // PUBLIC METHODS
    // ---------------------------

    /**
     *  Filters the parsed documentation output array.
     *
     *  @param {Array} docs - Documentation output array.
     *  @param {Object} [options] - Filter options.
     *  @param {Function} [predicate] - The function invoked per iteration.
     *  Returning a falsy value will remove the symbol from the output.
     *  Returning true will keep the original symbol. To keep the symbol and
     *  alter its contents, simply return an altered symbol object.
     *
     *  @returns {Array} - Filtered documentation array.
     */
    jsdocx.filter = function (docs, options, predicate) {<commit_msg>Fixed `promiseGlobFiles` to not replace the array of paths on every iteration.<commit_after>// core modules
var path = require('path'),
    fs = require('fs');
// dep modules
var _ = require('lodash'),
    Promise = require('bluebird'),
    glob = require('glob');
// own modules
var helper = require('./lib/helper'),
    utils = require('./lib/utils');

module.exports = (function () {

    var jsdocx = {};
    glob = Promise.promisify(glob);

    // if the dependent project has jsdoc dependency, ours will not be
    // installed. and since we don't use the jsdoc module via require but a
    // specific file (jsdoc.js), we cannot be sure of its location. this method
    // will try to find it if exists.
    var jsdocjs = 'jsdoc/jsdoc.js';
    try {
        var local = path.join('..', 'node_modules', jsdocjs);
        if (fs.existsSync(local)) {
            jsdocx.path = local;
        } else {
            // Use the internal require() machinery to look up the location
            // of a module, but rather than loading the module, just return
            // the resolved filename.
            jsdocx.path = require.resolve(jsdocjs);
            // jsdoc module does not expose a main file. so instead of
            // require.resolve('jsdoc') - which will fail; we'll look for
            // 'jsdoc/jsdoc.js'.

            // for example, if the parent project has it, we'll get:
            // /path/to/parent-project/node_modules/jsdoc/jsdoc.js
        }
    } catch (e) {
        throw new Error('Could not find jsdoc module.');
    }

    // ---------------------------
    // HELPERS
    // ---------------------------

    function identity(o) {
        return o;
    }

    // http://usejsdoc.org/about-commandline.html
    // http://usejsdoc.org/about-configuring-jsdoc.html
    function buildArgs(options) {
        // access, private, readme, template, etc.. These CLI options do not
        // have affect when run with --explain (-X) flag. They are for
        // jsdoc-genrated docs. access is handled within the filter method.
        var opts = _.defaults(options, {
            // files: ... // non-jsdoc option
            encoding: 'utf8',
            package: null, // path
            recurse: false,
            pedantic: false,
            query: null
            // debug: false
            // verbose: false
        });

        var args = ['-X'];
        args = args.concat(helper.ensureArray(opts.files));
        args.push('-e', opts.encoding);
        if (_.isString(opts.package)) {
            args.push('-P', opts.package);
        }
        if (opts.recurse) args.push('-r');
        if (opts.pedantic) args.push('--pedantic');
        if (opts.query) args.push('-q', opts.query);
        // if (opts.debug) args.push('--debug');
        // if (opts.verbose) args.push('--verbose');
        return args;
    }

    function relativePath(symbol, rPath) {
        if (!symbol || !rPath) return;
        var p = symbol.meta && helper.getStr(symbol.meta.path);
        if (p) {
            symbol.meta.path = helper.normalizePath(path.relative(p, rPath));
        }
    }

    function normalizeAccess(access) {
        // ['public', 'protected', 'private'];
        if (access === 'all') return access; // all
        if (!_.isString(access) && !_.isArray(access)) {
            return ['public', 'protected'];
        }
        return helper.ensureArray(access);
    }

    // if group'ed, symbols are sorted with operators (#.~) intact. Otherwise
    // operators are not taken into account.
    function getSorter(sortType) {
        var re = /[^#\.~]/g,
            group = sortType === 'grouped';
        return function symbolSorter(a, b) {
            var A = group ? a.longname : a.longname.replace(re, '_'),
                B = group ? b.longname : b.longname.replace(re, '_');
            return A.toLocaleUpperCase().localeCompare(B.toLocaleUpperCase());
        };
    }

    function hierarchy(docs, sortType) {
        var parent,
            sorter = sortType ? getSorter(sortType) : null;
        _.eachRight(docs, function (symbol, index) {
            // Move constructor (method definition) to class declaration symbol
            if (utils.isConstructor(symbol)) {
                parent = _.find(docs, function (o) {
                    return o.longname === symbol.longname && utils.isClass(o);
                });
                if (parent) {
                    parent.$constructor = symbol;
                    docs.splice(index, 1);
                }
            // otherwise, move symbols with memberof property to corresponding parent member.
            } else if (symbol.memberof) {
                parent = _.find(docs, { longname: symbol.memberof });
                if (parent) {
                    parent.$members = parent.$members || [];
                    parent.$members.push(symbol);
                    if (sorter) {
                        parent.$members.sort(sorter);
                    } else {
                        // reverse bec. we used eachRight
                        parent.$members.reverse();
                    }
                    docs.splice(index, 1);
                }
            }
        });
        if (sorter) {
            docs.sort(sorter);
        }
        return docs;
    }

    function promiseGlobFiles(globs) {
        globs = helper.ensureArray(globs);
        return Promise.reduce(globs, function (memo, pattern) {
            return glob(pattern).then(function (paths) {
                return memo.concat(paths);
            });
        }, []);
    }

    // ---------------------------
    // PUBLIC METHODS
    // ---------------------------

    /**
     *  Filters the parsed documentation output array.
     *
     *  @param {Array} docs - Documentation output array.
     *  @param {Object} [options] - Filter options.
     *  @param {Function} [predicate] - The function invoked per iteration.
     *  Returning a falsy value will remove the symbol from the output.
     *  Returning true will keep the original symbol. To keep the symbol and
     *  alter its contents, simply return an altered symbol object.
     *
     *  @returns {Array} - Filtered documentation array.
     */
    jsdocx.filter = function (docs, options, predicate) {
{'fraction_non_alphanumeric': 0.1418292292932523, 'fraction_numerical': 0.00047969299648225136, 'mean_word_length': 1.6246328157784304, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '3544281', 'n_tokens_mistral': 3421, 'n_tokens_neox': 3310, 'n_words': 958}