<commit_before>#!/usr/bin/env node

var spawn = require('cross-spawn')
var path = require('path')

var argv = require('minimist')(process.argv.slice(2))
var dotenv = require('dotenv')
var dotenvExpand = require('dotenv-expand')

var paths = ['.env']
if (argv.e) {
  if (typeof argv.e === 'string') {
    paths = [argv.e]
  } else {
    paths = argv.e
  }
}
paths.forEach(function (env) {
  dotenvExpand(dotenv.config({ path: path.resolve(env) }))
})

if (argv.p) {
  console.log(process.env[argv.p])
  process.exit()
}

spawn(argv._[0], argv._.slice(1), { stdio: 'inherit' })
  .on('exit', function (exitCode) {
    process.exit(exitCode)
  })
<commit_msg>feat: add help<commit_after>#!/usr/bin/env node

var spawn = require('cross-spawn')
var path = require('path')

var argv = require('minimist')(process.argv.slice(2))
var dotenv = require('dotenv')
var dotenvExpand = require('dotenv-expand')

function printHelp () {
  console.log([
    'Usage: dotenv [--help] [-e <path>] [-p <variable name>] [-- command]',
    '  --help              print help',
    '  -e <path>           parses the file <path> as a `.env` file and adds the variables to the environment',
    '  -e <path>           multiple -e flags are allowed',
    '  -p <variable>       print value of <variable> to the console. If you specify this, you do not have to specify a `command`',
    '  command             `command` is the actual command you want to run. Best practice is to precede this command with ` -- `. Everything after `--` is considered to be your command. So any flags will not be parsed by this tool but be passed to your command. If you do not do it, this tool will strip those flags'
  ].join('\n'))
}

if (argv.help) {
  printHelp()
  process.exit()
}

var paths = ['.env']
if (argv.e) {
  if (typeof argv.e === 'string') {
    paths = [argv.e]
  } else {
    paths = argv.e
  }
}
paths.forEach(function (env) {
  dotenvExpand(dotenv.config({ path: path.resolve(env) }))
})

if (argv.p) {
  console.log(process.env[argv.p])
  process.exit()
}

var command = argv._[0]
if (!command) {
  printHelp()
  process.exit(1)
}

spawn(command, argv._.slice(1), { stdio: 'inherit' })
  .on('exit', function (exitCode) {
    process.exit(exitCode)
  })
<|endoftext|>


<commit_before>#!/usr/bin/env node
var args = require('minimist')(process.argv.splice(2))
var usage = require('./usage')
var fs = require('fs')
var singleLineLog = require('single-line-log')
var prettyBytes = require('pretty-bytes')
var dat = require('./index.js')

var firstArg = args._[0]

run()

function run () {
  var loc = args.path || process.cwd()
  if (!fs.existsSync(loc)) {
    console.error('Does not exist:', loc)
    return usage('root.txt')
  }
  var db = dat({home: args.home})
  if (firstArg === 'link') {
    var dirs = args._.slice(1)
    if (dirs.length === 0) dirs = loc
    var statsScan = db.fileStats(dirs, function (err, stats) {
      printScanProgress(stats)
      clearInterval(scanInterval)
      if (err) throw err
      console.error() // newline
      var statsAdd = db.addFiles(dirs, function (err, link) {
        printAddProgress(statsAdd, statsScan.files)
        clearInterval(addInterval)
        if (err) throw err
        db.joinTcpSwarm(link, function (_err, link, port, close) {
          singleLineLog.stderr('') // clear previous stderr
          singleLineLog.stdout('dat://' + link)
          console.error() // final newline
        })
      })

      var addInterval = setInterval(function () {
        printAddProgress(statsAdd, statsScan.files)
      }, 100)
    })

    var scanInterval = setInterval(function () {
      printScanProgress(statsScan)
    }, 100)
  } else if (firstArg) {
    // download/share
    var hash = args._[0]
    if (!hash) return usage('root.txt')
    console.log('Downloading...')
    db.download(hash, loc, function (err) {
      if (err) throw err
      console.log('Done downloading.')
    })
  } else {
    return usage('root.txt')
  }
}

function printScanProgress (stats) {
  singleLineLog.stderr(
    'Scanning folder, found ' + stats.files + ' files, ' +
    stats.directories + ' directories.' +
    (stats.size ? ' ' + prettyBytes(stats.size) + ' total.' : '')
  )
}

function printAddProgress (stats, total) {
  singleLineLog.stderr(
    'Fingerprinting files... (' + stats.files + '/' + total + ')'
  )
}
<commit_msg>graceful exit dat link process<commit_after>#!/usr/bin/env node
var args = require('minimist')(process.argv.splice(2))
var usage = require('./usage')
var fs = require('fs')
var singleLineLog = require('single-line-log')
var prettyBytes = require('pretty-bytes')
var dat = require('./index.js')

var firstArg = args._[0]

run()

function run () {
  var loc = args.path || process.cwd()
  if (!fs.existsSync(loc)) {
    console.error('Does not exist:', loc)
    return usage('root.txt')
  }
  var db = dat({home: args.home})
  if (firstArg === 'link') {
    var dirs = args._.slice(1)
    if (dirs.length === 0) dirs = loc
    var statsScan = db.fileStats(dirs, function (err, stats) {
      if (err) throw err
      printScanProgress(stats)
      clearInterval(scanInterval)
      console.error() // newline
      var statsAdd = db.addFiles(dirs, function (err, link) {
        printAddProgress(statsAdd, statsScan.files)
        clearInterval(addInterval)
        if (err) throw err
        db.joinTcpSwarm(link, function (_err, link, port, close) {
          singleLineLog.stderr('') // clear previous stderr
          singleLineLog.stdout('dat://' + link)
          console.error() // final newline

          // intercept ctrl+c and do graceful exit
          process.on('SIGINT', function () {
            close(function (err) {
              if (err) throw err
              else process.exit(0)
            })
          })
        })
      })

      var addInterval = setInterval(function () {
        printAddProgress(statsAdd, statsScan.files)
      }, 100)
    })

    var scanInterval = setInterval(function () {
      printScanProgress(statsScan)
    }, 100)
  } else if (firstArg) {
    // download/share
    var hash = args._[0]
    if (!hash) return usage('root.txt')
    console.log('Downloading...')
    db.download(hash, loc, function (err) {
      if (err) throw err
      console.log('Done downloading.')
    })
  } else {
    return usage('root.txt')
  }
}

function printScanProgress (stats) {
  singleLineLog.stderr(
    'Scanning folder, found ' + stats.files + ' files in ' +
    stats.directories + ' directories.' +
    (stats.size ? ' ' + prettyBytes(stats.size) + ' total.' : '')
  )
}

