<commit_before> 'use strict';

var build       = module.exports = {};
var fs          = require('fs-sync');
var node_path   = require('path');
var hum         = require('hum');
var semver      = require('semver');
var async       = require('async');
var putin       = require('put-in');
var pkg_helper  = require('../util/package');
var lang        = require('../util/lang');
var exec        = require('child_process').exec;

var USER_HOME   = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];


// @param {Object} options
//      see ./lib/option/build.js for details
build.run = function(options, callback){
    // if the destination is not specified by user
    if ( !options.dest ) {
        options.dest = this.context.profile.get('built_root');
    }

    var self = this;

    var pkg = pkg_helper.get_package_json(options.cwd);

    if ( pkg.err ) {
        return callback(pkg.err);
    }

    options.pkg = pkg.cortex;

    this.MESSAGE = this.context.locale.require('command-build');

    async.series([
        function (done) {
            // run custom build script
            self.run_build_script(options, done);
        },

        function (done) {
            // determine build tasks
            self.determine_tasks(options, done);
        },

        function (done) {
            // real building
            self.build(options, done);
        }

    ], function (err) {
        callback(err);
    });
};


// Run custom build scripts, such as 'grunt'
build.run_build_script = function (options, callback) {
    var pkg = options.pkg;
    var build_script = lang.makeArray( pkg.scripts && pkg.scripts.build );

    if ( !build_script.length ) {
        return callback(null);
    }

    var self = this;

    this.logger.info('{{cyan run}} build script ...');

    exec( build_script.join(' && '), {
        cwd: options.cwd

    }, function(err, stdout, stderr){
        if ( err ) {
            return callback(err);
        }

        self.logger.info(stdout);
        stderr && self.logger.error(stderr);

        callback(null);
    });
};


build.determine_tasks = function (options, callback) {
    var cache_root  = this.context.profile.get('cache_root');
    var tasks       = options.tasks = {};
    var name        = options.pkg.name;
    var version     = options.pkg.version;
    var built_root  = options.dest;

    var self = this;

    if ( options.ranges ) {
        pkg_helper.get_module_document({
            name: name,
            cache_root: cache_root

        }, function (err, json) {
            if ( err ) {
                return callback(err);
            }

            var versions    = json && json.versions && Object.keys(json.versions) || [];
            versions.sort(semver.rcompare);

            // '1.2.3' -> '~1.2.0'
            var range       = self.get_base_range(version);
            var max_patch   = semver.maxSatisfying(versions, range);
            var max         = versions[0];
            
            if (
                // if the current minor(feature) doesn't exist
                !max_patch ||

                // greater or equal to
                semver.gte(version, max_patch)
            ) {
                tasks[version] = node_path.join(built_root, name, range);

                // if the package is not the latest patch,
                // it's impossible to be the latest of all versions
                if (
                    // if there's no versions on the regsitry
                    !max ||

                    // if current version is the greatest
                    semver.gte(version, max)
                ) {
                    tasks.latest = node_path.join(built_root, name, 'latest');
                }
            }

            // {
            //     '1.2.3': '/xxx/~1.2.0',
            //     'latest': '/xxx/latest'
            // }
            callback(null, tasks);
        });

    } else {
        tasks[version] = node_path.join(built_root, name, version);
        callback(null, tasks);
    }
};


// '1.2.3' -> '~1.2.0'
build.get_base_range = function (version) {
    var parsed_semver = semver.parse(version);

    return ['~' + parsed_semver.major, parsed_semver.minor, 0].join('.');
};


