codenautas/dir-info

View on GitHub
dir-info.js

Summary

Maintainability
C
1 day
Test Coverage
"use strict";

var dirInfo = {}; 

var Path = require('path');
var fs = require('fs-extra');
var exec = require('child-process-promise').exec;
var readYaml = require('read-yaml-promise');
//var winOS = Path.sep==='\\';
var ncu = require('npm-check-updates');

dirInfo.config = { gitDir:false };

/*
    Esta funcion toma el path de git de:
    - dirInfo.config.gitDir
    - local-config.yaml git_dir
    - process.env.GITDIR
    O asume que está en el PATH y devuelve ''
 */
dirInfo.gitPath = function gitPath() {
    var localyaml='./local-config.yaml';
    return Promise.resolve().then(function() {
        return fs.exists(localyaml);
    }).then(function(existsYAML) {
        if(existsYAML) { return readYaml(localyaml); }
        return false;
    }).then(function(yconf){
        if(dirInfo.config.gitDir) { return dirInfo.config.gitDir; }
        if(yconf && yconf.git_dir) { return yconf.git_dir; }
        if(process.env.GITDIR) { return process.env.GITDIR; }
        return '';
    });
};

function gitRegExpTo(cual) {
    // es posible que siempre sea https y sobre el '?', pero esto asegura
    return new RegExp('^((https?://)?[^.]+'+cual+'\.com)');
}

dirInfo.getInfo = function getInfo(path, opts){
    opts = opts || {};
    var info={ origin:null };
    return Promise.resolve().then(function(){
        if(!path) { throw new Error('null path'); }
        info.name = Path.basename(path); // BAD! only the last dirname
        return fs.exists(path);
    }).then(function(exists) {
        if(!exists) { throw new Error("'"+path+"' does not exists"); }
        return fs.stat(path);
    }).then(function(stat) {
        var gitDir='';
        var execOptions = {};
        if(stat.isDirectory()) {
            gitDir = path+Path.sep+".git";
            return fs.stat(gitDir).then(function(statDotGit){
                return statDotGit.isDirectory();
            }).catch(function(/*err*/){
                return false;
            }).then(function(isDirDotGit) {
                if(isDirDotGit){
                    info.isGit = true;
                }
                if(opts.cmd) {
                    return Promise.resolve().then(function(){
                        return dirInfo.gitPath();
                    }).then(function(gitDir) {
                        execOptions.cwd = path;
                        execOptions.env = process.env;
                        if(execOptions.env.PATH.indexOf(gitDir)===-1) {
                            execOptions.env.PATH+=Path.delimiter+gitDir;
                        }
                        return exec('git status -z', execOptions);
                    }).then(function(resStatusZ) {
                        if(!info.isGit){
                            info.isGitSubdir=true;
                        }
                        return exec('git config --get remote.origin.url', execOptions).catch(function(err){
                            if(err.code===1){
                                return {errorInExec:true};
                            }else{
                                throw err;
                            }
                        }).then(function(resConfig) {
                            if(!resConfig.errorInExec){
                                info.origin=resConfig.stdout.replace(/([\t\r\n ]*)$/g,'');
                                if(gitRegExpTo('github').test(resConfig.stdout)) {
                                    info.isGithub = true;
                                }
                                if(gitRegExpTo('gitlab').test(resConfig.stdout)) {
                                    info.isGitlab = true;
                                }
                            }
                            return exec('git rev-parse --abbrev-ref HEAD', execOptions);
                        }).then(function(resBranch) {
                            info.branch = resBranch.stdout.substring(0, resBranch.stdout.length-1);
                            return exec('git rev-parse --show-toplevel', execOptions);
                        }).then(function(resTopLevel) {
                            resStatusZ.topLevel = resTopLevel.stdout;
                            return resStatusZ;
                        });
                    }).then(function(resStatusZ){
                        var topDir=resStatusZ.topLevel;
                        topDir = topDir.substring(0,topDir.length-1);
                        var reMods = /(M|A|D|\?\?) (?:\s)?([^\u0000]+)\s*/g;
                        var modifieds=[];
                        var deletes=[];
                        var addeds=[];
                        var untrackeds=[];
                        var absPath=Path.resolve(path);
                        var mod;
                        while ((mod = reMods.exec(resStatusZ.stdout)) !== null) {
                            //var msg = 'Found ' + mod[1] + ' ['+mod[2]+']'; console.log(msg);
                            var fullPath = topDir+'/'+mod[2];
                            // ATENCION: esto funciona porque nunca se incluyen los parent dirs
                            if(Path.resolve(fullPath).substring(0,absPath.length)===absPath){
                                var file = fullPath.substring(absPath.length+1);
                                if(fullPath.indexOf(Path.basename(path))==-1){
                                    continue;
                                }
                                ({
                                    M: modifieds,
                                    D: deletes,
                                    A: addeds,
                                    '??': untrackeds
                                })[mod[1]].push(file);
                            }
                        }                            
                        var hasChanges = modifieds.length || addeds.length || untrackeds.length || deletes.length;
                        if(hasChanges) {
                            if(modifieds.length) { info.modifieds = modifieds; }
                            if(deletes.length) {
                                info.deletes = deletes;
                            }
                            if(addeds.length) { info.addeds = addeds; }
                            if(untrackeds.length) {
                                info.untrackeds = untrackeds;
                            }
                        }
                        if(opts.net && (info.isGithub || info.isGitlab)) {
                            return exec('git remote show origin', execOptions).catch(function(/*err*/) {
                                return {errorInExec:true};
                            }).then(function(resRemote) {
                                if(!resRemote.errorInExec) {
                                    if(resRemote.stdout.match(/local out of date/)) {
                                        info.syncPending = true;
                                    }             
                                    if(info.isGitlab) {
                                        if(resRemote.stdout.match(/fast-forwardable/)) {
                                            info.pushPending = true;
                                        }
                                    }
                                }
                                return exec('git log --branches --not --remotes', execOptions);
                            }).then(function(resLOG) {
                                if(info.isGithub) {
                                    var rst=resLOG.stdout.substring(0, resLOG.stdout.length-1);
                                    if(rst !== "") { info.pushPending = true; }
                                }
                                return info;
                            });                                
                        }
                    }).catch(function(err){
                        //console.log("Error en path ", path, ":", err);
                        if(err.code!=128){
                            throw err;
                        }
                    }).then(function(){
                        return info;
                    });
                }
                return info;
            });
        } else { // it's a file
            info.name = Path.basename(Path.dirname(path))+'/'+Path.basename(path);
            if(path.match(/(package.json)$/i)) {
                info.isPackageJson = true;
            }
            if(path.match(/(\.json)$/i)) {
                info.isJson = true;
            }
            if(info.isJson && opts.cmd) {
                return fs.readJson(path).catch(function(/*err*/) {
                    info.hasError = true;
                    return {errorInRJS:true};
                }).then(function(json) {
                    if(!json.errorInRJS && opts.cmd) {
                        if(opts.net) {
                            return ncu.run({packageFile: Path.normalize(path)}).then(function(npmres) {
                                function hasUpdates(o) {
                                    for(var prop in o) { if (o.hasOwnProperty(prop)) { return true; } }
                                    return false;
                                }
                                if(hasUpdates(npmres)) {
                                    info.isOutdated = true;
                                }
                                return info;
                            });
                        }
                    }
                    return info;
                });
            }            
            return info;
        }
    }).then(function(infoForClean){
        return infoForClean;
    });
};

exports = module.exports = dirInfo;