function printAddProgress (stats, total) {
  singleLineLog.stderr(
    'Fingerprinting files... (' + stats.files + '/' + total + ')'
  )
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * Created by zhengwei on 16/7/19.
 * PanshiTool
 * 基于fis3-smarty解决方案的定制化开发命令集合
 */
const path = require('path');
const fs = require('fs');
const readline = require('readline');
const program = require('commander');
const Liftoff = require('liftoff');
const _ = require('fis3/lib/util');
var fis3 = require('fis3');
const fis3Cli = require('./lib/fis3cli');
const utils = require('./lib/utils');
const packageConf = require('./package.json');
const localServer = require('fis3-command-server/lib/util');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

/*
 * 实例化一个Liftoff对象,用来加载fis
 * */
var cli = new Liftoff({
  name: 'fis3',
  processTitle: 'fis',
  moduleName: 'fis3',
  configName: 'fis-conf',
  extensions: {
    '.js': null
  }
});
var lunchEnv = function (argv) {
  return {
    cwd: argv.r || argv.root,
    configPath: argv.f || argv.file
  }
};
var runFis = function (argv, command, cbCtrl, log) {
  return function (env) {
    /*
     * 不使用全局fis3,保持fis3的版本由该工具控制
     * */
    fis3.require.paths.unshift(path.join(__dirname, 'node_modules', 'fis3', 'node_modules'));
    fis3.require.paths.push(path.join(__dirname, 'node_modules'));
    fis3.cli.name = this.name;
    log && log();
    fis3.cli.run(argv, env);
    if (cbCtrl && !cbCtrl.left) {
      cbCtrl.cb && cbCtrl.cb();
    }
  }
};
/*
 * 启动(重启)测试服务器
 * */
const serverLog = {
  'start' : '启动',
  'stop' : '关闭',
  'open' : '打开'
};
var server = function (todo,port,type) {
  var fisArgv = {_:['server']};
  fisArgv._.push(todo);
  fisArgv.port = port || 8686;
  fisArgv.type = type || 'smarty';
  cli.launch({}, runFis(fisArgv,'server','',function () {
    fis.log.info('%s本地开发服务器: 端口%s', serverLog[todo] ,fisArgv.port);
  }));
};
/*
* 获取模块目录下的所有fis3配置文件
* */
var getFisConfigs = function (p) {
  return utils.getFisConfigs(process.cwd(),p || '')
    .then(function (fileNames) {
      if (!fileNames.length) {
        fis.log.warn('当前目录[%s]未检索到模块,请检查是否正确配置fis3', p || '项目根目录');
        return false;
      }
      return fileNames;
    });
};
/*
 * 根据模块配置信息依次编译
 * */
var release = function (module,media,cbCtrl) {
  var fisArgv = {_:['release']};
  var runFuc;
  if (module) {
    fisArgv.r = program.base ? path.join(program.base, module) : module;
  }
  if (media) {
    fisArgv.d = path.join(process.cwd(),'dist');
    fisArgv._.push(media);
  }
  runFuc = runFis(fisArgv,'release',cbCtrl, function (){
    fis.log.info('当前编译的模块: %s', module || path.basename(process.cwd()));
  });
  cli.launch(lunchEnv(fisArgv), runFuc);
};
var releaseAll = function (configs, media, cb) {
  fis.log.info('开始编译指定目录下所有模块:');
  configs.every(function (fileFullName,n) {
    var moduleDir = path.basename(path.dirname(fileFullName));
    var cbCtrl = cb ? {left:configs.length - (n+1),cb: cb} : null;
    release(moduleDir,media,cbCtrl);
    return true;
  });
};
/*
* 在某一模块下,启动fis3的文件监听。
* */
var watch = function (reload) {
  var fisArgv = {_:['release'],w:true};
  fisArgv.L = reload || false;
  if (~process.argv.indexOf('--child-flag')){
    fis.log.info('监听启动:');
  }
  cli.launch({},runFis(fisArgv));
};
/*
* 启动开发模式
* */
var devFunc = function devFunc() {
  getFisConfigs(program.base).then(function (fileNames) {
    releaseAll(fileNames);
    server("start",program.port,program.type);
  });
};
/*
* 调用编译方法,生成编译后文件
* */
var outPutAll = function outPutAll(todo, cb) {
  getFisConfigs(program.base).then(function (fileNames) {
    releaseAll(fileNames,todo,cb);
  });
};
var outPut = function output(module,todo, cb) {
  utils.isFis3Module(process.cwd(),program.base,module).then(function () {
    release(module, todo, cb && {left:0,cb:cb});
  }, function () {
    fis.log.error('未找到指定模块[%s]的配置文件,请检查后重试',module);
  });
};

/*
* 发布编译后的文件配置文件指定的目录
* */
var getOutPath = function (conf,confName) {
  return _.isAbsolute(conf[confName]) ?
    conf[confName] : path.join(process.cwd(), conf[confName]);
};
var deploy = function deploy(conf,module) {
  return function () {
    fis.log.info('输出编译后文件到指定目录');
    var mapOutPath = getOutPath(conf, 'mapPath')
      ,staticPath = getOutPath(conf, 'staticPath')
      ,tempPath = getOutPath(conf, 'tempPath');
    /*
    * 输出资源映射文件
    * */
    if (mapOutPath) {
      mapOutPath = path.join(mapOutPath,module ? module+'-map.json' : '');
      _.del(mapOutPath);
      _.copy(path.join(
        process.cwd(),
        'dist',
        'config',
        module ? module+'-map.json' : ''),mapOutPath);
    } else {
      fis.log.warn('你没有配置资源映射文件发布目录');
    }
    /*
    * 输出静态资源
    * */
    if(staticPath) {
      staticPath = path.join(staticPath, module || '');
      _.del(staticPath);
      _.copy(path.join(process.cwd(),'dist','static',module || ''),staticPath);
    } else {
      fis.log.warn('你没有配置静态资源发布目录');
    }
    /*
    * 输出模版文件
    * */
    if (tempPath) {
      tempPath = path.join(tempPath, module || '');
      _.del(tempPath);
      _.copy(path.join(process.cwd(),'dist','views',module || ''),tempPath);
    } else {
      fis.log.warn('你没有配置模版文件发布目录');
    }
    fis.log.info('操作结束');
  };
};
/*
* 给deploy和qa用的动作
* */
var releaseAction = function (module,media) {
  _.del(path.join(process.cwd(),'dist'));
  var confPath = path.join(process.cwd(),program.base || '','fst.config');
  try {
    if (module) {
      outPut(module, media, deploy(require(confPath),module));
    } else {
      outPutAll(media, deploy(require(confPath)));
    }
  } catch (e){
    fis.log.error('指定模块所在根目录[%s]下未找到fst.config',program.base || '项目根目录');
  }
};

fis3.cli = fis3Cli;// 覆盖原来的cli

/*
* 命令行设置
* */
program
  .version(packageConf.version + ' 基于fis3-' + fis3Cli.info.version)
  .usage('[command] [options]')
  .option('-b, --base <path>', 'set the root path of modules')
  .option('-P, --port <port>', 'set the port of local server,default 8686')
  .option('-T, --type <type>', 'set the type of local server,default smarty')
  .option('-L, --live', 'use for the watch command, reload browser after release')
  .option('-M, --major', 'deploy the major change')
  .option('-m, --minor', 'deploy the minor change')
  .option('-p, --patch', 'deploy the patch change')
  .option('--child-flag','(no need set!)run the watch as child process');
program
  .command('dev [options]')
  .description('release module as development and start local test server')
  .action(function () {
    if (!program.base) {
      fis.log.info('你未指定模块们所在目录,工具将以项目根目录为检索对象');
    }
    devFunc();
  });
program
  .command('server <toDo>')
  .description('start , stop or clean the local server')
  .action(function (toDo) {
    if (~['start', 'stop', 'open'].indexOf(toDo)){
      server(toDo);
    } else if (toDo === 'clean'){
      var serverInfo = localServer.serverInfo();
      fis.log.info('正在初始化本地测试服务器:');
      fis.log.info('清除静态资源目录:');
      _.del(path.join(serverInfo.root,'static'));
      fis.log.info('清除资源映射文件:');
      _.del(path.join(serverInfo.root,'config'));
      fis.log.info('清除路由配置信息:');
      _.del(path.join(serverInfo.root,'server-conf'));
      fis.log.info('清除测试数据:');
      _.del(path.join(serverInfo.root,'test'));
      fis.log.info('清除模版文件:');
      _.del(path.join(serverInfo.root,'template'));
      fis.log.info('ola');
    } else {
      fis.log.error('没有你要的[%s]操作,请查看帮助信息',toDo);
    }
    rl.close();
  });
program
  .command('watch [options]')
  .description('watch files change,after changed will release automatic')
  .action(function () {watch(program.live || false);});
program
  .command('deploy [module] [options...]')
  .description('deploy to the server side,this way will minify asset files ,uglify scripts and update version')
  .action(function (module) {
    rl.question('你正在进行发布,请确定相关特性已测试,确定?(Y/N)', function(answer) {
      if (answer.toUpperCase() === 'Y') {
        releaseAction(module,'prod');
      }
      rl.close();
    })
  });
program
  .command('qa [module] [options...]')
  .description('release the source without do any minify or uglify,but also deploy to the server side for test')
  .action(function (module) {
    fis.log.info('以测试模式编译代码,并输出到服务器端代码中');
    releaseAction(module,'qa');
    rl.close();
  });
program
  .command('release [module]')
  .description('release module alone.')
  .action(function (module) {
    release(module);
    rl.close();
  });
program.parse(process.argv);
<commit_msg>bug fix<commit_after>#!/usr/bin/env node

/**
 * Created by zhengwei on 16/7/19.
 * PanshiTool
 * 基于fis3-smarty解决方案的定制化开发命令集合
 */
const path = require('path');
const fs = require('fs');
const readline = require('readline');
const program = require('commander');
const Liftoff = require('liftoff');
const _ = require('fis3/lib/util');
var fis3 = require('fis3');
const fis3Cli = require('./lib/fis3cli');
const utils = require('./lib/utils');
const packageConf = require('./package.json');
const localServer = require('fis3-command-server/lib/util');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

/*
 * 实例化一个Liftoff对象,用来加载fis
 * */
var cli = new Liftoff({
  name: 'fis3',
  processTitle: 'fis',
  moduleName: 'fis3',
  configName: 'fis-conf',
  extensions: {
    '.js': null
  }
});
var lunchEnv = function (argv) {
  return {
    cwd: argv.r || argv.root,
    configPath: argv.f || argv.file
  }
};
var runFis = function (argv, command, cbCtrl, log) {
  return function (env) {
    /*
     * 不使用全局fis3,保持fis3的版本由该工具控制
     * */
    fis3.require.paths.unshift(path.join(__dirname, 'node_modules', 'fis3', 'node_modules'));
    fis3.require.paths.push(path.join(__dirname, 'node_modules'));
    fis3.cli.name = this.name;
    log && log();
    fis3.cli.run(argv, env);
    if (cbCtrl && !cbCtrl.left) {
      cbCtrl.cb && cbCtrl.cb();
    }
  }
};
/*
 * 启动(重启)测试服务器
 * */
const serverLog = {
  'start' : '启动',
  'stop' : '关闭',
  'open' : '打开'
};
var server = function (todo,port,type) {
  var fisArgv = {_:['server']};
  fisArgv._.push(todo);
  fisArgv.port = port || 8686;
  fisArgv.type = type || 'smarty';
  cli.launch({}, runFis(fisArgv,'server','',function () {
    fis.log.info('%s本地开发服务器: 端口%s', serverLog[todo] ,fisArgv.port);
  }));
};
/*
* 获取模块目录下的所有fis3配置文件
* */
var getFisConfigs = function (p) {
  return utils.getFisConfigs(process.cwd(),p || '')
    .then(function (fileNames) {
      if (!fileNames.length) {
        fis.log.warn('当前目录[%s]未检索到模块,请检查是否正确配置fis3', p || '项目根目录');
        return false;
      }
      return fileNames;
    });
};
/*
 * 根据模块配置信息依次编译
 * */
var release = function (module,media,cbCtrl) {
  var fisArgv = {_:['release']};
  var runFuc;
  if (module) {
    fisArgv.r = program.base ? path.join(program.base, module) : module;
  }
  if (media) {
    fisArgv.d = path.join(process.cwd(),'dist');
    fisArgv._.push(media);
  }
  runFuc = runFis(fisArgv,'release',cbCtrl, function (){
    fis.log.info('当前编译的模块: %s', module || path.basename(process.cwd()));
  });
  cli.launch(lunchEnv(fisArgv), runFuc);
};
var releaseAll = function (configs, media, cb) {
  fis.log.info('开始编译指定目录下所有模块:');
  configs.every(function (fileFullName,n) {
    var moduleDir = path.basename(path.dirname(fileFullName));
    var cbCtrl = cb ? {left:configs.length - (n+1),cb: cb} : null;
    release(moduleDir,media,cbCtrl);
    return true;
  });
};
/*
* 在某一模块下,启动fis3的文件监听。
* */
var watch = function (reload) {
  var fisArgv = {_:['release'],w:true};
  fisArgv.L = reload || false;
  if (~process.argv.indexOf('--child-flag')){
    fis.log.info('监听启动:');
  }
  cli.launch({},runFis(fisArgv));
};
/*
* 启动开发模式
* */
var devFunc = function devFunc() {
  getFisConfigs(program.base).then(function (fileNames) {
    releaseAll(fileNames);
    server("start",program.port,program.type);
  });
};
/*
* 调用编译方法,生成编译后文件
* */
var outPutAll = function outPutAll(todo, cb) {
  getFisConfigs(program.base).then(function (fileNames) {
    releaseAll(fileNames,todo,cb);
  });
};
var outPut = function output(module,todo, cb) {
  utils.isFis3Module(process.cwd(),program.base,module).then(function () {
    release(module, todo, cb && {left:0,cb:cb});
  }, function () {
    fis.log.error('未找到指定模块[%s]的配置文件,请检查后重试',module);
  });
};

/*
* 发布编译后的文件配置文件指定的目录
* */
var getOutPath = function (conf,confName) {
  return _.isAbsolute(conf[confName]) ?
    conf[confName] : path.join(process.cwd(), conf[confName]);
};
var deploy = function deploy(conf,module) {
  return function () {
    fis.log.info('输出编译后文件到指定目录');
    var mapOutPath = getOutPath(conf, 'mapPath')
      ,staticPath = getOutPath(conf, 'staticPath')
      ,tempPath = getOutPath(conf, 'tempPath');
    /*
    * 输出静态资源
    * */
    if(staticPath) {
      staticPath = path.join(staticPath, module || '');
      _.del(staticPath);
      _.copy(path.join(process.cwd(),'dist','static',module || ''),staticPath);
    } else {
      fis.log.warn('你没有配置静态资源发布目录');
    }
    /*
    * 输出模版文件
    * */
    if (tempPath) {
      tempPath = path.join(tempPath, module || '');
      _.del(tempPath);
      _.copy(path.join(process.cwd(),'dist','views',module || ''),tempPath);
    } else {
      fis.log.warn('你没有配置模版文件发布目录');
    }
    /*
     * 输出资源映射文件
     * */
    if (mapOutPath) {
      mapOutPath = path.join(mapOutPath,module ? module+'-map.json' : '');
      _.del(mapOutPath);
      _.copy(path.join(
        process.cwd(),
        'dist',
        'config',
        module ? module+'-map.json' : ''),mapOutPath);
    } else {
      fis.log.warn('你没有配置资源映射文件发布目录');
    }
    fis.log.info('操作结束');
  };
};
/*
* 给deploy和qa用的动作
* */
var releaseAction = function (module,media) {
  _.del(path.join(process.cwd(),'dist'));
  var confPath = path.join(process.cwd(),program.base || '','fst.config');
  try {
    if (module) {
      outPut(module, media, deploy(require(confPath),module));
    } else {
      outPutAll(media, deploy(require(confPath)));
    }
  } catch (e){
    fis.log.error('指定模块所在根目录[%s]下未找到fst.config',program.base || '项目根目录');
  }
};

fis3.cli = fis3Cli;// 覆盖原来的cli

/*
* 命令行设置
* */
program
  .version(packageConf.version + ' 基于fis3-' + fis3Cli.info.version)
  .usage('[command] [options]')
  .option('-b, --base <path>', 'set the root path of modules')
  .option('-P, --port <port>', 'set the port of local server,default 8686')
  .option('-T, --type <type>', 'set the type of local server,default smarty')
  .option('-L, --live', 'use for the watch command, reload browser after release')
  .option('-M, --major', 'deploy the major change')
  .option('-m, --minor', 'deploy the minor change')
  .option('-p, --patch', 'deploy the patch change')
  .option('--child-flag','(no need set!)run the watch as child process');
program
  .command('dev [options]')
  .description('release module as development and start local test server')
  .action(function () {
    if (!program.base) {
      fis.log.info('你未指定模块们所在目录,工具将以项目根目录为检索对象');
    }
    devFunc();
  });
program
  .command('server <toDo>')
  .description('start , stop or clean the local server')
  .action(function (toDo) {
    if (~['start', 'stop', 'open'].indexOf(toDo)){
      server(toDo);
    } else if (toDo === 'clean'){
      var serverInfo = localServer.serverInfo();
      fis.log.info('正在初始化本地测试服务器:');
      fis.log.info('清除静态资源目录:');
      _.del(path.join(serverInfo.root,'static'));
      fis.log.info('清除资源映射文件:');
      _.del(path.join(serverInfo.root,'config'));
      fis.log.info('清除路由配置信息:');
      _.del(path.join(serverInfo.root,'server-conf'));
      fis.log.info('清除测试数据:');
      _.del(path.join(serverInfo.root,'test'));
      fis.log.info('清除模版文件:');
      _.del(path.join(serverInfo.root,'template'));
      fis.log.info('ola');
    } else {
      fis.log.error('没有你要的[%s]操作,请查看帮助信息',toDo);
    }
    rl.close();
  });
program
  .command('watch [options]')
  .description('watch files change,after changed will release automatic')
  .action(function () {watch(program.live || false);});
program
  .command('deploy [module] [options...]')
  .description('deploy to the server side,this way will minify asset files ,uglify scripts and update version')
  .action(function (module) {
    rl.question('你正在进行发布,请确定相关特性已测试,确定?(Y/N)', function(answer) {
      if (answer.toUpperCase() === 'Y') {
        releaseAction(module,'prod');
      }
      rl.close();
    })
  });
program
  .command('qa [module] [options...]')
  .description('release the source without do any minify or uglify,but also deploy to the server side for test')
  .action(function (module) {
    fis.log.info('以测试模式编译代码,并输出到服务器端代码中');
    releaseAction(module,'qa');
    rl.close();
  });
program
  .command('release [module]')
  .description('release module alone.')
  .action(function (module) {
    release(module);
    rl.close();
  });
program.parse(process.argv);
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
* @Author: Manraj Singh
* @Date:   2016-07-10 20:00:15
* @Last Modified by:   Manraj Singh
* @Last Modified time: 2016-07-25 11:15:39
*/

'use strict';

const yargs = require('yargs');
const fs = require('fs');
const ora = require('ora');
const chalk = require('chalk');
const config = require('./config');
const path = require('path');
const Table = require('cli-table');
const template = require('./template');
const languages = require('./languages');

const LANG_ERR = 'Not a valid languege. Please run `hbg config -l` to get the list of languages.';

/**
 * Returns full path of file
 */
const getPath = (folderPath, name) => {
  return path.resolve(folderPath, name);
}

/**
 * Returns file extension of param
 * @param  {string} lang [Language name]
 * @return {string}      [Extension of param language]
 */
const getExtension = (lang) => {
  return languages[lang];
}

/**
 * Returns if language is valid or not
 */
const validLang = (lang) => {
  return languages[lang] == undefined ? false : true;
}

/**
 * Generates boiler plate
 */
const generate = (folderPath, ques, lang) => {
  let files = [ques.toString() +'.'+ getExtension(lang), 'input.txt', 'output.txt'];
  fs.mkdirSync(folderPath);
  for(let i = 0; i<files.length; i++) {
    let data = template[lang] != undefined ? template[lang] : '';
    fs.writeFileSync(getPath(folderPath, files[i]), data, 'utf8');
  }
}

const argv = yargs
  .usage('sudo $0 <command>')
  .command('gen', 'Generate boilerplate', (yargs) => {
    const argv = yargs
      .usage('Usage: $0 gen <options>')
      .demand(['q'])
      .alias('l', 'lang').describe('l', 'Language. Change `config` for default')
      .alias('q', 'ques').describe('q', 'Number of questions')
      .example('sudo $0 gen -l cpp -q 4')
      .argv;
    let { q } = argv;
    let lang = (argv.l == undefined ? config['lang'] : argv.l).toLowerCase();
    if(validLang(lang)){
      for(let i = 1; i <= q; i++) {
        let folderPath = getPath(process.cwd(), i.toString());
        generate(folderPath, i, lang);
      }
    }
    else{
      console.log(chalk.red(LANG_ERR));
    }
  })
  .command('add', 'Add default template', (yargs) => {
    const argv = yargs
      .usage('Usage: $0 add <options>')
      .demand(['t', 'l'])
      .alias('t', 'template').describe('t', 'Path to the template file')
      .alias('l', 'lang').describe('l', 'Language')
      .example('$0 add -t test/template.cpp -l cpp')
      .argv;
    let { t } = argv;
    let lang = (argv.l == undefined ? config['lang'] : argv.l).toLowerCase();
    if(validLang(lang)) {
      let obj = template;
      let data = fs.readFileSync(getPath(process.cwd(), t), 'utf8');
      obj[lang] = data;
      fs.writeFileSync(getPath(__dirname, 'template.json'), JSON.stringify(obj, null, 2), 'utf8');
    }
    else{
      console.log(chalk.red(LANG_ERR));
    }
  })
  .command('config', 'Change config file', (yargs) => {
    const argv = yargs
      .usage('Usage: $0 config')
      .alias('l', 'list').describe('l', 'List language and their code').boolean('l')
      .example('$0')
      .argv;

    if (argv.list){
      const spinner = ora('Getting languages').start();
      let table = new Table({
        head: ['Language', 'File Extension'],
        colWidths: [20, 20]
      });
      for(let name in languages){
        table.push([name, languages[name]]);
      }
      spinner.stop();
      console.log(table.toString());
    }
    else {
      const questions = [{
        type: 'input',
        name: 'default_lang',
        message: 'Enter default language code <Leave blank in case unchanged>'
      } , {
        type: 'input',
        name: 'questions',
        message: 'Enter default number of questions <Leave blank in case unchanged>'
      }];
      inquirer.prompt(questions).then((answers) => {

      });
    }
  })
  .help('h')
  .alias('h', 'help')
  .argv;
<commit_msg>Minor changes<commit_after>#!/usr/bin/env node

/*
* @Author: Manraj Singh
* @Date:   2016-07-10 20:00:15
* @Last Modified by:   Manraj Singh
* @Last Modified time: 2016-07-25 12:38:30
*/

'use strict';

const yargs = require('yargs');
const fs = require('fs');
const ora = require('ora');
const chalk = require('chalk');
const config = require('./config');
const path = require('path');
const Table = require('cli-table');
const template = require('./template');
const languages = require('./languages');

const LANG_ERR = 'Not a valid languege. Please run `hbg config -l` to get the list of languages.';

/**
 * Returns full path of file
 */
const getPath = (folderPath, name) => {
  return path.resolve(folderPath, name);
}

/**
 * Returns file extension of param
 * @param  {string} lang [Language name]
 * @return {string}      [Extension of param language]
 */
const getExtension = (lang) => {
  return languages[lang];
}

/**
 * Returns if language is valid or not
 */
const validLang = (lang) => {
  return languages[lang] == undefined ? false : true;
}

/**
 * Generates boiler plate
 */
const generate = (folderPath, ques, lang) => {
  let files = [ques.toString() +'.'+ getExtension(lang), 'input.txt', 'output.txt'];
  fs.mkdirSync(folderPath);
  for(let i = 0; i<files.length; i++) {
    let data = template[lang] != undefined ? template[lang] : '';
    fs.writeFileSync(getPath(folderPath, files[i]), data, 'utf8');
  }
}

const argv = yargs
  .usage('sudo $0 <command>')
  .command('gen', 'Generate boilerplate', (yargs) => {
    const argv = yargs
      .usage('Usage: $0 gen <options>')
      .demand(['q'])
      .alias('l', 'lang').describe('l', 'Language. Change `config` for default')
      .alias('q', 'ques').describe('q', 'Number of questions. Change `config` for default')
      .example('sudo $0 gen -l cpp -q 4')
      .argv;
    let { q } = argv;
    let lang = (argv.l == undefined ? config['lang'] : argv.l).toLowerCase();
    if(validLang(lang)){
      for(let i = 1; i <= q; i++) {
        let folderPath = getPath(process.cwd(), i.toString());
        generate(folderPath, i, lang);
      }
    }
    else{
      console.log(chalk.red(LANG_ERR));
    }
  })
  .command('add', 'Add default template', (yargs) => {
    const argv = yargs
      .usage('Usage: $0 add <options>')
      .demand(['t', 'l'])
      .alias('t', 'template').describe('t', 'Path to the template file')
      .alias('l', 'lang').describe('l', 'Language chosen')
      .example('sudo $0 add -t test/template.cpp -l cpp')
      .argv;
    let { t } = argv;
    let lang = (argv.l == undefined ? config['lang'] : argv.l).toLowerCase();
    if(validLang(lang)) {
      let obj = template;
      let data = fs.readFileSync(getPath(process.cwd(), t), 'utf8');
      obj[lang] = data;
      fs.writeFileSync(getPath(__dirname, 'template.json'), JSON.stringify(obj, null, 2), 'utf8');
    }
    else{
      console.log(chalk.red(LANG_ERR));
    }
  })
  .command('config', 'Change config file', (yargs) => {
    const argv = yargs
      .usage('Usage: $0 config')
      .alias('l', 'list').describe('l', 'List language and their extension').boolean('l')
      .example('sudo $0 config -l')
      .argv;

    if (argv.list){
      const spinner = ora('Getting languages').start();
      let table = new Table({
        head: ['Language', 'File Extension'],
        colWidths: [20, 20]
      });
      for(let name in languages){
        table.push([name, languages[name]]);
      }
      spinner.stop();
      console.log(table.toString());
    }
    else {
      const questions = [{
        type: 'input',
        name: 'default_lang',
        message: 'Enter default language code <Leave blank in case unchanged>'
      } , {
        type: 'input',
        name: 'default_ques',
        message: 'Enter default number of questions <Leave blank in case unchanged>'
      }];
      inquirer.prompt(questions).then((answers) => {
        var obj = config;
        if (answers.default_lang !== ''){
          obj.default_lang = answers.default_lang;
        }
        if (answers.default_ques !== ''){
          obj.default_ques = answers.default_ques;
        }
        fs.writeFileSync(__dirname+'/config.json', JSON.stringify(obj, null, 2), 'utf8');
      });
    }
  })
  .help('h')
  .alias('h', 'help')
  .argv;
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var extName = require('./');
var meow = require('meow');

var cli = meow({
	help: [
		'Usage',
		'  $ ext-name <file>',
		'',
		'Example',
		'  $ ext-name file.tar.gz'
	].join('\n')
});

if (!cli.input.length) {
	console.error([
		'Specify a file',
		'',
		'Example',
		'  $ ext-name file.tar.gz'
	].join('\n'));

	process.exit(1);
}

console.log(extName(cli.input[0]));
<commit_msg>Small tweak<commit_after>#!/usr/bin/env node
'use strict';

var meow = require('meow');
var extName = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ ext-name <file>',
		'',
		'Example',
		'  $ ext-name file.tar.gz'
	].join('\n')
});