build.build = function (options, callback) {
    var pkg = options.pkg;

    var self = this;

    // distribution directory
    var dist = lang.object_member_by_namespaces(options.pkg, 'directories.dist');

    if ( dist ) {
        dist = node_path.join(options.cwd, dist);

        // if `dist` dir exists, skip building and just copy it
        if ( fs.exists(dist) ) {
            self.logger.info('dist dir "' + dist + '" found, {{cyan skip}} building ...');

            self.copy_dist(dist, options, callback);

        } else {
            // if `cortex.directories.dist` is declared, the dir must be existed.
            callback({
                code: 'EDISTNOTEXISTS',
                message: 'dist dir "' + dist + '" does not exist.',
                data: {
                    dist: dist
                }
            });
        }

    } else {
        self.get_node_path(function (search_path) {
            // make sure the commonjs builder already installed
            self.check_builder(search_path, options, function (err) {
                if ( err ) {
                    return callback(err);
                }

                self.get_entries(options.cwd, options.pkg, function (err, entries) {
                    if ( err ) {
                        return callback(err);
                    }

                    options.entries = entries;

                    var tasks = options.tasks;
                    async.parallel(
                        Object.keys(tasks).map(function (target_version) {
                            return function (done) {
                                var to = tasks[target_version];
                                self.run_build_task(target_version, to, options, done);
                            }
                        }), 

                        callback
                    );
                });
            });
        });
    }
};


// copy dist dir to the destination dirs
build.copy_dist = function (dist, options, callback) {
    var self = this;

    lang.each(options.tasks, function (to) {
        self.logger.info('{{cyan copy}} "' + dist + '" to "' + to + '".');
        self.cp(dist, to);
    });

    callback(null);
};


build.get_node_path = function (callback) {
    var NODE_PATH = process.env.NODE_PATH;
    var search_path;

    if ( NODE_PATH ) {
        search_path = NODE_PATH.split(':').filter(Boolean)[0];
    }

    callback( search_path || '/usr/local/lib/node_modules' );
};


// Check if the builder exists.
build.check_builder = function (search_path, options, callback) {
    var builder = options.builder = this.context.profile.get('builder');
    options.builder_task = this.context.profile.get('builder_task');

    if(!builder){
        return callback(this.MESSAGE.BUILDER_MUST_BE_DEFINED);
    }

    if(!fs.exists(node_path.join(search_path, builder))){
        this.logger.info('\n{{cyan install}} commonjs builder...');

        // install tasks if not exists
        putin(search_path).install(builder, function(code){
            if(code === 0){
                callback(null);

            }else{
                callback(self.logger.template(self.MESSAGE.PACKAGE_NOT_FOUNT_ON_NPM,{
                    name: builder
                }));
            }
        });

    } else {
        callback(null);
    }
};


build.get_entries = function (cwd, pkg, callback) {
    var main_entry = pkg.main || 'index.js';
    var entries = pkg.entries ? 
            fs.expand(pkg.entries, {
                cwd: cwd
            }) : 
            [];

    var fail = entries.some(function (entry) {
        // entries should not be located at the parent folder of the main entry.
        if ( node_path.relative( node_path.dirname(main_entry), entry).indexOf('..') === 0 ) {
            callback({
                code: 'EINVALIDENTRIES',
                message: 'entry "' + entry + '" should not be located at the parent folder of "' + main_entry + '"',
                data: {
                    main: main_entry,
                    entry: entry
                }
            });

            return true;
        }
    });
    
    if ( !fail ) {
        entries.push(main_entry);

        callback(null, entries);
    }
};


build.run_build_task = function (target_version, to, options, callback) {
    var task_name = options.builder_task;
    var init_config = {};
    var entries = {};

    options.entries.forEach(function (entry) {
        entries[ node_path.join(options.cwd, entry) ] = node_path.join(to, entry); 
    });

    init_config[task_name] = {
        'all': {
            entries: entries,
            targetVersion: target_version
        }
    };

    var self = this;

    async.parallel([
        // run commonjs builder
        function (done) {
            hum({
                cwd: options.cwd
            })
            .npmTasks(options.builder)
            .task(options.builder_task)
            .init(init_config)
            .options({
                force: true<commit_msg>cortex build: fix grunt init config<commit_after> 'use strict';

var build       = module.exports = {};
var fs          = require('fs-sync');
var node_path   = require('path');
var hum         = require('hum');
var semver      = require('semver');
var async       = require('async');
var putin       = require('put-in');
var pkg_helper  = require('../util/package');
var lang        = require('../util/lang');
var exec        = require('child_process').exec;

var USER_HOME   = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];


// @param {Object} options
//      see ./lib/option/build.js for details
build.run = function(options, callback){
    // if the destination is not specified by user
    if ( !options.dest ) {
        options.dest = this.context.profile.get('built_root');
    }

    var self = this;
    var pkg = pkg_helper.get_package_json(options.cwd);

    if ( pkg.err ) {
        return callback(pkg.err);
    }

    options.pkg = pkg.cortex;
    this.MESSAGE = this.context.locale.require('command-build');

    async.series([
        function (done) {
            // run custom build script
            self.run_build_script(options, done);
        },

        function (done) {
            // determine build tasks
            self.determine_tasks(options, done);
        },

        function (done) {
            // real building
            self.build(options, done);
        }

    ], function (err) {
        callback(err);
    });
};


// Run custom build scripts, such as 'grunt'
build.run_build_script = function (options, callback) {
    var pkg = options.pkg;
    var build_script = lang.makeArray( pkg.scripts && pkg.scripts.build );

    if ( !build_script.length ) {
        return callback(null);
    }

    var self = this;

    this.logger.info('{{cyan run}} build script ...');

    exec( build_script.join(' && '), {
        cwd: options.cwd

    }, function(err, stdout, stderr){
        if ( err ) {
            return callback(err);
        }

        self.logger.info(stdout);
        stderr && self.logger.error(stderr);

        callback(null);
    });
};


build.determine_tasks = function (options, callback) {
    var cache_root  = this.context.profile.get('cache_root');
    var tasks       = options.tasks = {};
    var name        = options.pkg.name;
    var version     = options.pkg.version;
    var built_root  = options.dest;

    var self = this;

    if ( options.ranges ) {
        pkg_helper.get_module_document({
            name: name,
            cache_root: cache_root

        }, function (err, json) {
            if ( err ) {
                return callback(err);
            }

            var versions    = json && json.versions && Object.keys(json.versions) || [];
            versions.sort(semver.rcompare);

            // '1.2.3' -> '~1.2.0'
            var range       = self.get_base_range(version);
            var max_patch   = semver.maxSatisfying(versions, range);
            var max         = versions[0];
            
            if (
                // if the current minor(feature) doesn't exist
                !max_patch ||

                // greater or equal to
                semver.gte(version, max_patch)
            ) {
                tasks[version] = node_path.join(built_root, name, range);

                // if the package is not the latest patch,
                // it's impossible to be the latest of all versions
                if (
                    // if there's no versions on the regsitry
                    !max ||

                    // if current version is the greatest
                    semver.gte(version, max)
                ) {
                    tasks.latest = node_path.join(built_root, name, 'latest');
                }
            }

            // {
            //     '1.2.3': '/xxx/~1.2.0',
            //     'latest': '/xxx/latest'
            // }
            callback(null, tasks);
        });

    } else {
        tasks[version] = node_path.join(built_root, name, version);
        callback(null, tasks);
    }
};


// '1.2.3' -> '~1.2.0'
build.get_base_range = function (version) {
    var parsed_semver = semver.parse(version);

    return ['~' + parsed_semver.major, parsed_semver.minor, 0].join('.');
};


build.build = function (options, callback) {
    var pkg = options.pkg;

    var self = this;

    // distribution directory
    var dist = lang.object_member_by_namespaces(options.pkg, 'directories.dist');

    if ( dist ) {
        dist = node_path.join(options.cwd, dist);

        // if `dist` dir exists, skip building and just copy it
        if ( fs.exists(dist) ) {
            self.logger.info('dist dir "' + dist + '" found, {{cyan skip}} building ...');

            self.copy_dist(dist, options, callback);

        } else {
            // if `cortex.directories.dist` is declared, the dir must be existed.
            callback({
                code: 'EDISTNOTEXISTS',
                message: 'dist dir "' + dist + '" does not exist.',
                data: {
                    dist: dist
                }
            });
        }

    } else {
        self.get_node_path(function (search_path) {
            // make sure the commonjs builder already installed
            self.check_builder(search_path, options, function (err) {
                if ( err ) {
                    return callback(err);
                }

                self.get_entries(options.cwd, options.pkg, function (err, entries) {
                    if ( err ) {
                        return callback(err);
                    }

                    options.entries = entries;

                    var tasks = options.tasks;
                    async.parallel(
                        Object.keys(tasks).map(function (target_version) {
                            return function (done) {
                                var to = tasks[target_version];
                                self.run_build_task(target_version, to, options, done);
                            }
                        }), 

                        callback
                    );
                });
            });
        });
    }
};


// copy dist dir to the destination dirs
build.copy_dist = function (dist, options, callback) {
    var self = this;

    lang.each(options.tasks, function (to) {
        self.logger.info('{{cyan copy}} "' + dist + '" to "' + to + '".');
        self.cp(dist, to);
    });

    callback(null);
};


build.get_node_path = function (callback) {
    var NODE_PATH = process.env.NODE_PATH;
    var search_path;

    if ( NODE_PATH ) {
        search_path = NODE_PATH.split(':').filter(Boolean)[0];
    }

    callback( search_path || '/usr/local/lib/node_modules' );
};


// Check if the builder exists.
build.check_builder = function (search_path, options, callback) {
    var builder = options.builder = this.context.profile.get('builder');
    options.builder_task = this.context.profile.get('builder_task');

    if(!builder){
        return callback(this.MESSAGE.BUILDER_MUST_BE_DEFINED);
    }

    if(!fs.exists(node_path.join(search_path, builder))){
        this.logger.info('\n{{cyan install}} commonjs builder...');

        // install tasks if not exists
        putin(search_path).install(builder, function(code){
            if(code === 0){
                callback(null);

            }else{
                callback(self.logger.template(self.MESSAGE.PACKAGE_NOT_FOUNT_ON_NPM,{
                    name: builder
                }));
            }
        });

    } else {
        callback(null);
    }
};


build.get_entries = function (cwd, pkg, callback) {
    var main_entry = pkg.main || 'index.js';
    var entries = pkg.entries ? 
            fs.expand(pkg.entries, {
                cwd: cwd
            }) : 
            [];

    var fail = entries.some(function (entry) {
        // entries should not be located at the parent folder of the main entry.
        if ( node_path.relative( node_path.dirname(main_entry), entry).indexOf('..') === 0 ) {
            callback({
                code: 'EINVALIDENTRIES',
                message: 'entry "' + entry + '" should not be located at the parent folder of "' + main_entry + '"',
                data: {
                    main: main_entry,
                    entry: entry
                }
            });

            return true;
        }
    });
    
    if ( !fail ) {
        entries.push(main_entry);

        callback(null, entries);
    }
};


build.run_build_task = function (target_version, to, options, callback) {
    var task_name = options.builder_task;
    var init_config = {};
    var entries = {};

    options.entries.forEach(function (entry) {
        entries[ node_path.join(options.cwd, entry) ] = node_path.join(to, entry); 
    });

    init_config[task_name] = {
        'all': {
            options: {
                entries: entries,
                targetVersion: target_version
            }
        }
    };

    var self = this;

    async.parallel([
        // run commonjs builder
        function (done) {
            hum({
                cwd: options.cwd
            })
            .npmTasks(options.builder)
            .task(options.builder_task)
            .init(init_config)
            .options({
                force: true
{'fraction_non_alphanumeric': 0.12600290775940984, 'fraction_numerical': 0.0026923698239190133, 'mean_word_length': 1.5662567362166644, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2317007', 'n_tokens_mistral': 5376, 'n_tokens_neox': 5088, 'n_words': 1171}