if (!cli.input.length) {
	console.error([
		'Specify a file',
		'',
		'Example',
		'  $ ext-name file.tar.gz'
	].join('\n'));

	process.exit(1);
}

console.log(extName(cli.input[0]));
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const yargs = require('yargs');
const inquirer = require('inquirer');
const fs = require('fs');
const ora = require('ora');
const chalk = require('chalk');
const request = require('request');
const Table = require('cli-table');
const config = require('./config');
const path = require('path');

const RUN_URL = 'http://api.hackerrank.com/checker/submission.json';
const LANG_URL = 'http://api.hackerrank.com/checker/languages.json';

const openIssue = () => {
  console.log(chalk.yellow('If problem persist, please open an issue at https://github.com/ManrajGrover/HackerRank-CLI/issues .'));
  process.exit(-1);
}

const end = () => {
  console.log(chalk.cyan('Copyright © 2016 Manraj Singh.'))
  console.log(chalk.green('Powered by HackerRank API (https://www.hackerrank.com/api)'));
  console.log(chalk.yellow('Support project at https://github.com/ManrajGrover/HackerRank-CLI'));
  process.exit(-1);
}

/* eslint-disable no-unused-vars */

const argv = yargs
  .usage('$0 <command>')
  .command('run', 'Run code on HackerRank server', (yargs) => {
    var argv = yargs
      .usage('Usage: $0 run <options>')
      .demand(['s', 'i', 'o'])
      .alias('s', 'source').describe('s', 'Source Code file path')
      .alias('i', 'input').describe('i', 'Input file path')
      .alias('l', 'language').describe('l', 'Language. Change `config` for default.')
      .alias('o', 'output').describe('o', 'Output file path')
      .example('$0 run -s A.cpp -i Input00.in -o Output.txt -l 2')
      .argv;

    if (config.api_key === '') {
      console.log(chalk.red('Please add API KEY to config. Run `sudo hackerrank config` for this.'));
      openIssue();
      process.exit(-1);
    }

    const source = fs.readFileSync(argv.source, 'utf8');
    const input = fs.readFileSync(argv.input, 'utf8');
    const output = argv.output;
    const spinner = ora('Running').start();
    const lang = argv.language === undefined ? config.default_lang : argv.language;
    var data = {
      'api_key': config.api_key,
      'source': source,
      'lang': parseInt(lang),
      'testcases': `[${JSON.stringify(input)}]'`
    };
    request.post({url: RUN_URL, form: data}, (err, response) => {
      if (err) {
        spinner.stop();
        console.log(chalk.red('Error Occured'));
        openIssue();
      } else {
        spinner.stop();
        const result = JSON.parse(response.body).result;
        if (result.compilemessage !== '') {
          console.log(chalk.red('Compilation Error'));
          console.log(chalk.red(result.compilemessage));
          process.exit(-1);
        }
        var data = result.stdout[0] === undefined ? '' : result.stdout[0];
        fs.writeFileSync(output, data, 'utf8');
        var table = new Table({
          head: ['Message', 'Memory', 'Time'],
          colWidths: [20, 20, 20]
        });
        table.push([result['message'], result['memory'][0], result['time'][0]]);
        console.log(table.toString());
        end();
      }
    });
  })
  .command('config', 'Change config file', (yargs) => {
    var argv = yargs
      .usage('Usage: sudo $0 config [options]')
      .alias('l', 'list').describe('l', 'List language and their code').boolean('l')
      .example('sudo $0 config -l')
      .argv;

    if (argv.list) {
      const spinner = ora('Getting languages').start();
      request(LANG_URL, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          var languages = JSON.parse(body);
          var names = languages.languages.names;
          var codes = languages.languages.codes;
          spinner.stop();
          var table = new Table({
            head: ['Language', 'Code', 'Number'],
            colWidths: [20, 20, 20]
          });
          for (let name in names) {
            table.push([names[name], name, codes[name]]);
          }
          console.log(table.toString());
          end();
        } else {
          spinner.stop();
          console.log(chalk.red(error));
          openIssue();
        }
      });
    } else {
      const questions = [{
        type: 'input',
        name: 'api_key',
        message: 'Enter API Key <leave blank incase unchanged>'
      }, {
        type: 'input',
        name: 'default_lang',
        message: 'Enter default language number (Run `sudo hackerrank config -l` to list codes)'
      }];
      inquirer.prompt(questions).then((answers) => {
        var obj = config;
        if (answers.api_key !== '') {
          obj.api_key = answers.api_key;
        }
        if (answers.default_lang !== '') {
          obj.default_lang = answers.default_lang;
        }
        fs.writeFileSync(path.resolve(__dirname, '/config.json'), JSON.stringify(obj, null, 2), 'utf8');
      });
    }
  })
  .help('h')
  .alias('h', 'help')
  .argv;

/* eslint-enable no-unused-vars */
<commit_msg>CLI: Moved constants to separate file<commit_after>#!/usr/bin/env node

'use strict';

const yargs = require('yargs');
const inquirer = require('inquirer');
const fs = require('fs');
const ora = require('ora');
const chalk = require('chalk');
const request = require('request');
const Table = require('cli-table');
const config = require('./config');
const path = require('path');

const {
  RUN_URL,
  LANG_URL,
  OPEN_ISSUE_MSG,
  AUTHOR_MSG,
  POWERED_BY_MSG,
  SUPPORT_MSG,
  API_KEY_ERR_MSG,
  GENERAL_ERR_MSG,
  COMPILATION_ERR_MSG
} = require('./constants');

const openIssue = () => {
  console.log(chalk.yellow(OPEN_ISSUE_MSG));
  process.exit(-1);
}

const end = () => {
  console.log(chalk.cyan(AUTHOR_MSG))
  console.log(chalk.green(POWERED_BY_MSG));
  console.log(chalk.yellow(SUPPORT_MSG));
  process.exit(-1);
}

/* eslint-disable no-unused-vars */

const argv = yargs
  .usage('$0 <command>')
  .command('run', 'Run code on HackerRank server', (yargs) => {
    var argv = yargs
      .usage('Usage: $0 run <options>')
      .demand(['s', 'i', 'o'])
      .alias('s', 'source').describe('s', 'Source Code file path')
      .alias('i', 'input').describe('i', 'Input file path')
      .alias('l', 'language').describe('l', 'Language. Change `config` for default.')
      .alias('o', 'output').describe('o', 'Output file path')
      .example('$0 run -s A.cpp -i Input00.in -o Output.txt -l 2')
      .argv;

    if (config.api_key === '') {
      console.log(chalk.red(API_KEY_ERR_MSG));
      openIssue();
      process.exit(-1);
    }

    const source = fs.readFileSync(argv.source, 'utf8');
    const input = fs.readFileSync(argv.input, 'utf8');
    const output = argv.output;
    const spinner = ora('Running').start();
    const lang = argv.language === undefined ? config.default_lang : argv.language;
    var data = {
      'api_key': config.api_key,
      'source': source,
      'lang': parseInt(lang),
      'testcases': `[${JSON.stringify(input)}]'`
    };
    request.post({url: RUN_URL, form: data}, (err, response) => {
      if (err) {
        spinner.stop();
        console.log(chalk.red(GENERAL_ERR_MSG));
        openIssue();
      } else {
        spinner.stop();
        const result = JSON.parse(response.body).result;
        if (result.compilemessage !== '') {
          console.log(chalk.red(COMPILATION_ERR_MSG));
          console.log(chalk.red(result.compilemessage));
          process.exit(-1);
        }
        var data = result.stdout[0] === undefined ? '' : result.stdout[0];
        fs.writeFileSync(output, data, 'utf8');
        var table = new Table({
          head: ['Message', 'Memory', 'Time'],
          colWidths: [20, 20, 20]
        });
        table.push([result['message'], result['memory'][0], result['time'][0]]);
        console.log(table.toString());
        end();
      }
    });
  })
  .command('config', 'Change config file', (yargs) => {
    var argv = yargs
      .usage('Usage: sudo $0 config [options]')
      .alias('l', 'list').describe('l', 'List language and their code').boolean('l')
      .example('sudo $0 config -l')
      .argv;

    if (argv.list) {
      const spinner = ora('Getting languages').start();
      request(LANG_URL, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          var languages = JSON.parse(body);
          var names = languages.languages.names;
          var codes = languages.languages.codes;
          spinner.stop();
          var table = new Table({
            head: ['Language', 'Code', 'Number'],
            colWidths: [20, 20, 20]
          });
          for (let name in names) {
            table.push([names[name], name, codes[name]]);
          }
          console.log(table.toString());
          end();
        } else {
          spinner.stop();
          console.log(chalk.red(error));
          openIssue();
        }
      });
    } else {
      const questions = [{
        type: 'input',
        name: 'api_key',
        message: 'Enter API Key <leave blank incase unchanged>'
      }, {
        type: 'input',
        name: 'default_lang',
        message: 'Enter default language number (Run `sudo hackerrank config -l` to list codes)'
      }];
      inquirer.prompt(questions).then((answers) => {
        var obj = config;
        if (answers.api_key !== '') {
          obj.api_key = answers.api_key;
        }
        if (answers.default_lang !== '') {
          obj.default_lang = answers.default_lang;
        }
        fs.writeFileSync(path.resolve(__dirname, '/config.json'), JSON.stringify(obj, null, 2), 'utf8');
      });
    }
  })
  .help('h')
  .alias('h', 'help')
  .argv;

/* eslint-enable no-unused-vars */
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
  TODO
    replace mkdir and rm with fs.xxx
    rename?
    docs
      unify description in meow readme and package.json
    gifs
    mention on twitter
*/

"use strict";

const os = require("os");
const fs = require("fs");
const path = require("path");
const meow = require("meow");
const chalk = require("chalk");
const execa = require("execa");
const inquirer = require("inquirer");
const indentString = require("indent-string");

// Global variables

const configDir = path.join(os.homedir(), ".config");
const configPath = path.join(configDir, "sshfs-box.json");
const promptPageSize = 16; // https://github.com/SBoudrias/Inquirer.js/#question

// CLI

const cli = meow(
  `
  Small CLI tool to mount/unmount directories remote servers with SSHFS.

  Usage
    $ sshfs-box

  Options
    --config, -c  Configure remote & local paths
                  Config stored in ~/.config/sshfs-box.json
`,
  {
    alias: {
      c: "config",
      configure: "config",
      settings: "config",
    },
  }
);

// Main

async function main() {
  let configString;
  try {
    configString = fs.readFileSync(configPath, "utf8");
  } catch (e) {
    stdoutNewline(1);
    stdout(`Can't open config on ${configPath}, creating new config`);
    const config = await promptEditConfig();
    await promptSshfs(config);
    return;
  }

  if (cli.flags.config) {
    const config = await promptEditConfig(configString);
    await promptSshfs(config);
  } else {
    const [configValid, errorMessage] = validateConfigString(configString);
    if (configValid) {
      await promptSshfs(JSON.parse(configString));
    } else {
      stdoutNewline(1);
      stdoutError({
        title: `${
          configPath
        } does not contain valid config, opening editor so you can fix it`,
        err: errorMessage,
      });

      const config = await promptEditConfig(configString);
      await promptSshfs(config);
    }
  }
}

main(); // Start the app.

// Prompt functions.

async function promptEditConfig(defaultConfigOverride) {
  const defaultConfig = JSON.stringify(
    {
      urls: ["user@host1:", "user@host2:/home/user", "user@host2:/www"],
      folder: path.join(os.homedir(), "remote"),
    },
    null,
    2
  );

  const promptSettings = {
    type: "editor",
    name: "config",
    message: "Configure sshfs-box",
    default: defaultConfigOverride || defaultConfig,
    validate: userInput => {
      const [valid, err] = validateConfigString(userInput);
      // Inquirer expects true if input is valid, otherwise string error message.
      return valid ? true : err;
    },
  };

  stdoutNewline(1);
  const response = await inquirer.prompt(promptSettings);

  const configString = response.config;
  const config = JSON.parse(configString);
  try {
    fs.mkdirSync(configDir);
  } catch (e) {
    // TODO.
  }

  fs.writeFileSync(configPath, configString, "utf8");

  return config;
}

async function promptSshfs(config) {
  let mountStr;
  try {
    mountStr = await execa.shellSync("mount");
  } catch (err) {
    stdoutError({
      title: `Error while getting SSHFS mounted folders, exiting`,
      err: err,
    });
    stdoutNewline(1);

    return;
  }

  const mounted = mountStr.stdout.split(os.EOL);

  const destinations = config.urls.map(remote => {
    // user@host:/dir/subdir => user@host--dir-subdir
    const local = path.join(
      config.folder,
      remote.replace(/:/g, "-").replace(/\//g, "-")
    );

    const isChecked = isMountedWithMount(mounted, remote, local);

    return {
      name: `${remote} ↔ ${local}`,
      checked: isChecked,
      remote: remote,
      local: local,
    };
  });

  stdoutNewline(1);
  const response = await inquirer.prompt({
    type: "checkbox",
    message: "SSHFS mount/unmount dirs",
    name: "urls",
    choices: destinations,
    pageSize: promptPageSize,
  });
  stdoutNewline(1);

  const selectedUrls = response.urls;

  // mount selected items that are not already mounted
  const mountItems = selectedUrls
    .map(url => destinations.find(item => item.name === url))
    .filter(item => !isMountedWithMount(mounted, item.remote, item.local));

  for (const mountItem of mountItems) {
    try {
      await execa("mkdir", ["-p", mountItem.local]);
    } catch (err) {
      stdoutError({
        title: mountItem.remote,
        description: `Error while creating local directory "${
          mountItem.local
        }"`,
        err: err,
      });
      continue;
    }

    try {
      await execa("sshfs", [mountItem.remote, mountItem.local]);
    } catch (err) {
      stdoutError({
        title: mountItem.remote,
        description: `Error while mounting`,
        err: err,
      });
      continue;
    }

    stdoutMounted(mountItem.remote);
  }

  // unmount items that have been unselected
  const unmountItems = destinations
    .filter(item => !selectedUrls.includes(item.name))
    .filter(item => isMountedWithMount(mounted, item.remote, item.local));
  let unmountErrors = [];
  for (const item of unmountItems) {
    const unmountSuccesful = await unmount(item);
    if (!unmountSuccesful) {
      unmountErrors.push(item);
      continue;
    }
  }

  if (unmountErrors.length > 0) {
    const forceUnmountChoices = unmountErrors.map(choice => {
      choice.checked = false;
      return choice;
    });

    stdoutNewline(1);
    const answer = await inquirer.prompt({
      type: "checkbox",
      message:
        "There were poblems with unmomunting, force unmount by killing SSHFS process?",
      name: "urls",
      choices: forceUnmountChoices,
      pageSize: promptPageSize,
    });
    stdoutNewline(1);

    const forceUnmountUrls = answer.urls;

    const forceUnmountItems = forceUnmountUrls.map(url =>
      destinations.find(item => item.name === url)
    );

    let responsePsx = null;
    try {
      responsePsx = await execa("ps", ["-x"]);
    } catch (err) {
      stdoutError({
        title: `Error while running "ps -x" command`,
        err: err,
      });
      return;
    }

    const processes = responsePsx.stdout.split(os.EOL);
    if (!processes) {
      stdoutError({
        title: `After running "ps -x" we were unable to find any SSHFS processes`,
      });
      return;
    }

    for (const item of forceUnmountItems) {
      const processRow = processes.find(row =>
        row.includes(`sshfs ${item.remote} ${item.local}`)
      );
      if (!processRow) {
        stdoutError({
          title: item.remote,
          description: `Unable to find "${item.local}" SSHFS process`,
        });
        continue;
      }
      const pidMatches = processRow.match(/^\s*\d+/);
      const processId = pidMatches.length > 0 ? pidMatches[0] : null;

      if (!processId) {
        stdoutError({
          title: item.remote,
          description: `Unable to parse SSHFS process id`,
        });
        continue;
      }

      let processKilled = false;
      try {
        await execa("kill", ["-9", processId]);
        processKilled = true;
      } catch (err) {
        stdoutError({
          title: item.remote,
          description: `Unable to kill SSHFS process with id "${processId}"`,
          err: err,
        });
        continue;
      }
      if (processKilled) {
        stdoutUnmountForce(item.remote, processId);
      }

      await unmount(item);
    }
  }
}

function isMountedWithMount(mountRows, remote, local) {
  return mountRows.some(mount => mount.startsWith(remote + " on " + local));
}

async function unmount(item) {
  try {
    await execa("fusermount", ["-u", item.local]);
  } catch (err) {
    stdoutError({
      title: item.remote,
      description: `Unable to unmount`,
      err: err,
    });
    return false;
  }

  // TODO async
  // TODO error handling
  execa.sync("rm", ["-r", item.local]);

  stdoutUnmounted(item.remote);
  return true;
}

// Utility functions

function validateConfigString(configString) {
  let config;
  try {
    config = JSON.parse(configString);
  } catch (e) {
    return [false, "Error parsing JSON"];
  }

  if (!config.urls) {
    return [false, `The "urls" field is missing or empty`];
  } else if (!Array.isArray(config.urls)) {
    return [false, `The "urls" filed is not an array`];
  } else if (config.urls.some(item => typeof item !== "string")) {
    return [false, `All fields in "urls" filed need to be string`];
  } else if (!config.folder) {
    return [false, `The "folder" field is missing or empy`];
  } else if (typeof config.folder !== "string") {
    return [false, `The "folder" field must be string`];
  }

  return [true, null];
}

function removeTrailingNewlines(str) {
  while (str.endsWith(os.EOL)) {
    str = str.slice(0, str.length - os.EOL.length);
  }
  return str;
}

function stdout(input) {
  process.stdout.write(input);
}

function stdoutError({ title, description, err }) {
  stdout(chalk.bgRed(`[ERROR] ${title}`));
  stdoutNewline(1);

  if (description) {
    stdout(indentString(description, 4));
    stdoutNewline(1);
  }

  if (err) {
    let errFormatted = err;
    errFormatted = String(errFormatted);
    errFormatted = removeTrailingNewlines(errFormatted);
    errFormatted = indentString(errFormatted, 4);
    stdout(errFormatted);
    stdoutNewline(1);
  }
}

function stdoutMounted(input) {
  stdout(chalk.green(`[MOUNTED] ${input}`));
  stdoutNewline(1);
}

function stdoutUnmounted(input) {
  stdout(chalk.blue(`[UNMOUNTED]: ${input}`));
  stdoutNewline(1);
}

function stdoutUnmountForce(input, precessId) {
  stdout(chalk.bgBlue(`[SSHFS PROCESS KILLED]: ${input}`));
  stdoutNewline(1);
  stdout(indentString(`Killed process with id "${precessId}"`, 4));
  stdoutNewline(1);
}

function stdoutNewline(number = 1) {
  let newlines = "";
  for (let i = 0; i < number; i++) {
    newlines += os.EOL;
  }
  stdout(newlines);
}
<commit_msg>Grammar<commit_after>#!/usr/bin/env node

/*
  TODO
    add support for spacer
    replace mkdir and rm with fs.xxx
    finish remaining todos
    rename?
    docs
      unify description in meow readme and package.json
    gifs
    mention on twitter
*/

"use strict";

const os = require("os");
const fs = require("fs");
const path = require("path");
const meow = require("meow");
const chalk = require("chalk");
const execa = require("execa");
const inquirer = require("inquirer");
const indentString = require("indent-string");

// Global variables

const configDir = path.join(os.homedir(), ".config");
const configPath = path.join(configDir, "sshfs-box.json");
const promptPageSize = 16; // https://github.com/SBoudrias/Inquirer.js/#question

// CLI

const cli = meow(
  `
  Small CLI tool to mount/unmount directories remote servers with SSHFS.

  Usage
    $ sshfs-box

  Options
    --config, -c  Configure remote & local paths
                  Config stored in ~/.config/sshfs-box.json
`,
  {
    alias: {
      c: "config",
      configure: "config",
      settings: "config",
    },
  }
);

// Main

async function main() {
  let configString;
  try {
    configString = fs.readFileSync(configPath, "utf8");
  } catch (e) {
    stdoutNewline(1);
    stdout(`Can't open config on ${configPath}, creating new config`);
    const config = await promptEditConfig();
    await promptSshfs(config);
    return;
  }

  if (cli.flags.config) {
    const config = await promptEditConfig(configString);
    await promptSshfs(config);
  } else {
    const [configValid, errorMessage] = validateConfigString(configString);
    if (configValid) {
      await promptSshfs(JSON.parse(configString));
    } else {
      stdoutNewline(1);
      stdoutError({
        title: `${
          configPath
        } does not contain valid config, opening editor so you can fix it`,
        err: errorMessage,
      });

      const config = await promptEditConfig(configString);
      await promptSshfs(config);
    }
  }
}

main(); // Start the app.

// Prompt functions.

async function promptEditConfig(defaultConfigOverride) {
  const defaultConfig = JSON.stringify(
    {
      urls: ["user@host1:", "user@host2:/home/user", "user@host2:/www"],
      folder: path.join(os.homedir(), "remote"),
    },
    null,
    2
  );

  const promptSettings = {
    type: "editor",
    name: "config",
    message: "Configure sshfs-box",
    default: defaultConfigOverride || defaultConfig,
    validate: userInput => {
      const [valid, err] = validateConfigString(userInput);
      // Inquirer expects true if input is valid, otherwise string error message.
      return valid ? true : err;
    },
  };

  stdoutNewline(1);
  const response = await inquirer.prompt(promptSettings);

  const configString = response.config;
  const config = JSON.parse(configString);
  try {
    fs.mkdirSync(configDir);
  } catch (e) {
    // TODO.
  }

  fs.writeFileSync(configPath, configString, "utf8");

  return config;
}

async function promptSshfs(config) {
  let mountStr;
  try {
    mountStr = await execa.shellSync("mount");
  } catch (err) {
    stdoutError({
      title: `Error while getting SSHFS mounted folders, exiting`,
      err: err,
    });
    stdoutNewline(1);

    return;
  }

  const mounted = mountStr.stdout.split(os.EOL);

  const destinations = config.urls.map(remote => {
    // user@host:/dir/subdir => user@host--dir-subdir
    const local = path.join(
      config.folder,
      remote.replace(/:/g, "-").replace(/\//g, "-")
    );

    const isChecked = isMountedWithMount(mounted, remote, local);

    return {
      name: `${remote} ↔ ${local}`,
      checked: isChecked,
      remote: remote,
      local: local,
    };
  });

  stdoutNewline(1);
  const response = await inquirer.prompt({
    type: "checkbox",
    message: "SSHFS mount/unmount dirs",
    name: "urls",
    choices: destinations,
    pageSize: promptPageSize,
  });
  stdoutNewline(1);

  const selectedUrls = response.urls;

  // Mount selected items that are not already mounted.
  const mountItems = selectedUrls
    .map(url => destinations.find(item => item.name === url))
    .filter(item => !isMountedWithMount(mounted, item.remote, item.local));

  for (const mountItem of mountItems) {
    try {
      await execa("mkdir", ["-p", mountItem.local]);
    } catch (err) {
      stdoutError({
        title: mountItem.remote,
        description: `Error while creating local directory "${
          mountItem.local
        }"`,
        err: err,
      });
      continue;
    }

    try {
      await execa("sshfs", [mountItem.remote, mountItem.local]);
    } catch (err) {
      stdoutError({
        title: mountItem.remote,
        description: `Error while mounting`,
        err: err,
      });
      continue;
    }

    stdoutMounted(mountItem.remote);
  }

  // Unmount items that have been unselected.
  const unmountItems = destinations
    .filter(item => !selectedUrls.includes(item.name))
    .filter(item => isMountedWithMount(mounted, item.remote, item.local));
  let unmountErrors = [];
  for (const item of unmountItems) {
    const unmountSuccesful = await unmount(item);
    if (!unmountSuccesful) {
      unmountErrors.push(item);
      continue;
    }
  }

  // Force unmount.
  if (unmountErrors.length > 0) {
    const forceUnmountChoices = unmountErrors.map(choice => {
      choice.checked = false;
      return choice;
    });

    stdoutNewline(1);
    const answer = await inquirer.prompt({
      type: "checkbox",
      message:
        "There were poblems with unmomunting, force unmount by killing SSHFS process?",
      name: "urls",
      choices: forceUnmountChoices,
      pageSize: promptPageSize,
    });
    stdoutNewline(1);

    const forceUnmountUrls = answer.urls;

    const forceUnmountItems = forceUnmountUrls.map(url =>
      destinations.find(item => item.name === url)
    );

    let responsePsx = null;
    try {
      responsePsx = await execa("ps", ["-x"]);
    } catch (err) {
      stdoutError({
        title: `Error while running "ps -x" command`,
        err: err,
      });
      return;
    }

    const processes = responsePsx.stdout.split(os.EOL);
    if (!processes) {
      stdoutError({
        title: `After running "ps -x" we were unable to find any SSHFS processes`,
      });
      return;
    }

    for (const item of forceUnmountItems) {
      const processRow = processes.find(row =>
        row.includes(`sshfs ${item.remote} ${item.local}`)
      );
      if (!processRow) {
        stdoutError({
          title: item.remote,
          description: `Unable to find "${item.local}" SSHFS process`,
        });
        continue;
      }
      const pidMatches = processRow.match(/^\s*\d+/);
      const processId = pidMatches.length > 0 ? pidMatches[0] : null;

      if (!processId) {
        stdoutError({
          title: item.remote,
          description: `Unable to parse SSHFS process id`,
        });
        continue;
      }

      let processKilled = false;
      try {
        await execa("kill", ["-9", processId]);
        processKilled = true;
      } catch (err) {
        stdoutError({
          title: item.remote,
          description: `Unable to kill SSHFS process with id "${processId}"`,
          err: err,
        });
        continue;
      }
      if (processKilled) {
        stdoutUnmountForce(item.remote, processId);
      }

      await unmount(item);
    }
  }
}

function isMountedWithMount(mountRows, remote, local) {
  return mountRows.some(mount => mount.startsWith(remote + " on " + local));
}

async function unmount(item) {
  try {
    await execa("fusermount", ["-u", item.local]);
  } catch (err) {
    stdoutError({
      title: item.remote,
      description: `Unable to unmount`,
      err: err,
    });
    return false;
  }

  // TODO async
  // TODO error handling
  execa.sync("rm", ["-r", item.local]);

  stdoutUnmounted(item.remote);
  return true;
}

// Utility functions

function validateConfigString(configString) {
  let config;
  try {
    config = JSON.parse(configString);
  } catch (e) {
    return [false, "Error parsing JSON"];
  }

  if (!config.urls) {
    return [false, `The "urls" field is missing or empty`];
  } else if (!Array.isArray(config.urls)) {
    return [false, `The "urls" filed is not an array`];
  } else if (config.urls.some(item => typeof item !== "string")) {
    return [false, `All fields in "urls" filed need to be string`];
  } else if (!config.folder) {
    return [false, `The "folder" field is missing or empy`];
  } else if (typeof config.folder !== "string") {
    return [false, `The "folder" field must be string`];
  }

  return [true, null];
}

function removeTrailingNewlines(str) {
  while (str.endsWith(os.EOL)) {
    str = str.slice(0, str.length - os.EOL.length);
  }
  return str;
}

function stdout(input) {
  process.stdout.write(input);
}

function stdoutError({ title, description, err }) {
  stdout(chalk.bgRed(`[ERROR] ${title}`));
  stdoutNewline(1);

  if (description) {
    stdout(indentString(description, 4));
    stdoutNewline(1);
  }

  if (err) {
    let errFormatted = err;
    errFormatted = String(errFormatted);
    errFormatted = removeTrailingNewlines(errFormatted);
    errFormatted = indentString(errFormatted, 4);
    stdout(errFormatted);
    stdoutNewline(1);
  }
}

function stdoutMounted(input) {
  stdout(chalk.green(`[MOUNTED] ${input}`));
  stdoutNewline(1);
}

function stdoutUnmounted(input) {
  stdout(chalk.blue(`[UNMOUNTED]: ${input}`));
  stdoutNewline(1);
}

function stdoutUnmountForce(input, precessId) {
  stdout(chalk.bgBlue(`[SSHFS PROCESS KILLED]: ${input}`));
  stdoutNewline(1);
  stdout(indentString(`Killed process with id "${precessId}"`, 4));
  stdoutNewline(1);
}

function stdoutNewline(number = 1) {
  let newlines = "";
  for (let i = 0; i < number; i++) {
    newlines += os.EOL;
  }
  stdout(newlines);
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* eslint-env node */
/* eslint no-nested-ternary: 0 */
'use strict';

var meow = require('meow');
var path = require('path');
var swPrecache = require('./');

function setDefaults(cli, configFileFlags) {
  var compositeFlags = cli.flags;

  compositeFlags.root = compositeFlags.root || configFileFlags.root || './';
  if (compositeFlags.root.lastIndexOf('/') !== compositeFlags.root.length - 1) {
    compositeFlags.root += '/';
  }

  compositeFlags.stripPrefix = compositeFlags.stripPrefix ||
    configFileFlags.stripPrefix || compositeFlags.root;

  compositeFlags.stripPrefixMulti = compositeFlags.stripPrefixMulti ||
    configFileFlags.stripPrefixMulti || {};

  compositeFlags.swFile = compositeFlags.swFile || configFileFlags.swFile ||
    'service-worker.js';
  compositeFlags.swFilePath = compositeFlags.swFilePath ||
    configFileFlags.swFilePath ||
    path.join(compositeFlags.root, compositeFlags.swFile);

  compositeFlags.cacheId = compositeFlags.cacheId ||
    configFileFlags.cacheId || cli.pkg.name;

  compositeFlags.dynamicUrlToDependencies =
    compositeFlags.dynamicUrlToDependencies ||
    configFileFlags.dynamicUrlToDependencies;

  compositeFlags.directoryIndex = compositeFlags.directoryIndex ||
    configFileFlags.directoryIndex;

  compositeFlags.navigateFallback = compositeFlags.navigateFallback ||
    configFileFlags.navigateFallback;

  compositeFlags.staticFileGlobs = compositeFlags.staticFileGlobs ||
    configFileFlags.staticFileGlobs;
  if (compositeFlags.staticFileGlobs) {
    if (typeof compositeFlags.staticFileGlobs === 'string') {
      compositeFlags.staticFileGlobs = [compositeFlags.staticFileGlobs];
    }
  } else {
    compositeFlags.staticFileGlobs = [compositeFlags.root + '/**/*.*'];
  }

  compositeFlags.ignoreUrlParametersMatching =
    compositeFlags.ignoreUrlParametersMatching ||
    configFileFlags.ignoreUrlParametersMatching;
  if (compositeFlags.ignoreUrlParametersMatching &&
      typeof compositeFlags.ignoreUrlParametersMatching === 'string') {
    compositeFlags.ignoreUrlParametersMatching =
      compositeFlags.ignoreUrlParametersMatching.split(',').map(function(s) {
        return new RegExp(s);
      });
  }

  compositeFlags.importScripts = compositeFlags.importScripts ||
    configFileFlags.importScripts;
  if (compositeFlags.importScripts &&
      typeof compositeFlags.importScripts === 'string') {
    compositeFlags.importScripts = compositeFlags.importScripts.split(',');
  }

  compositeFlags.runtimeCaching = compositeFlags.runtimeCaching ||
    configFileFlags.runtimeCaching;

  compositeFlags.templateFilePath = compositeFlags.templateFilePath ||
    configFileFlags.templateFilePath;

  compositeFlags.maximumFileSizeToCacheInBytes =
    compositeFlags.maximumFileSizeToCacheInBytes ||
    configFileFlags.maximumFileSizeToCacheInBytes;

  // We can't just use ||, since compositeFlags.skipWaiting might be false.
  compositeFlags.skipWaiting = ('skipWaiting' in compositeFlags) ?
    compositeFlags.skipWaiting :
    (('skipWaiting' in configFileFlags) ?
      configFileFlags.skipWaiting : undefined);

  // We can't just use ||, since compositeFlags.clientsClaim might be false.
  compositeFlags.clientsClaim = ('clientsClaim' in compositeFlags) ?
    compositeFlags.clientsClaim :
    (('clientsClaim' in configFileFlags) ?
      configFileFlags.clientsClaim : undefined);

  compositeFlags.dontCacheBustUrlsMatching =
    compositeFlags.dontCacheBustUrlsMatching ||
    configFileFlags.dontCacheBustUrlsMatching;

  return compositeFlags;
}

var cli = meow({
  help: 'Options from https://github.com/GoogleChrome/sw-precache#options ' +
        'are accepted as flags.\nAlternatively, use --config <file>, where ' +
        '<file> is the path to a JavaScript file that defines the same ' +
        'options via module.exports.\n' +
        'When both a config file and command line option is given, the ' +
        'command line option takes precedence.'
});

// If the --config option is used, then read the options from an external
// JSON configuration file. Options from the --config file can be overwritten
// by any command line options.
var configFileFlags = cli.flags.config ?
  require(path.resolve(cli.flags.config)) : {};
var options = setDefaults(cli, configFileFlags);

swPrecache.write(options.swFilePath, options, function(error) {
  if (error) {
    console.error(error.stack);
    process.exit(1);
  }

  console.log(options.swFilePath,
    'has been generated with the service worker contents.');
});
<commit_msg>fix: set navigateFallbackWhitelist from config (#227)<commit_after>#!/usr/bin/env node
/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* eslint-env node */
/* eslint no-nested-ternary: 0 */
'use strict';

var meow = require('meow');
var path = require('path');
var swPrecache = require('./');

function setDefaults(cli, configFileFlags) {
  var compositeFlags = cli.flags;

  compositeFlags.root = compositeFlags.root || configFileFlags.root || './';
  if (compositeFlags.root.lastIndexOf('/') !== compositeFlags.root.length - 1) {
    compositeFlags.root += '/';
  }

  compositeFlags.stripPrefix = compositeFlags.stripPrefix ||
    configFileFlags.stripPrefix || compositeFlags.root;

  compositeFlags.stripPrefixMulti = compositeFlags.stripPrefixMulti ||
    configFileFlags.stripPrefixMulti || {};

  compositeFlags.swFile = compositeFlags.swFile || configFileFlags.swFile ||
    'service-worker.js';
  compositeFlags.swFilePath = compositeFlags.swFilePath ||
    configFileFlags.swFilePath ||
    path.join(compositeFlags.root, compositeFlags.swFile);

  compositeFlags.cacheId = compositeFlags.cacheId ||
    configFileFlags.cacheId || cli.pkg.name;

  compositeFlags.dynamicUrlToDependencies =
    compositeFlags.dynamicUrlToDependencies ||
    configFileFlags.dynamicUrlToDependencies;

  compositeFlags.directoryIndex = compositeFlags.directoryIndex ||
    configFileFlags.directoryIndex;

  compositeFlags.navigateFallback = compositeFlags.navigateFallback ||
    configFileFlags.navigateFallback;

  compositeFlags.navigateFallbackWhitelist = compositeFlags.navigateFallbackWhitelist ||
    configFileFlags.navigateFallbackWhitelist;

  compositeFlags.staticFileGlobs = compositeFlags.staticFileGlobs ||
    configFileFlags.staticFileGlobs;
  if (compositeFlags.staticFileGlobs) {
    if (typeof compositeFlags.staticFileGlobs === 'string') {
      compositeFlags.staticFileGlobs = [compositeFlags.staticFileGlobs];
    }
  } else {
    compositeFlags.staticFileGlobs = [compositeFlags.root + '/**/*.*'];
  }

  compositeFlags.ignoreUrlParametersMatching =
    compositeFlags.ignoreUrlParametersMatching ||
    configFileFlags.ignoreUrlParametersMatching;
  if (compositeFlags.ignoreUrlParametersMatching &&
      typeof compositeFlags.ignoreUrlParametersMatching === 'string') {
    compositeFlags.ignoreUrlParametersMatching =
      compositeFlags.ignoreUrlParametersMatching.split(',').map(function(s) {
        return new RegExp(s);
      });
  }

  compositeFlags.importScripts = compositeFlags.importScripts ||
    configFileFlags.importScripts;
  if (compositeFlags.importScripts &&
      typeof compositeFlags.importScripts === 'string') {
    compositeFlags.importScripts = compositeFlags.importScripts.split(',');
  }

  compositeFlags.runtimeCaching = compositeFlags.runtimeCaching ||
    configFileFlags.runtimeCaching;

  compositeFlags.templateFilePath = compositeFlags.templateFilePath ||
    configFileFlags.templateFilePath;

  compositeFlags.maximumFileSizeToCacheInBytes =
    compositeFlags.maximumFileSizeToCacheInBytes ||
    configFileFlags.maximumFileSizeToCacheInBytes;

  // We can't just use ||, since compositeFlags.skipWaiting might be false.
  compositeFlags.skipWaiting = ('skipWaiting' in compositeFlags) ?
    compositeFlags.skipWaiting :
    (('skipWaiting' in configFileFlags) ?
      configFileFlags.skipWaiting : undefined);

  // We can't just use ||, since compositeFlags.clientsClaim might be false.
  compositeFlags.clientsClaim = ('clientsClaim' in compositeFlags) ?
    compositeFlags.clientsClaim :
    (('clientsClaim' in configFileFlags) ?
      configFileFlags.clientsClaim : undefined);

  compositeFlags.dontCacheBustUrlsMatching =
    compositeFlags.dontCacheBustUrlsMatching ||
    configFileFlags.dontCacheBustUrlsMatching;

  return compositeFlags;
}

var cli = meow({
  help: 'Options from https://github.com/GoogleChrome/sw-precache#options ' +
        'are accepted as flags.\nAlternatively, use --config <file>, where ' +
        '<file> is the path to a JavaScript file that defines the same ' +
        'options via module.exports.\n' +
        'When both a config file and command line option is given, the ' +
        'command line option takes precedence.'
});

// If the --config option is used, then read the options from an external
// JSON configuration file. Options from the --config file can be overwritten
// by any command line options.
var configFileFlags = cli.flags.config ?
  require(path.resolve(cli.flags.config)) : {};
var options = setDefaults(cli, configFileFlags);

swPrecache.write(options.swFilePath, options, function(error) {
  if (error) {
    console.error(error.stack);
    process.exit(1);
  }

  console.log(options.swFilePath,
    'has been generated with the service worker contents.');
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

/*
 * Dependencies.
 */

var metaphone,
    pack;

metaphone = require('./');
pack = require('./package.json');

/*
 * Arguments.
 */

var argv;

argv = process.argv.slice(2);

/*
 * Command.
 */

var command;

command = Object.keys(pack.bin)[0];

/**
 * Help.
 */
function help() {
    console.log([
        '',
        'Usage: ' + command + ' [options] string',
        '',
        pack.description,
        '',
        'Options:',
        '',
        '  -h, --help           output usage information',
        '  -v, --version        output version number',
        '',
        'Usage:',
        '',
        '# output phonetics of given value',
        '$ ' + command + ' detestable',
        '# TTSTBL',
        '',
        '# output phonetics from stdin',
        '$ echo "vileness" | ' + command,
        '# FLNS'
    ].join('\n  ') + '\n');
}

/*
 * Program.
 */

if (
    argv.indexOf('--help') !== -1 ||
    argv.indexOf('-h') !== -1
) {
    help();
} else if (
    argv.indexOf('--version') !== -1 ||
    argv.indexOf('-v') !== -1
) {
    console.log(pack.version);
} else if (argv.length === 1) {
    console.log(metaphone(argv[0]));
} else if (argv.length) {
    help();
} else {
    process.stdin.resume();
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', function (data) {
        console.log(metaphone(data.trim()));
    });
}
<commit_msg>Update CLI with multi-value support, pipe-in detection<commit_after>#!/usr/bin/env node
'use strict';

/*
 * Dependencies.
 */

var metaphone,
    pack;

pack = require('./package.json');
metaphone = require('./');

/*
 * Detect if a value is expected to be piped in.
 */

var expextPipeIn;

expextPipeIn = !process.stdin.isTTY;

/*
 * Arguments.
 */

var argv;

argv = process.argv.slice(2);

/*
 * Command.
 */

var command;

command = Object.keys(pack.bin)[0];

/**
 * Get the distance for a word.
 *
 * @param {Array.<string>} values
 * @return {string}
 */
function phonetics(values) {
    return values.map(metaphone).join(' ');
}

/**
 * Help.
 *
 * @return {string}
 */
function help() {
    return [
        '',
        'Usage: ' + command + ' [options] <words...>',
        '',
        pack.description,
        '',
        'Options:',
        '',
        '  -h, --help           output usage information',
        '  -v, --version        output version number',
        '',
        'Usage:',
        '',
        '# output phonetics',
        '$ ' + command + ' considerations detestable',
        '# ' + phonetics(['considerations', 'detestable']),
        '',
        '# output phonetics from stdin',
        '$ echo "hiccups vileness" | ' + command,
        '# ' + phonetics(['hiccups', 'vileness']),
        ''
    ].join('\n  ') + '\n';
}

/**
 * Get the edit distance for a list containing one word.
 *
 * @param {Array.<string>} values
 */
function getPhonetics(values) {
    if (values.length) {
        console.log(phonetics(values));
    } else {
        process.stderr.write(help());
        process.exit(1);
    }
}

/*
 * Program.
 */

if (
    argv.indexOf('--help') !== -1 ||
    argv.indexOf('-h') !== -1
) {
    console.log(help());
} else if (
    argv.indexOf('--version') !== -1 ||
    argv.indexOf('-v') !== -1
) {
    console.log(pack.version);
} else if (argv.length) {
    getPhonetics(argv.join(' ').split(/\s+/g));
} else if (!expextPipeIn) {
    getPhonetics([]);
} else {
    process.stdin.resume();
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', function (data) {
        getPhonetics(data.trim().split(/\s+/g));
    });
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
var args = require('minimist')(process.argv.splice(2), {
  alias: {p: 'port', q: 'quiet', v: 'version'},
  boolean: ['color'],
  default: {color: true}
})

process.title = 'dat'

// set debug before requiring other modules
if (args.debug) {
  var debug = args.debug
  if (typeof args.debug === 'boolean') debug = '*' // default
  process.env.DEBUG = debug
}

if (args.version) {
  var pkg = require(__dirname + '/package.json')
  console.log(pkg.version)
  process.exit(0)
}

var fs = require('fs')
var singleLineLog = require('single-line-log')
var prettyBytes = require('pretty-bytes')
var chalk = require('chalk')
var dat = require('./index.js')
var usage = require('./usage')

function noop () {}

var cmd = args._[0]
var logger = getLogger()

var LOG_INTERVAL = (args.logspeed ? +args.logspeed : 200)
if (isNaN(LOG_INTERVAL)) LOG_INTERVAL = 200
if (!args.color) chalk = new chalk.constructor({enabled: false})

checkLocation()

function checkLocation () {
  var loc = process.cwd()
  if (!args.path) return runCommand(loc)
  loc = args.path
  fs.exists(loc, function (exists) {
    if (!exists) {
      logger.error('Does not exist:', loc)
      return usage('root.txt')
    }
    runCommand(loc)
  })
}

function runCommand (loc) {
  if (!cmd) return usage('root.txt')

  var db = dat({home: args.home})

  if (cmd === 'link') link(loc, db)
  else if (cmd === 'list') list(loc, db)
  else if (cmd) download(loc, db)
}

function link (loc, db) {
  var dirs = args._.slice(1)
  if (dirs.length === 0) dirs = loc

  var statsScan = db.fileStats(dirs, function (err, stats) {
    if (err) throw err
    printScanProgress(stats, true)
    clearInterval(scanInterval)
    logger.log('') // newline
    var statsAdd = db.addFiles(dirs, function (err, link) {
      printAddProgress(statsAdd, statsScan)
      clearInterval(addInterval)
      if (err) throw err
      db.joinTcpSwarm({link: link, port: args.port}, function (_err, swarm) {
        // ignore _err
        logger.stdout('') // clear progress lines
        logger.log(
          chalk.green.bold('Your Dat Link: ') +
          chalk.underline.blue('dat://' + swarm.link)
        )
        startProgressLogging({swarm: swarm})
      })
    })

    var addInterval = setInterval(function () {
      printAddProgress(statsAdd, statsScan)
    }, LOG_INTERVAL)
  })

  var scanInterval = setInterval(function () {
    printScanProgress(statsScan)
  }, LOG_INTERVAL)
}

function list (loc, db) {
  var hash = args._[1]
  db.joinTcpSwarm({link: hash, port: args.port}, function (err, swarm) {
    if (err) throw err
    var archive = db.drive.get(swarm.link, loc)
    archive.ready(function (err) {
      if (err) throw err
      archive.createEntryStream().on('data', function (o) {
        logger.log(o)
      })
    })
  })
}

function download (loc, db) {
  // download/share
  var hash = args._[0]
  if (!hash) return usage('root.txt')
  hash = hash.trim().replace('dat://', '').replace('dat:', '')
  if (hash.length !== 64) {
    logger.error('Error: Invalid dat link\n')
    return usage('root.txt')
  }
  var downloadStats = db.download(hash, loc, function (err, swarm) {
    if (err) throw err
    swarm.downloading = false
    swarm.downloadComplete = true
  })
  startProgressLogging(downloadStats)
}

function startProgressLogging (stats) {
  setInterval(function () {
    printSwarmStatus(stats)
  }, LOG_INTERVAL)
  printSwarmStatus(stats)

  process.on('SIGINT', function () {
    process.exit(1)
    setTimeout(function () {
      // sigterm if it is still running
      process.kill(process.pid)
    }, 2000)
  })
}

function printScanProgress (stats, last) {
  var dirCount = stats.directories + 1 // parent folder
  var msg = chalk.bold.blue('Calculating Size: ')
  if (last) msg = chalk.bold.green('Creating Dat Link ')
  msg += chalk.black(
    '(' + stats.files + ' files, ' + dirCount + ' folders, ' +
    (stats.size ? prettyBytes(stats.size) + ' total' : '') + ')'
  )
  logger.stdout(msg)
}

function printAddProgress (statsAdd, statsScan) {
  var msg = ''
  var indent = '  ' // indent for single file info

  while (true) {
    if (statsAdd.files.length === 0) break
    var complete = printFileProgress(statsAdd.files[0])
    if (complete) statsAdd.files.shift()
    else break
  }

  function printFileProgress (fileStats) {
    var complete = (fileStats.stats.bytesTotal === fileStats.stats.bytesRead)
    if (complete) {
      if (msg === '') {
        // hack to avoid flashing no progress
        msg += chalk.bold.gray(indent + '[' + 100 + '%] ')
        msg += chalk.blue(fileStats.name)
      }
      logger.stdout(chalk.green.dim(indent + '[Done] ') + chalk.dim(fileStats.name))
      logger.log('')
      return true
    }

    var filePercent = 0
    if (fileStats.stats.bytesTotal > 0) {
      filePercent = Math.floor(
        100 * (fileStats.stats.bytesRead / fileStats.stats.bytesTotal)
      )
    }
    // = to overwrite fake 100% msg
    if (filePercent > 0) {
      msg = chalk.bold.gray(indent + '[' + ('   ' + filePercent).slice(-3) + '%] ')
    } else {
      msg = chalk.bold.gray(indent + '       ')
    }
    msg += chalk.blue(fileStats.name)
  }

  var totalPer = Math.floor(100 * (statsAdd.totalStats.bytesRead / statsScan.size))
  msg += '\n'
  msg += chalk.bold.red('[' + ('  ' + totalPer).slice(-3) + '% ] ')
  msg += chalk.magenta(
    'Adding Files to Dat: ' +
    statsAdd.totalStats.filesRead + ' of ' + statsScan.files +
    chalk.dim(
      ' (' + prettyBytes(statsAdd.totalStats.bytesRead) +
      ' of ' + prettyBytes(statsScan.size) + ')'
    )
  )
  logger.stdout(msg)
}

function printSwarmStatus (stats) {
  var swarm = stats.swarm
  if (!swarm) return logger.stdout('Finding data sources...\n')
  var totalCount = swarm.blocks
  var downloadCount = stats.files + stats.directories
  var activeCount = swarm.peersConnected

  var msg = ''
  var count = '0'
  if (activeCount > 0) count = activeCount + '/' + (swarm.peersConnecting)
  if ((swarm.downloading || swarm.downloadComplete) && stats.downloaded > 0) {
    msg += 'Downloaded ' + downloadCount + '/' + totalCount + ' files' +
      ' (' + prettyBytes(stats.downloadRate()) + '/s, ' + prettyBytes(stats.downloaded) + ' total)\n'
  }

  if (swarm.downloadComplete) msg += 'Download complete, sharing data. Connected to ' + count + ' sources\n'
  else if (swarm.downloading) msg += 'Connected to ' + count + ' sources\n'
  else msg += chalk.green('Sharing data on port ' + chalk.bold(swarm.address().port) + ', connected to ' + chalk.bold(count) + ' sources\n')
  logger.stdout(msg)
}

function getLogger () {
  if (args.quiet) {
    return {
      stderr: noop,
      stdout: noop,
      log: noop,
      error: noop
    }
  }

  if (args.debug) {
    return {
      stderr: console.error.bind(console),
      stdout: console.log.bind(console),
      log: console.error.bind(console),
      error: console.log.bind(console)
    }
  }

  return {
    stderr: singleLineLog.stderr,
    stdout: singleLineLog.stdout,
    log: console.log.bind(console),
    error: console.error.bind(console)
  }
}
<commit_msg>fix bug, make total dat stats show on dark terminals<commit_after>#!/usr/bin/env node
var args = require('minimist')(process.argv.splice(2), {
  alias: {p: 'port', q: 'quiet', v: 'version'},
  boolean: ['color'],
  default: {color: true}
})

process.title = 'dat'

// set debug before requiring other modules
if (args.debug) {
  var debug = args.debug
  if (typeof args.debug === 'boolean') debug = '*' // default
  process.env.DEBUG = debug
}

if (args.version) {
  var pkg = require(__dirname + '/package.json')
  console.log(pkg.version)
  process.exit(0)
}

var fs = require('fs')
var singleLineLog = require('single-line-log')
var prettyBytes = require('pretty-bytes')
var chalk = require('chalk')
var dat = require('./index.js')
var usage = require('./usage')

function noop () {}

var cmd = args._[0]
var logger = getLogger()

var LOG_INTERVAL = (args.logspeed ? +args.logspeed : 200)
if (isNaN(LOG_INTERVAL)) LOG_INTERVAL = 200
if (!args.color) chalk = new chalk.constructor({enabled: false})

checkLocation()

function checkLocation () {
  var loc = process.cwd()
  if (!args.path) return runCommand(loc)
  loc = args.path
  fs.exists(loc, function (exists) {
    if (!exists) {
      logger.error('Does not exist:', loc)
      return usage('root.txt')
    }
    runCommand(loc)
  })
}

function runCommand (loc) {
  if (!cmd) return usage('root.txt')

  var db = dat({home: args.home})

  if (cmd === 'link') link(loc, db)
  else if (cmd === 'list') list(loc, db)
  else if (cmd) download(loc, db)
}

function link (loc, db) {
  var dirs = args._.slice(1)
  if (dirs.length === 0) dirs = loc

  var statsScan = db.fileStats(dirs, function (err, stats) {
    if (err) throw err
    printScanProgress(stats, true)
    clearInterval(scanInterval)
    logger.log('') // newline
    var statsAdd = db.addFiles(dirs, function (err, link) {
      printAddProgress(statsAdd, statsScan)
      clearInterval(addInterval)
      if (err) throw err
      db.joinTcpSwarm({link: link, port: args.port}, function (_err, swarm) {
        // ignore _err
        logger.stdout('') // clear progress lines
        logger.log(
          chalk.green.bold('Your Dat Link: ') +
          chalk.underline.blue('dat://' + swarm.link)
        )
        startProgressLogging({swarm: swarm})
      })
    })

    var addInterval = setInterval(function () {
      printAddProgress(statsAdd, statsScan)
    }, LOG_INTERVAL)
  })

  var scanInterval = setInterval(function () {
    printScanProgress(statsScan)
  }, LOG_INTERVAL)
}

function list (loc, db) {
  var hash = args._[1]
  db.joinTcpSwarm({link: hash, port: args.port}, function (err, swarm) {
    if (err) throw err
    var archive = db.drive.get(swarm.link, loc)
    archive.ready(function (err) {
      if (err) throw err
      archive.createEntryStream().on('data', function (o) {
        logger.log(o)
      })
    })
  })
}

function download (loc, db) {
  // download/share
  var hash = args._[0]
  if (!hash) return usage('root.txt')
  hash = hash.trim().replace('dat://', '').replace('dat:', '')
  if (hash.length !== 64) {
    logger.error('Error: Invalid dat link\n')
    return usage('root.txt')
  }
  var downloadStats = db.download(hash, loc, function (err, swarm) {
    if (err) throw err
    swarm.downloading = false
    swarm.downloadComplete = true
  })
  startProgressLogging(downloadStats)
}

function startProgressLogging (stats) {
  setInterval(function () {
    printSwarmStatus(stats)
  }, LOG_INTERVAL)
  printSwarmStatus(stats)

  process.on('SIGINT', function () {
    process.exit(1)
    setTimeout(function () {
      // sigterm if it is still running
      process.kill(process.pid)
    }, 2000)
  })
}

function printScanProgress (stats, last) {
  var dirCount = stats.directories + 1 // parent folder
  var msg = chalk.bold.blue('Calculating Size: ')
  if (last) msg = chalk.bold.green('Creating Dat Link ')
  msg += chalk.bold(
    '(' + stats.files + ' files, ' + dirCount + ' folders, ' +
    (stats.size ? prettyBytes(stats.size) + ' total' : '') + ')'
  )
  logger.stdout(msg)
}

function printAddProgress (statsAdd, statsScan) {
  var msg = ''
  var indent = '  ' // indent for single file info

  while (true) {
    if (statsAdd.files.length === 0) break
    var complete = printFileProgress(statsAdd.files[0])
    if (complete) statsAdd.files.shift()
    else break
  }

  function printFileProgress (fileStats) {
    var complete = (fileStats.stats.bytesTotal === fileStats.stats.bytesRead)
    if (complete) {
      if (msg === '') {
        // hack to avoid flashing no progress
        msg += chalk.bold.gray(indent + '[' + 100 + '%] ')
        msg += chalk.blue(fileStats.name)
      }
      logger.stdout(chalk.green.dim(indent + '[Done] ') + chalk.dim(fileStats.name))
      logger.log('')
      return true
    }

    var filePercent = 0
    if (fileStats.stats.bytesTotal > 0) {
      filePercent = Math.floor(
        100 * (fileStats.stats.bytesRead / fileStats.stats.bytesTotal)
      )
    }
    // = to overwrite fake 100% msg
    if (filePercent > 0) {
      msg = chalk.bold.gray(indent + '[' + ('   ' + filePercent).slice(-3) + '%] ')
    } else {
      msg = chalk.bold.gray(indent + '       ')
    }
    msg += chalk.blue(fileStats.name)
  }

  var totalPer = Math.floor(100 * (statsAdd.totalStats.bytesRead / statsScan.size))
  msg += '\n'
  msg += chalk.bold.red('[' + ('  ' + totalPer).slice(-3) + '% ] ')
  msg += chalk.magenta(
    'Adding Files to Dat: ' +
    statsAdd.totalStats.filesRead + ' of ' + statsScan.files +
    chalk.dim(
      ' (' + prettyBytes(statsAdd.totalStats.bytesRead) +
      ' of ' + prettyBytes(statsScan.size) + ')'
    )
  )
  logger.stdout(msg)
}

function printSwarmStatus (stats) {
  var swarm = stats.swarm
  if (!swarm) return logger.stdout('Finding data sources...\n')
  var totalCount = swarm.blocks
  var downloadCount = stats.files + stats.directories
  var activeCount = swarm.peersConnected

  var msg = ''
  var count = '0'
  if (activeCount > 0) count = activeCount + '/' + (swarm.peersConnecting)
  if ((swarm.downloading || swarm.downloadComplete) && stats.downloaded > 0) {
    msg += 'Downloaded ' + downloadCount + '/' + totalCount + ' files' +
      ' (' + prettyBytes(stats.downloadRate()) + '/s, ' + prettyBytes(stats.downloaded) + ' total)\n'
  }

  if (swarm.downloadComplete) msg += 'Download complete, sharing data. Connected to ' + count + ' sources\n'
  else if (swarm.downloading) msg += 'Connected to ' + count + ' sources\n'
  else msg += chalk.green('Sharing data on port ' + chalk.bold(swarm.address().port) + ', connected to ' + chalk.bold(count) + ' sources\n')
  logger.stdout(msg)
}

function getLogger () {
  if (args.quiet) {
    return {
      stderr: noop,
      stdout: noop,
      log: noop,
      error: noop
    }
  }

  if (args.debug) {
    return {
      stderr: console.error.bind(console),
      stdout: console.log.bind(console),
      log: console.error.bind(console),
      error: console.log.bind(console)
    }
  }

  return {
    stderr: singleLineLog.stderr,
    stdout: singleLineLog.stdout,
    log: console.log.bind(console),
    error: console.error.bind(console)
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var retrieveKeyframes = require('./')

var file = process.argv[2]
var fn = file.match('.mp4$') ? retrieveKeyframes.getForMp4 : (file.match('.mkv$') ? retrieveKeyframes.getForMkv : null)

if (! fn) {
	console.error('pass a mkv or mp4 file')
	process.exit(1)
}

fn(file, function(err, res) {
	if (err) return console.error(err)

	res.forEach(function(frame) { console.log(frame) })
})
<commit_msg>cli: now can log frame seconds<commit_after>#!/usr/bin/env node

var retrieveKeyframes = require('./')

var file = process.argv[2]
var fn = file.match('.mp4$') ? retrieveKeyframes.getForMp4 : (file.match('.mkv$') ? retrieveKeyframes.getForMkv : null)

var isSeconds = process.argv.some(function(arg) { return arg === '--seconds' })

if (! fn) {
	console.error('pass a mkv or mp4 file')
	process.exit(1)
}

fn(file, function(err, res) {
	if (err) return console.error(err)

	if (isSeconds) res.forEach(function(frame) { console.log((frame.timestamp / 1000).toFixed(6)) })
	else res.forEach(function(frame) { console.log(frame) })
})
<|endoftext|>


<commit_before>#!/usr/bin/env node
const path = require('path')

cmd()

function cmd () {
  var argv = process.argv.slice(2)

  if (~argv.indexOf('--cmd') || ~argv.indexOf('-c')) {
    return require('./command')(argv)
  }

  if (!argv.length || ~argv.indexOf('-h') || ~argv.indexOf('--help')) {
    process.stdout.write('\n')
    banner()
    return require('fs')
      .createReadStream(__dirname + '/usage.txt')
      .pipe(process.stdout)
  }

  if (~argv.indexOf('-v') || ~argv.indexOf('--version')) {
    return banner()
  }

  var stacksOnlyIx = argv.indexOf('--stacks-only')
  if (argv[stacksOnlyIx + 1] === '-') {
    argv[stacksOnlyIx] = '--stacks-only=-'
    argv.splice(stacksOnlyIx + 1, 1)
  }

  var args = require('minimist')(argv, {
    number: ['delay'],
    boolean: ['open'],
    alias: {
      open: 'o',
      delay: 'd',
      'output-dir': 'D'
    },
    default: {
      node: false,
      delay: 300
    }
  })

  args.script = args._[0]

  require('./')(args, args.node)
}

function banner() {
  var version = require('./package.json').version
  process.stdout.write('0x ' + version + '\n')
}
<commit_msg>More tidy up to cmd.js<commit_after>#!/usr/bin/env node
const path = require('path')

cmd()

function cmd () {
  var argv = process.argv.slice(2)

  var args = require('minimist')(argv, {
    number: ['delay'],
    boolean: ['open', 'version', 'help', 'cmd'],
    alias: {
      open: 'o',
      delay: 'd',
      'output-dir': 'D',
      version: 'v',
      help: 'h',
      cmd: 'c'
    },
    default: {
      node: false,
      delay: 300
    }
  })

  if (args.version) {
    return banner()
  }

  if (args.help) {
    process.stdout.write('\n')
    banner()
    return require('fs')
      .createReadStream(__dirname + '/usage.txt')
      .pipe(process.stdout)
  }

  if (args.cmd) {
    return require('./command')(argv)
  }

  args.script = args._[0]

  require('./')(args, args.node)
}

function banner() {
  var version = require('./package.json').version
  process.stdout.write('0x ' + version + '\n')
}
<|endoftext|>