<commit_before>/* global process, __dirname */

/**
 * Module dependencies
 */

var path = require('path')
var mkdirp = require('mkdirp')
var crypto = require('crypto')
var chalk = require('chalk')
var _s = require('underscore.string')

/**
 * DEPRECATED
 *
 * Initialize Directory
 *
 * If the directory exists and is not empty, this will prompt
 * to continue. If the directory does not exist, it will be
 * created and the process cwd changed.
 */

/*
function directory (context) {
  return function (done) {
    var log = context.cli.log,
      dir = path.join(process.cwd(), context.dir)

    // if it's an existing directory
    if (fs.existsSync(dir)) {
      // and it's not empty
      if (fs.readdirSync(dir).length > 0) {
        // prompt to confirm
        inquirer.prompt([
          {
            type: 'confirm',
            name: 'existing',
            message: 'This is not an empty directory. Are you sure you want to proceed?',
            default: false
          }
        ], function (answers) {
          if (answers.existing) {
            process.chdir(dir)
            log()
            done()
          } else {
            log()
            log.error('ABORTED')
            process.exit()
          }
        })
      }

      // otherwise continue
      else {
        done()
      }
    } else {
      mkdirp(dir, function (err) {
        if (err) { return done(err); }
        process.chdir(dir)
        done()
      })
    }
  }
}
*/

/**
 * Register command
 */

function registerInit (cli, options, done) {
  cli.command('init')
    .handler(function (data, flags, done) {
      /**
       * Command dependencies
       */

      var cwd = process.cwd()
      var request = require('request')
      var AnvilConnectKeys = require('anvil-connect-keys')
      var keygen = new AnvilConnectKeys(path.join(cwd, 'connect'))
      var yeoman = require('yeoman-environment')
      var generators = require('yeoman-generator')
      var env = yeoman.createEnv()
      var log = cli.log

      /**
       * File and template  paths
       */

      var templates = path.join(__dirname, '..', '..', 'templates')

      /**
       * Generator
       */

      var Generator = generators.Base.extend({
        constructor: function () {
          generators.Base.apply(this, arguments)

          // arguments
          // this.argument('appname', { type: String, required: true })

          // options

        // destination
        // this.destinationRoot(?)
        }
      })

      /**
       * Get Latest Anvil Connect Version
       */

      Generator.prototype.packageVersion = function () {
        var callback = this.async()

        request({
          url: 'https://registry.npmjs.org/anvil-connect',
          json: true
        }, function (err, res, body) {
          if (err) { return callback(err) }
          this.version = body['dist-tags'].latest
          callback()
        }.bind(this))
      }

      /**
       * Ask for instance name
       */

      Generator.prototype.askForAppName = function () {
        var callback = this.async()

        var appname = path.basename(process.cwd())

        this.prompt([{
          type: 'input',
          name: 'appname',
          message: 'What would you like to name your Connect instance?',
          default: _s.slugify(_s.humanize(appname))
        }], function (answers) {
          this.appname = _s.slugify(_s.humanize(answers.appname))
          callback()
        }.bind(this))
      }

      /**
       * Ask for issuer
       */

      Generator.prototype.askForIssuer = function () {
        var callback = this.async()

        this.prompt([{
          type: 'input',
          name: 'issuer',
          message: 'What (sub)domain will you use?',
          default: 'connect.example.com'
        }], function (answers) {
          this.issuer = answers.issuer
          callback()
        }.bind(this))
      }

      /**
       * Ask for Docker
       */

      Generator.prototype.askForDocker = function () {
        var callback = this.async()

        this.prompt([{
          type: 'confirm',
          name: 'docker',
          message: 'Would you like to use Docker?',
          default: true
        }], function (answers) {
          this.docker = answers.docker
          callback()
        }.bind(this))
      }

      /**
       * Ask for Redis
       */

      Generator.prototype.askForRedis = function () {
        var callback = this.async()

        this.prompt([{
          type: 'confirm',
          name: 'redis',
          message: 'Would you like to run Redis?',
          default: true
        }], function (answers) {
          this.redis = answers.redis
          callback()
        }.bind(this))
      }

      /**
       * Ask for nginx
       */

      Generator.prototype.askForNginx = function () {
        var callback = this.async()

        this.prompt([{
          type: 'confirm',
          name: 'nginx',
          message: 'Would you like to run nginx?',
          default: true
        }], function (answers) {
          this.nginx = answers.nginx
          callback()
        }.bind(this))
      }

      /**
       * Ask for SSL
       */

      Generator.prototype.askForSSL = function () {
        if (this.nginx) {
          var callback = this.async()

          this.prompt([{
            type: 'confirm',
            name: 'ssl',
            message: 'Would you like to create a self-signed SSL cert?',
            default: true
          }], function (answers) {
            this.ssl = answers.ssl
            callback()
          }.bind(this))
        }
      }

      /**
       * Ask for SSL certificate params
       */

      Generator.prototype.askForSSLParams = function () {
        if (this.ssl) {
          var callback = this.async()

          this.prompt([

            // Country Name (2 letter code) [AU]:
            {
              type: 'input',
              name: 'C',
              message: 'Country Name (2 letter code)',
              default: 'US'
            },

            // State or Province Name (full name) [Some-State]:
            {
              type: 'input',
              name: 'ST',
              message: 'State or Province Name (full name)',
              default: 'South Dakota'
            },

            // Locality Name (eg, city) []:
            {
              type: 'input',
              name: 'L',
              message: 'Locality Name (eg, city)',
              default: 'Rapid City'
            },

            // Organization Name (eg, company) [Internet Widgits Pty Ltd]:
            {
              type: 'input',
              name: 'O',
              message: 'Organization Name (eg, company)',
              default: 'Anvil Research, Inc.'
            }

            // Common Name (e.g. server FQDN or YOUR name)
            // Use this.issuer

          ], function (answers) {
            this.C = answers.C
            this.ST = answers.ST
            this.L = answers.L
            this.O = answers.O
            this.CN = this.issuer

            callback()
          }.bind(this))
        }
      }

      /**
       * Git init
       */

      Generator.prototype.gitInit = function () {
        this.spawnCommand('git', ['init'], { stdio: 'ignore' })
        this.log('   ' + chalk.green('create ') + 'git repository')
      }

      /**
       * .gitignore
       */

      Generator.prototype.gitignore = function () {
        this.copy(
          path.join(templates, '_gitignore'),
          this.destinationPath('.gitignore')
        )
      }

      /**
       * README
       */

      Generator.prototype.readme = function () {
        if (this.docker) {
          this.template(
            path.join(templates, 'README.md'),
            this.destinationPath('README.md')
          )
        }
      }
      /**
       * Docker Compose
       */

      Generator.prototype.dc = function () {
        if (this.docker) {
          this.template(
            path.join(templates, 'docker-compose.yml'),
            this.destinationPath('docker-compose.yml')
          )
        }
      }

      /**
       * Connect
       */

      Generator.prototype.connect = function () {
        // server.js
        this.template(
          path.join(templates, 'connect', 'server.js'),
          this.destinationPath(path.join('connect', 'server.js'))
        )

        // development config file
        this.cookie_secret = crypto.randomBytes(10).toString('hex')
        this.session_secret = crypto.randomBytes(10).toString('hex')

        this.template(
          path.join(templates, 'connect', 'config', 'development.json'),
          this.destinationPath(path.join('connect', 'config', 'development.json'))
        )

        // production config file
        this.cookie_secret = crypto.randomBytes(10).toString('hex')
        this.session_secret = crypto.randomBytes(10).toString('hex')

        this.template(
          path.join(templates, 'connect', 'config', 'production.json'),
          this.destinationPath(path.join('connect', 'config', 'production.json'))
        )

        // package.json
        this.template(
          path.join(templates, 'connect', 'package.json'),
          this.destinationPath(path.join('connect', 'package.json'))
        )

        // .bowerrc
        this.template(
          path.join(templates, 'connect', '_bowerrc'),
          this.destinationPath(path.join('connect', '.bowerrc'))
        )

        // bower.json
        this.template(
          path.join(templates, 'connect', 'bower.json'),
          this.destinationPath(path.join('connect', 'bower.json'))
        )

        // Make docker volume mapped folders
        mkdirp.sync(this.destinationPath(path.join('connect', 'email')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'protocols')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'providers')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'public')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'views')))

        // Dockerfile
        if (this.docker) {
          this.template(
            path.join(templates, 'connect', 'Dockerfile'),
            this.destinationPath(path.join('connect', 'Dockerfile'))
          )
        }
      }

      /**
       * nginx
       */

      Generator.prototype.nginx = function () {
        if (this.nginx) {
          // main conf
          this.template(
            path.join(templates, 'nginx', 'nginx.conf'),
            this.destinationPath(path.join('nginx', 'nginx.conf'))
          )

          // virtual host
          this.template(
            path.join(templates, 'nginx', 'conf.d', 'default.conf'),
            this.destinationPath(path.join('nginx', 'conf.d', 'default.conf'))
          )

          // upstream entries
          this.template(
            path.join(templates, 'nginx', 'conf.d', 'upstream.conf'),
            this.destinationPath(path.join('nginx', 'conf.d', 'upstream.conf'))
          )

          // Make docker volume mapped folders
          mkdirp.sync(this.destinationPath(path.join('nginx', 'logs')))

          // Dockerfile
          if (this.docker) {
            this.template(
              path.join(templates, 'nginx', 'Dockerfile'),
              this.destinationPath(path.join('nginx', 'Dockerfile'))
            )
          }
        }
      }

      /**
       * Redis
       */

      Generator.prototype.redis = function () {
        if (this.redis) {
          // redis config
          this.template(
            path.join(templates, 'redis', 'etc', 'redis.conf'),
            this.destinationPath('redis', 'etc', 'redis.conf')
          )

          // Make docker volume mapped folders
          mkdirp.sync(this.destinationPath(path.join('redis', 'data')))
          mkdirp.sync(this.destinationPath(path.join('redis', 'logs')))

          // Dockerfile
          if (this.docker) {
            this.template(
              path.join(templates, 'redis', 'Dockerfile'),
              this.destinationPath(path.join('redis', 'Dockerfile'))

            )
          }
        }
      }

      /**
       * Generate key pairs
       */

      Generator.prototype.generateKeyPairs = function () {
        keygen.generateKeyPairs()
        this.log('   ' + chalk.green('create ') + 'connect/keys/sig.rsa.pub.pem')
        this.log('   ' + chalk.green('create ') + 'connect/keys/sig.rsa.prv.pem')
        this.log('   ' + chalk.green('create ') + 'connect/keys/enc.rsa.pub.pem')
        this.log('   ' + chalk.green('create ') + 'connect/keys/enc.rsa.prv.pem')
      }

      /**
       * Generate setup token
       */

      Generator.prototype.generateSetupToken = function () {
        keygen.generateSetupToken()
        this.log('   ' + chalk.green('create ') + 'connect/keys/setup.token')
      }

      /**
       * Self-signed SSL Cert
       */

      Generator.prototype.selfSignedCert = function () {
        var self = this

        if (this.ssl) {
          var callback = this.async()

          var subj = ''
          subj += '/C=' + this.C
          subj += '/ST=' + this.ST
          subj += '/L=' + this.L
          subj += '/O=' + this.O
          subj += '/CN=' + this.CN

          mkdirp.sync(this.destinationPath('nginx/certs'))
          var cert = this.spawnCommand('openssl', [
            'req',
            '-new',
            '-newkey',
            'rsa:4096',
            '-days',
            '365',
            '-nodes',
            '-x509',
            '-subj',
            subj,
            '-keyout',
            this.destinationPath(path.join('nginx', 'certs', 'nginx.key')),
            '-out',
            this.destinationPath(path.join('nginx', 'certs', 'nginx.crt'))
          ], {
            stdio: 'ignore'
          })

          cert.on('exit', function () {
            self.log('   ' + chalk.green('create ') + 'nginx/certs/nginx.key')
            self.log('   ' + chalk.green('create ') + 'nginx/certs/nginx.crt')
            callback()
          })
        }
      }

      /**
       * Run
       */

      env.registerStub(Generator, 'connect:docker')
      env.run('connect:docker', function () {
        log('Connect all the things :)')
        done()
      })
    })
  done()
}

/**
 * Exports
 */

module.exports = registerInit
<commit_msg>style: Remove trailing space in lib/commands/init.js<commit_after>/* global process, __dirname */

/**
 * Module dependencies
 */

var path = require('path')
var mkdirp = require('mkdirp')
var crypto = require('crypto')
var chalk = require('chalk')
var _s = require('underscore.string')

/**
 * DEPRECATED
 *
 * Initialize Directory
 *
 * If the directory exists and is not empty, this will prompt
 * to continue. If the directory does not exist, it will be
 * created and the process cwd changed.
 */

/*
function directory (context) {
  return function (done) {
    var log = context.cli.log,
      dir = path.join(process.cwd(), context.dir)

    // if it's an existing directory
    if (fs.existsSync(dir)) {
      // and it's not empty
      if (fs.readdirSync(dir).length > 0) {
        // prompt to confirm
        inquirer.prompt([
          {
            type: 'confirm',
            name: 'existing',
            message: 'This is not an empty directory. Are you sure you want to proceed?',
            default: false
          }
        ], function (answers) {
          if (answers.existing) {
            process.chdir(dir)
            log()
            done()
          } else {
            log()
            log.error('ABORTED')
            process.exit()
          }
        })
      }

      // otherwise continue
      else {
        done()
      }
    } else {
      mkdirp(dir, function (err) {
        if (err) { return done(err); }
        process.chdir(dir)
        done()
      })
    }
  }
}
*/

/**
 * Register command
 */

function registerInit (cli, options, done) {
  cli.command('init')
    .handler(function (data, flags, done) {
      /**
       * Command dependencies
       */

      var cwd = process.cwd()
      var request = require('request')
      var AnvilConnectKeys = require('anvil-connect-keys')
      var keygen = new AnvilConnectKeys(path.join(cwd, 'connect'))
      var yeoman = require('yeoman-environment')
      var generators = require('yeoman-generator')
      var env = yeoman.createEnv()
      var log = cli.log

      /**
       * File and template  paths
       */

      var templates = path.join(__dirname, '..', '..', 'templates')

      /**
       * Generator
       */

      var Generator = generators.Base.extend({
        constructor: function () {
          generators.Base.apply(this, arguments)

          // arguments
          // this.argument('appname', { type: String, required: true })

          // options

        // destination
        // this.destinationRoot(?)
        }
      })

      /**
       * Get Latest Anvil Connect Version
       */

      Generator.prototype.packageVersion = function () {
        var callback = this.async()

        request({
          url: 'https://registry.npmjs.org/anvil-connect',
          json: true
        }, function (err, res, body) {
          if (err) { return callback(err) }
          this.version = body['dist-tags'].latest
          callback()
        }.bind(this))
      }

      /**
       * Ask for instance name
       */

      Generator.prototype.askForAppName = function () {
        var callback = this.async()

        var appname = path.basename(process.cwd())

        this.prompt([{
          type: 'input',
          name: 'appname',
          message: 'What would you like to name your Connect instance?',
          default: _s.slugify(_s.humanize(appname))
        }], function (answers) {
          this.appname = _s.slugify(_s.humanize(answers.appname))
          callback()
        }.bind(this))
      }

      /**
       * Ask for issuer
       */

      Generator.prototype.askForIssuer = function () {
        var callback = this.async()

        this.prompt([{
          type: 'input',
          name: 'issuer',
          message: 'What (sub)domain will you use?',
          default: 'connect.example.com'
        }], function (answers) {
          this.issuer = answers.issuer
          callback()
        }.bind(this))
      }

      /**
       * Ask for Docker
       */

      Generator.prototype.askForDocker = function () {
        var callback = this.async()

        this.prompt([{
          type: 'confirm',
          name: 'docker',
          message: 'Would you like to use Docker?',
          default: true
        }], function (answers) {
          this.docker = answers.docker
          callback()
        }.bind(this))
      }

      /**
       * Ask for Redis
       */

      Generator.prototype.askForRedis = function () {
        var callback = this.async()

        this.prompt([{
          type: 'confirm',
          name: 'redis',
          message: 'Would you like to run Redis?',
          default: true
        }], function (answers) {
          this.redis = answers.redis
          callback()
        }.bind(this))
      }

      /**
       * Ask for nginx
       */

      Generator.prototype.askForNginx = function () {
        var callback = this.async()

        this.prompt([{
          type: 'confirm',
          name: 'nginx',
          message: 'Would you like to run nginx?',
          default: true
        }], function (answers) {
          this.nginx = answers.nginx
          callback()
        }.bind(this))
      }

      /**
       * Ask for SSL
       */

      Generator.prototype.askForSSL = function () {
        if (this.nginx) {
          var callback = this.async()

          this.prompt([{
            type: 'confirm',
            name: 'ssl',
            message: 'Would you like to create a self-signed SSL cert?',
            default: true
          }], function (answers) {
            this.ssl = answers.ssl
            callback()
          }.bind(this))
        }
      }

      /**
       * Ask for SSL certificate params
       */

      Generator.prototype.askForSSLParams = function () {
        if (this.ssl) {
          var callback = this.async()

          this.prompt([

            // Country Name (2 letter code) [AU]:
            {
              type: 'input',
              name: 'C',
              message: 'Country Name (2 letter code)',
              default: 'US'
            },

            // State or Province Name (full name) [Some-State]:
            {
              type: 'input',
              name: 'ST',
              message: 'State or Province Name (full name)',
              default: 'South Dakota'
            },

            // Locality Name (eg, city) []:
            {
              type: 'input',
              name: 'L',
              message: 'Locality Name (eg, city)',
              default: 'Rapid City'
            },

            // Organization Name (eg, company) [Internet Widgits Pty Ltd]:
            {
              type: 'input',
              name: 'O',
              message: 'Organization Name (eg, company)',
              default: 'Anvil Research, Inc.'
            }

            // Common Name (e.g. server FQDN or YOUR name)
            // Use this.issuer

          ], function (answers) {
            this.C = answers.C
            this.ST = answers.ST
            this.L = answers.L
            this.O = answers.O
            this.CN = this.issuer

            callback()
          }.bind(this))
        }
      }

      /**
       * Git init
       */

      Generator.prototype.gitInit = function () {
        this.spawnCommand('git', ['init'], { stdio: 'ignore' })
        this.log('   ' + chalk.green('create ') + 'git repository')
      }

      /**
       * .gitignore
       */

      Generator.prototype.gitignore = function () {
        this.copy(
          path.join(templates, '_gitignore'),
          this.destinationPath('.gitignore')
        )
      }

      /**
       * README
       */

      Generator.prototype.readme = function () {
        if (this.docker) {
          this.template(
            path.join(templates, 'README.md'),
            this.destinationPath('README.md')
          )
        }
      }
      /**
       * Docker Compose
       */

      Generator.prototype.dc = function () {
        if (this.docker) {
          this.template(
            path.join(templates, 'docker-compose.yml'),
            this.destinationPath('docker-compose.yml')
          )
        }
      }

      /**
       * Connect
       */

      Generator.prototype.connect = function () {
        // server.js
        this.template(
          path.join(templates, 'connect', 'server.js'),
          this.destinationPath(path.join('connect', 'server.js'))
        )

        // development config file
        this.cookie_secret = crypto.randomBytes(10).toString('hex')
        this.session_secret = crypto.randomBytes(10).toString('hex')

        this.template(
          path.join(templates, 'connect', 'config', 'development.json'),
          this.destinationPath(path.join('connect', 'config', 'development.json'))
        )

        // production config file
        this.cookie_secret = crypto.randomBytes(10).toString('hex')
        this.session_secret = crypto.randomBytes(10).toString('hex')

        this.template(
          path.join(templates, 'connect', 'config', 'production.json'),
          this.destinationPath(path.join('connect', 'config', 'production.json'))
        )

        // package.json
        this.template(
          path.join(templates, 'connect', 'package.json'),
          this.destinationPath(path.join('connect', 'package.json'))
        )

        // .bowerrc
        this.template(
          path.join(templates, 'connect', '_bowerrc'),
          this.destinationPath(path.join('connect', '.bowerrc'))
        )

        // bower.json
        this.template(
          path.join(templates, 'connect', 'bower.json'),
          this.destinationPath(path.join('connect', 'bower.json'))
        )

        // Make docker volume mapped folders
        mkdirp.sync(this.destinationPath(path.join('connect', 'email')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'protocols')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'providers')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'public')))
        mkdirp.sync(this.destinationPath(path.join('connect', 'views')))

        // Dockerfile
        if (this.docker) {
          this.template(
            path.join(templates, 'connect', 'Dockerfile'),
            this.destinationPath(path.join('connect', 'Dockerfile'))
          )
        }
      }

      /**
       * nginx
       */

      Generator.prototype.nginx = function () {
        if (this.nginx) {
          // main conf
          this.template(
            path.join(templates, 'nginx', 'nginx.conf'),
            this.destinationPath(path.join('nginx', 'nginx.conf'))
          )

          // virtual host
          this.template(
            path.join(templates, 'nginx', 'conf.d', 'default.conf'),
            this.destinationPath(path.join('nginx', 'conf.d', 'default.conf'))
          )

          // upstream entries
          this.template(
            path.join(templates, 'nginx', 'conf.d', 'upstream.conf'),
            this.destinationPath(path.join('nginx', 'conf.d', 'upstream.conf'))
          )

          // Make docker volume mapped folders
          mkdirp.sync(this.destinationPath(path.join('nginx', 'logs')))

          // Dockerfile
          if (this.docker) {
            this.template(
              path.join(templates, 'nginx', 'Dockerfile'),
              this.destinationPath(path.join('nginx', 'Dockerfile'))
            )
          }
        }
      }

      /**
       * Redis
       */

      Generator.prototype.redis = function () {
        if (this.redis) {
          // redis config
          this.template(
            path.join(templates, 'redis', 'etc', 'redis.conf'),
            this.destinationPath('redis', 'etc', 'redis.conf')
          )

          // Make docker volume mapped folders
          mkdirp.sync(this.destinationPath(path.join('redis', 'data')))
          mkdirp.sync(this.destinationPath(path.join('redis', 'logs')))

          // Dockerfile
          if (this.docker) {
            this.template(
              path.join(templates, 'redis', 'Dockerfile'),
              this.destinationPath(path.join('redis', 'Dockerfile'))

            )
          }
        }
      }

      /**
       * Generate key pairs
       */

      Generator.prototype.generateKeyPairs = function () {
        keygen.generateKeyPairs()
        this.log('   ' + chalk.green('create ') + 'connect/keys/sig.rsa.pub.pem')
        this.log('   ' + chalk.green('create ') + 'connect/keys/sig.rsa.prv.pem')
        this.log('   ' + chalk.green('create ') + 'connect/keys/enc.rsa.pub.pem')
        this.log('   ' + chalk.green('create ') + 'connect/keys/enc.rsa.prv.pem')
      }

      /**
       * Generate setup token
       */

      Generator.prototype.generateSetupToken = function () {
        keygen.generateSetupToken()
        this.log('   ' + chalk.green('create ') + 'connect/keys/setup.token')
      }

      /**
       * Self-signed SSL Cert
       */

      Generator.prototype.selfSignedCert = function () {
        var self = this

        if (this.ssl) {
          var callback = this.async()

          var subj = ''
          subj += '/C=' + this.C
          subj += '/ST=' + this.ST
          subj += '/L=' + this.L
          subj += '/O=' + this.O
          subj += '/CN=' + this.CN

          mkdirp.sync(this.destinationPath('nginx/certs'))
          var cert = this.spawnCommand('openssl', [
            'req',
            '-new',
            '-newkey',
            'rsa:4096',
            '-days',
            '365',
            '-nodes',
            '-x509',
            '-subj',
            subj,
            '-keyout',
            this.destinationPath(path.join('nginx', 'certs', 'nginx.key')),
            '-out',
            this.destinationPath(path.join('nginx', 'certs', 'nginx.crt'))
          ], {
            stdio: 'ignore'
          })

          cert.on('exit', function () {
            self.log('   ' + chalk.green('create ') + 'nginx/certs/nginx.key')
            self.log('   ' + chalk.green('create ') + 'nginx/certs/nginx.crt')
            callback()
          })
        }
      }

      /**
       * Run
       */

      env.registerStub(Generator, 'connect:docker')
      env.run('connect:docker', function () {
        log('Connect all the things :)')
        done()
      })
    })
  done()
}

/**
 * Exports
 */

module.exports = registerInit
<|endoftext|>


<commit_before>var smith   = require("../blacksmith"),
    fs      = require('fs'),
    winston = require('winston'),
    cp      = require('ncp').ncp,
    prompt  = require("prompt");

module.exports = function () {

  var argv = Array.prototype.slice.call();

  // I can't pass this in as an extra argument through flatiron.cli ?
  //var sitename = "blog";

  winston.info("Executing command "+"init".yellow);

  smith.sites(function (err, sites) {

    if (err) {
      throw err;
    }

    Object.keys(sites).forEach(function (k) {
      try {
        sites[k] = {
          "description": JSON.parse(fs.readFileSync(sites[k]+'/package.json').toString()).description,
          "path": sites[k]
        }
      }
      catch (err) {
        winston.warn(err.message);
        sites[k] = {
          "path": sites[k],
          "description": '<No description found>'
        };
      }
    });

    prompt.start();

    var getType = function getType (cb) {
      winston.help('init'.yellow + ' can create any of these site types:');
      winston.help('');
      Object.keys(sites).forEach(function (site) {
        winston.help('    * '+site.yellow+ ': '+sites[site].description);
      });
      winston.help('');

      prompt.get([{
        name: 'type',
        message: 'Choose your website type'
      }], function (err, i) {
        if (err) {
          return cb(err);
        }

        if (sites.hasOwnProperty(i.type)) {
          winston.info('Selecting website type '+i.type.yellow+'...');
          cb(null, i.type);
        }
        else {
          winston.error('That\'s not a website type! Try again.');
          getType(cb);
        }
      });
    };

    getType(function (err, type) {
      if (err) {
        throw err;
      }

      prompt.get([{
          name: 'sitename',
          message: 'Name your website'
      }], function (err, i) {

        if (err) {
          throw err;
        }

        var sitename = i.sitename;

        winston.warn('blacksmith'.yellow+' is about to write a '+type.yellow+' site to '+ ('./'+sitename).yellow + '!');
        prompt.get([{
          name: 'okay',
          message: 'Is this okay? [y/N]'
        }], function (err, j) {
          if (err) {
            throw err;
          }

          if (j.okay && (j.okay[0] == 'y' || j.okay[0] == 'Y') ) {

            cp(sites[type].path, './'+sitename, function (err) {
              if (err) {
                throw err;
              }

              winston.info('* Created';+('`./'+sitename+'`').yellow+'.');
            });
          }

        });
      });
    });

  });
}
<commit_msg>[fix] typo in init.js<commit_after>var smith   = require("../blacksmith"),
    fs      = require('fs'),
    winston = require('winston'),
    cp      = require('ncp').ncp,
    prompt  = require("prompt");

module.exports = function () {

  var argv = Array.prototype.slice.call();

  // I can't pass this in as an extra argument through flatiron.cli ?
  //var sitename = "blog";

  winston.info("Executing command "+"init".yellow);

  smith.sites(function (err, sites) {

    if (err) {
      throw err;
    }

    Object.keys(sites).forEach(function (k) {
      try {
        sites[k] = {
          "description": JSON.parse(fs.readFileSync(sites[k]+'/package.json').toString()).description,
          "path": sites[k]
        }
      }
      catch (err) {
        winston.warn(err.message);
        sites[k] = {
          "path": sites[k],
          "description": '<No description found>'
        };
      }
    });

    prompt.start();

    var getType = function getType (cb) {
      winston.help('init'.yellow + ' can create any of these site types:');
      winston.help('');
      Object.keys(sites).forEach(function (site) {
        winston.help('    * '+site.yellow+ ': '+sites[site].description);
      });
      winston.help('');

      prompt.get([{
        name: 'type',
        message: 'Choose your website type'
      }], function (err, i) {
        if (err) {
          return cb(err);
        }

        if (sites.hasOwnProperty(i.type)) {
          winston.info('Selecting website type '+i.type.yellow+'...');
          cb(null, i.type);
        }
        else {
          winston.error('That\'s not a website type! Try again.');
          getType(cb);
        }
      });
    };

    getType(function (err, type) {
      if (err) {
        throw err;
      }

      prompt.get([{
          name: 'sitename',
          message: 'Name your website'
      }], function (err, i) {

        if (err) {
          throw err;
        }

        var sitename = i.sitename;

        winston.warn('blacksmith'.yellow+' is about to write a '+type.yellow+' site to '+ ('./'+sitename).yellow + '!');
        prompt.get([{
          name: 'okay',
          message: 'Is this okay? [y/N]'
        }], function (err, j) {
          if (err) {
            throw err;
          }

          if (j.okay && (j.okay[0] == 'y' || j.okay[0] == 'Y') ) {

            cp(sites[type].path, './'+sitename, function (err) {
              if (err) {
                throw err;
              }

              winston.info('* Created'+('`./'+sitename+'`').yellow+'.');
            });
          }

        });
      });
    });

  });
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Patrick Quist
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var Compile = require('../base-compiler'),
    logger = require('../logger').logger,
    utils = require('../utils'),
    fs = require("fs"),
    path = require("path");

function compileFPC(info, env) {
    var compiler = new Compile(info, env);
    compiler.supportsOptOutput = false;

    var originalExecBinary = compiler.execBinary;

    var symbolcache = {};
    var sortedsymbolcache = [];
    var fixedsymbols = {};
    var ignoredsymbols = [];

    fixedsymbols.OUTPUT_$$_init = 'unit_initialization';
    fixedsymbols.OUTPUT_$$_finalize = 'unit_finalization';
    fixedsymbols.OUTPUT_$$_init_implicit = 'unit_initialization_implicit';
    fixedsymbols.OUTPUT_$$_finalize_implicit ='unit_finalization_implicit';

    ignoredsymbols = [
        ".L", "VMT_$", "INIT_$", "INIT$_$", "FINALIZE$_$", "RTTI_$", "_$",
        "DEBUGSTART_$", "DEBUGEND_$", "DBG_$", "DBG2_$", "DBGREF_$"];

    var addDemangleToCache = function(text) {
        if (text.endsWith(':')) {
            if (shouldIgnoreSymbol(text)) {
                return;
            }

            for (var k in fixedsymbols) {
                if (text.startsWith(k)) {
                    text = text.replace(k, fixedsymbols[k]);
                    symbolcache[k] = fixedsymbols[k];
                    return;
                }
            }

            text = text.substr(0, text.length - 1);

            if (text.startsWith("U_$OUTPUT_$$_")) {
                var unmangledglobalvar = text.substr(13).toLowerCase();
                symbolcache[text] = unmangledglobalvar;
                return;
            }

            var idx, paramtype = "", signature = "", phase = 0;
            var unitname = "", classname = "", methodname = "", params = "", resulttype = "";

            idx = text.indexOf("$_$");
            if (idx != -1) {
                unitname = text.substr(0, idx - 1);
                classname = text.substr(idx + 3, text.indexOf("_$_", idx + 2) - idx - 3);
            }

            signature = "";
            idx = text.indexOf("_$$_");
            if (idx != -1) {
                if (unitname == "") unitname = text.substr(0, idx - 1);
                signature =  text.substr(idx + 3);
            }

            if (unitname == "") {
                idx = text.indexOf("OUTPUT_");
                if (idx != -1) {
                    unitname = "OUTPUT";

                    idx = text.indexOf("_$__");
                    if (idx != -1) {
                        classname = text.substr(7, idx - 7);
                        signature = text.substr(idx + 3);
                    } else {
                        signature = text.substr(6);
                    }
                }
            }

            if (signature != "") {
                for (idx = 1; idx < signature.length; idx++) {
                    if (signature[idx] == '$') {
                        if (phase == 0) phase = 1;
                        else if (phase == 1) {
                            if (paramtype == "") phase = 2;
                            else if (params != "") {
                                params = params + "," + paramtype;
                                paramtype = "";
                            } else if (params == "") {
                                params = paramtype;
                                paramtype = "";
                            }
                        }
                    } else {
                        if (phase == 0) methodname = methodname + signature[idx];
                        else if (phase == 1) paramtype = paramtype + signature[idx];
                        else if (phase == 2) resulttype = resulttype + signature[idx];
                    }
                }

                if (paramtype != "") {
                    if (params != "") params = params + "," + paramtype;
                    else params = paramtype;
                }
            }

            symbolcache[text] = composeReadableMethodSignature(unitname, classname, methodname, params);
        }
    };

    var composeReadableMethodSignature = function (unitname, classname, methodname, params) {
        var signature = "";

        if (classname != "") signature = classname.toLowerCase() + ".";

        signature = signature + methodname.toLowerCase();
        signature = signature + "(" + params.toLowerCase() + ")";

        return signature;
    };

    var shouldIgnoreSymbol = function(text) {
        for (var k in ignoredsymbols) {
            if (text.startsWith(ignoredsymbols[k])) {
                return true;
            }
        }

        return false;
    };

    var demangleIfNeeded = function(text) {
        if (text.includes('$')) {
            if (shouldIgnoreSymbol(text)) {
                return text;
            }

            for (var idx in sortedsymbolcache) {
                text = text.replace(sortedsymbolcache[idx][0], sortedsymbolcache[idx][1]);
            }

            return text;
        } else {
            return text;
        }
    };

    var buildOrderedCache = function () {
        sortedsymbolcache = [];
        for (var symbol in symbolcache) {
           sortedsymbolcache.push([symbol, symbolcache[symbol]]);
        }

        sortedsymbolcache = sortedsymbolcache.sort(function(a, b) {
            return b[0].length - a[0].length;
        });

        symbolcache = {};
    };

    compiler.postProcessAsm = function (result) {
        if (!result.okToCache) return result;

        for (var i = 0; i < result.asm.length; ++i) {
            addDemangleToCache(result.asm[i].text);
        }

        buildOrderedCache();

        for (var j = 0; j < result.asm.length; ++j)
            result.asm[j].text = demangleIfNeeded(result.asm[j].text);

        return result;
    };

    compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
        return ['@/opt/compiler-explorer/fpc/fpc.cfg', '-a', '-g'];
    };

    var saveDummyProjectFile = function (filename) {
        fs.writeFile(filename,
            "program prog; " +
            "uses output in 'output.pas'; " +
            "begin " +
            "end.");
    };

    compiler.objdump = function (outputFilename, result, maxSize, intelAsm, demangle) {
         outputFilename = path.join(path.dirname(outputFilename), "prog");

         var args = ["-d", outputFilename, "-l", "--insn-width=16"];
         if (demangle) args = args.concat(["-C"]);
         if (intelAsm) args = args.concat(["-M", "intel"]);
         return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize})
            .then(function (objResult) {
                result.asm = objResult.stdout;
                var relevantAsmStartsAt = result.asm.indexOf("<OUTPUT");
                if (relevantAsmStartsAt != -1) {
                    var lastLinefeedBeforeStart = result.asm.lastIndexOf("\n", relevantAsmStartsAt);
                    if (lastLinefeedBeforeStart != -1) {
                        result.asm =
                            result.asm.substr(0, result.asm.indexOf("00000000004")) + "\n" +
                            result.asm.substr(lastLinefeedBeforeStart + 1);
                    } else {
                        result.asm =
                            result.asm.substr(0, result.asm.indexOf("00000000004")) + "\n" +
                            result.asm.substr(relevantAsmStartsAt);
                    }
                }

                if (objResult.code !== 0) {
                    result.asm = "<No output: objdump returned " + objResult.code + ">";
                }
                return result;
            });
    };

    compiler.runCompiler = function (compiler, options, inputFilename, execOptions) {
        if (!execOptions) {
            execOptions = this.getDefaultExecOptions();
        }

        var projectFile = path.join(path.dirname(inputFilename), "prog.dpr");

        saveDummyProjectFile(projectFile);

        options.pop();
        options.push('-B');
        options.push(projectFile);

        return this.exec(compiler, options, execOptions).then(function (result) {
            result.inputFilename = inputFilename;
            result.stdout = utils.parseOutput(result.stdout, inputFilename);
            result.stderr = utils.parseOutput(result.stderr, inputFilename);
            return result;
        });
    };

    compiler.execBinary = function (executable, result, maxSize) {
        executable = path.join(path.dirname(executable), "prog");

        originalExecBinary(executable, result, maxSize);
    };

    return compiler.initialise();
}

module.exports = compileFPC;
<commit_msg>added objdump preprocessor to remove non-user code, added preprocesser for asm to add .file, .loc and .cfi_endproc tags<commit_after>// Copyright (c) 2017, Patrick Quist
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var Compile = require('../base-compiler'),
    logger = require('../logger').logger,
    utils = require('../utils'),
    fs = require("fs"),
    path = require("path");

function compileFPC(info, env) {
    var compiler = new Compile(info, env);
    compiler.supportsOptOutput = false;

    var originalExecBinary = compiler.execBinary;

    var symbolcache = {};
    var sortedsymbolcache = [];
    var fixedsymbols = {};
    var ignoredsymbols = [];

    fixedsymbols.OUTPUT_$$_init = 'unit_initialization';
    fixedsymbols.OUTPUT_$$_finalize = 'unit_finalization';
    fixedsymbols.OUTPUT_$$_init_implicit = 'unit_initialization_implicit';
    fixedsymbols.OUTPUT_$$_finalize_implicit ='unit_finalization_implicit';

    ignoredsymbols = [
        ".L", "VMT_$", "INIT_$", "INIT$_$", "FINALIZE$_$", "RTTI_$", "_$",
        "DEBUGSTART_$", "DEBUGEND_$", "DBG_$", "DBG2_$", "DBGREF_$"];

    var addDemangleToCache = function(text) {
        if (text.endsWith(':')) {
            if (shouldIgnoreSymbol(text)) {
                return;
            }

            for (var k in fixedsymbols) {
                if (text.startsWith(k)) {
                    text = text.replace(k, fixedsymbols[k]);
                    symbolcache[k] = fixedsymbols[k];
                    return;
                }
            }

            text = text.substr(0, text.length - 1);

            if (text.startsWith("U_$OUTPUT_$$_")) {
                var unmangledglobalvar = text.substr(13).toLowerCase();
                symbolcache[text] = unmangledglobalvar;
                return;
            }

            var idx, paramtype = "", signature = "", phase = 0;
            var unitname = "", classname = "", methodname = "", params = "", resulttype = "";

            idx = text.indexOf("$_$");
            if (idx != -1) {
                unitname = text.substr(0, idx - 1);
                classname = text.substr(idx + 3, text.indexOf("_$_", idx + 2) - idx - 3);
            }

            signature = "";
            idx = text.indexOf("_$$_");
            if (idx != -1) {
                if (unitname == "") unitname = text.substr(0, idx - 1);
                signature =  text.substr(idx + 3);
            }

            if (unitname == "") {
                idx = text.indexOf("OUTPUT_");
                if (idx != -1) {
                    unitname = "OUTPUT";

                    idx = text.indexOf("_$__");
                    if (idx != -1) {
                        classname = text.substr(7, idx - 7);
                        signature = text.substr(idx + 3);
                    } else {
                        signature = text.substr(6);
                    }
                }
            }

            if (signature != "") {
                for (idx = 1; idx < signature.length; idx++) {
                    if (signature[idx] == '$') {
                        if (phase == 0) phase = 1;
                        else if (phase == 1) {
                            if (paramtype == "") phase = 2;
                            else if (params != "") {
                                params = params + "," + paramtype;
                                paramtype = "";
                            } else if (params == "") {
                                params = paramtype;
                                paramtype = "";
                            }
                        }
                    } else {
                        if (phase == 0) methodname = methodname + signature[idx];
                        else if (phase == 1) paramtype = paramtype + signature[idx];
                        else if (phase == 2) resulttype = resulttype + signature[idx];
                    }
                }

                if (paramtype != "") {
                    if (params != "") params = params + "," + paramtype;
                    else params = paramtype;
                }
            }

            symbolcache[text] = composeReadableMethodSignature(unitname, classname, methodname, params);
        }
    };

    var composeReadableMethodSignature = function (unitname, classname, methodname, params) {
        var signature = "";

        if (classname != "") signature = classname.toLowerCase() + ".";

        signature = signature + methodname.toLowerCase();
        signature = signature + "(" + params.toLowerCase() + ")";

        return signature;
    };

    var shouldIgnoreSymbol = function(text) {
        for (var k in ignoredsymbols) {
            if (text.startsWith(ignoredsymbols[k])) {
                return true;
            }
        }

        return false;
    };

    var demangleIfNeeded = function(text) {
        if (text.includes('$')) {
            if (shouldIgnoreSymbol(text)) {
                return text;
            }

            for (var idx in sortedsymbolcache) {
                text = text.replace(sortedsymbolcache[idx][0], sortedsymbolcache[idx][1]);
            }

            return text;
        } else {
            return text;
        }
    };

    var buildOrderedCache = function () {
        sortedsymbolcache = [];
        for (var symbol in symbolcache) {
           sortedsymbolcache.push([symbol, symbolcache[symbol]]);
        }

        sortedsymbolcache = sortedsymbolcache.sort(function(a, b) {
            return b[0].length - a[0].length;
        });

        symbolcache = {};
    };

    compiler.postProcessAsm = function (result) {
        if (!result.okToCache) return result;

        buildOrderedCache();

        for (var j = 0; j < result.asm.length; ++j)
            result.asm[j].text = demangleIfNeeded(result.asm[j].text);

        return result;
    };

    compiler.optionsForFilter = function (filters, outputFilename, userOptions) {
        options = ['-g'];

        if (this.compiler.intelAsm && filters.intel && !filters.binary) {
            options = options.concat(this.compiler.intelAsm.split(" "));
        }

        filters.preProcessLines = preProcessLines;

        return options;
    };

    var saveDummyProjectFile = function (filename) {
        fs.writeFile(filename,
            "program prog; " +
            "uses output in 'output.pas'; " +
            "begin " +
            "end.");
    };

    var preProcessBinaryAsm = function (input) {
        var relevantAsmStartsAt = input.indexOf("<OUTPUT");
        if (relevantAsmStartsAt != -1) {
            var lastLinefeedBeforeStart = input.lastIndexOf("\n", relevantAsmStartsAt);
            if (lastLinefeedBeforeStart != -1) {
                input =
                    input.substr(0, input.indexOf("00000000004")) + "\n" +
                    input.substr(lastLinefeedBeforeStart + 1);
            } else {
                input =
                    input.substr(0, result.asm.indexOf("00000000004")) + "\n" +
                    input.substr(relevantAsmStartsAt);
            }
        }

        return input;
    };

    var getExtraAsmHint = function (asm) {
        if (asm.startsWith("# [")) {
            var bracketEndPos = asm.indexOf("]", 3);
            var valueInBrackets = asm.substr(3, bracketEndPos - 3);
            var colonPos = valueInBrackets.indexOf(":");
            if (colonPos != -1) {
                valueInBrackets = valueInBrackets.substr(0, colonPos - 1);
            }

            if (!isNaN(valueInBrackets)) {
                return "  .loc 1 " + valueInBrackets + " 0";
            } else if (valueInBrackets.includes("output.pas")) {
                return "  .file 1 \"<stdin>\"";
            } else {
                return false;
            }
        } else if (asm.startsWith(".Le")) {
            return "  .cfi_endproc";
        } else {
            return false;
        }
    };

    var preProcessLines = function(asmLines) {
        var i = 0;

        while (i < asmLines.length) {
            var extraHint = getExtraAsmHint(asmLines[i]);
            if (extraHint) {
                i++;
                asmLines.splice(i, 0, extraHint);
            } else {
                addDemangleToCache(asmLines[i]);
            }

            i++;
        }

        return asmLines;
    };

    compiler.objdump = function (outputFilename, result, maxSize, intelAsm, demangle) {
         outputFilename = path.join(path.dirname(outputFilename), "prog");

         var args = ["-d", outputFilename, "-l", "--insn-width=16"];
         if (demangle) args = args.concat(["-C"]);
         if (intelAsm) args = args.concat(["-M", "intel"]);
         return this.exec(this.compiler.objdumper, args, {maxOutput: maxSize})
            .then(function (objResult) {
                if (objResult.code !== 0) {
                    result.asm = "<No output: objdump returned " + objResult.code + ">";
                } else {
                    result.asm = preProcessBinaryAsm(objResult.stdout);
                }

                return result;
            });
    };

    compiler.runCompiler = function (compiler, options, inputFilename, execOptions) {
        if (!execOptions) {
            execOptions = this.getDefaultExecOptions();
        }

        var projectFile = path.join(path.dirname(inputFilename), "prog.dpr");

        saveDummyProjectFile(projectFile);

        options.pop();
        options.push('-B');
        options.push(projectFile);

        return this.exec(compiler, options, execOptions).then(function (result) {
            result.inputFilename = inputFilename;
            result.stdout = utils.parseOutput(result.stdout, inputFilename);
            result.stderr = utils.parseOutput(result.stderr, inputFilename);
            return result;
        });
    };

    compiler.execBinary = function (executable, result, maxSize) {
        executable = path.join(path.dirname(executable), "prog");

        originalExecBinary(executable, result, maxSize);
    };

    return compiler.initialise();
}

module.exports = compileFPC;
<|endoftext|>


<commit_before>var jf = require('jsonfile');
require('sugar');

jf.spaces = 4;

module.exports = Config;

function Config() {
    "use strict";
    this.sources = [];
}

Config.prototype.get = function (key) {
    var defaultValue = undefined;
    this.sources.forEach(function (source) {
        "use strict";
        if (source[key] !== undefined) {
            defaultValue = source[key];
        }
    });
    return defaultValue;
};

Config.prototype.readJsonConfig = function (path) {
    "use strict";
    var config = jf.readFileSync(path, {throws: false}) || {};
    this._externalConfig = config;
    this._externalConfigPath = path;
    return config;
};

Config.prototype.addSource = function addSource(source) {
    "use strict";
    if (Object.isString(source)) {
        source = this.readJsonConfig(source);
    }
    this.sources.add(source);
    return this;
};

Config.prototype.set = function (obj) {
    "use strict";
    if (!this._externalConfig || !this._externalConfigPath) {
        console.log("no config file to write config to");
        return;
    }
    Object.merge(this._externalConfig, obj, true);
    jf.writeFile(this._externalConfigPath, this._externalConfig);
};

Config.prototype.unset = function (params) {
    "use strict";
    for (var key in params) {
        if (params.hasOwnProperty(key)) {
            var param = params[key];
            if (Object.isBoolean(param) && param) {
                delete this._externalConfig[key];
            } else if (Object.isObject(param)) {
                for (var key2 in param) {
                    if (param.hasOwnProperty(key2)) {
                        if (Object.isBoolean(param[key2]) && param[key2]) {
                            delete this._externalConfig[key][key2];
                        }
                    }
                }
            } else if (Object.isArray(param)) {
                param.forEach(function (key2) {
                    delete this._externalConfig[key][key2]
                }, this)
            }
        }
    }
    jf.writeFile(this._externalConfigPath, this._externalConfig);
};<commit_msg>Catching file not found or format exceptions for config files. Now a fresh install can run without creating a config file first<commit_after>var jf = require('jsonfile');
require('sugar');

jf.spaces = 4;

module.exports = Config;

function Config() {
    "use strict";
    this.sources = [];
}

Config.prototype.get = function (key) {
    var defaultValue = undefined;
    this.sources.forEach(function (source) {
        "use strict";
        if (source[key] !== undefined) {
            defaultValue = source[key];
        }
    });
    return defaultValue;
};

Config.prototype.readJsonConfig = function (path) {
    "use strict";
    try {
        var config = jf.readFileSync(path) || {};
    } catch (e){
        config = {};
    }
    this._externalConfig = config;
    this._externalConfigPath = path;
    return config;
};

Config.prototype.addSource = function addSource(source) {
    "use strict";
    if (Object.isString(source)) {
        source = this.readJsonConfig(source);
    }
    this.sources.add(source);
    return this;
};

Config.prototype.set = function (obj) {
    "use strict";
    if (!this._externalConfig || !this._externalConfigPath) {
        console.log("no config file to write config to");
        return;
    }
    Object.merge(this._externalConfig, obj, true);
    jf.writeFile(this._externalConfigPath, this._externalConfig);
};

Config.prototype.unset = function (params) {
    "use strict";
    for (var key in params) {
        if (params.hasOwnProperty(key)) {
            var param = params[key];
            if (Object.isBoolean(param) && param) {
                delete this._externalConfig[key];
            } else if (Object.isObject(param)) {
                for (var key2 in param) {
                    if (param.hasOwnProperty(key2)) {
                        if (Object.isBoolean(param[key2]) && param[key2]) {
                            delete this._externalConfig[key][key2];
                        }
                    }
                }
            } else if (Object.isArray(param)) {
                param.forEach(function (key2) {
                    delete this._externalConfig[key][key2]
                }, this)
            }
        }
    }
    jf.writeFile(this._externalConfigPath, this._externalConfig);
};<|endoftext|>


<commit_before>class Composer {
  constructor (...handlers) {
    this.handler = Composer.compose(handlers)
  }

  use (...fns) {
    this.handler = Composer.compose([this.handler, ...fns])
    return this
  }

  on (updateTypes, ...fns) {
    return this.use(Composer.mount(updateTypes, Composer.compose(fns)))
  }

  hears (triggers, ...fns) {
    return this.use(Composer.hears(triggers, Composer.compose(fns)))
  }

  command (commands, ...fns) {
    return this.use(Composer.command(commands, Composer.compose(fns)))
  }

  action (triggers, ...fns) {
    return this.use(Composer.action(triggers, Composer.compose(fns)))
  }

  gameQuery (...fns) {
    return this.use(Composer.gameQuery(Composer.compose(fns)))
  }

  drop (predicate) {
    return this.use(Composer.drop(predicate))
  }

  filter (predicate) {
    return this.use(Composer.filter(predicate))
  }

  middleware () {
    return this.handler
  }

  static reply (...args) {
    return (ctx) => ctx.reply(...args)
  }

  static fork (middleware) {
    return (ctx, next) => {
      setImmediate(Composer.unwrap(middleware), ctx, Composer.safePassThru())
      return next(ctx)
    }
  }

  static passThru () {
    return (ctx, next) => next(ctx)
  }

  static safePassThru () {
    return (ctx, next) => typeof next === 'function' ? next(ctx) : Promise.resolve()
  }

  static lazy (factoryFn) {
    if (typeof factoryFn !== 'function') {
      throw new Error('Argument must be a function')
    }
    return (ctx, next) => Promise.resolve(factoryFn(ctx))
      .then((middleware) => Composer.unwrap(middleware)(ctx, next))
  }

  static log (logFn = console.log) {
    return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2)))
  }

  static branch (predicate, trueMiddleware, falseMiddleware) {
    if (typeof predicate !== 'function') {
      return predicate ? trueMiddleware : falseMiddleware
    }
    return Composer.lazy((ctx) => Promise.resolve(predicate(ctx))
      .then((value) => value ? trueMiddleware : falseMiddleware))
  }

  static optional (predicate, ...fns) {
    return Composer.branch(predicate, Composer.compose(fns), Composer.safePassThru())
  }

  static filter (predicate) {
    return Composer.branch(predicate, Composer.safePassThru(), () => {})
  }

  static drop (predicate) {
    return Composer.branch(predicate, () => {}, Composer.safePassThru())
  }

  static dispatch (routeFn, handlers) {
    return typeof routeFn === 'function'
      ? Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value]))
      : handlers[routeFn]
  }

  static mount (updateType, middleware) {
    const predicate = Array.isArray(updateType)
      ? (ctx) => updateType.includes(ctx.updateType) || updateType.some(type => ctx.updateSubTypes.includes(type))
      : (ctx) => updateType === ctx.updateType || ctx.updateSubTypes.includes(updateType)
    return Composer.optional(predicate, middleware)
  }

  static hears (triggers, middleware) {
    return Composer.mount('text', Composer.match(normalizeTriggers(triggers), middleware))
  }

  static action (triggers, middleware) {
    return Composer.mount('callback_query', Composer.match(normalizeTriggers(triggers), middleware))
  }

  static match (triggers, middleware) {
    return Composer.lazy((ctx) => {
      const text = (
        (ctx.message && (ctx.message.caption || ctx.message.text)) ||
        (ctx.callbackQuery && ctx.callbackQuery.data)
      )
      for (let trigger of triggers) {
        ctx.match = trigger(text, ctx)
        if (ctx.match) {
          return middleware
        }
      }
      return Composer.safePassThru()
    })
  }

  static acl (userId, middleware) {
    if (typeof userId === 'function') {
      return Composer.optional(userId, middleware)
    }
    const allowed = Array.isArray(userId) ? userId : [userId]
    return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), middleware)
  }

  static gameQuery (middleware) {
    return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, middleware))
  }

  static command (command, middleware) {
    let commands = Array.isArray(command) ? command : [command]
    commands = commands.map((cmd) => cmd.startsWith('/') ? cmd : `/${cmd}`)
    return Composer.mount('text', Composer.lazy((ctx) => {
      const text = ctx.message.text
      const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup')
        ? commands.map((command) => `${command}@${ctx.me}`)
        : []
      const hasMatch = ctx.message.entities && ctx.message.entities.find((entity) => {
        const command = text.substring(entity.offset, entity.offset + entity.length)
        return entity.type === 'bot_command' && entity.offset === 0 && (commands.includes(command) || groupCommands.includes(command))
      })
      return hasMatch ? middleware : Composer.safePassThru()
    }))
  }

  static unwrap (handler) {
    return handler && typeof handler.middleware === 'function'
      ? handler.middleware()
      : handler
  }

  static compose (middlewares) {
    if (!Array.isArray(middlewares)) {
      throw new Error('Middlewares must be an array')
    }
    if (middlewares.length === 0) {
      return Composer.safePassThru()
    }
    if (middlewares.length === 1) {
      return Composer.unwrap(middlewares[0])
    }
    return (ctx, next) => {
      let index = -1
      return execute(0, ctx)
      function execute (i, context) {
        if (i <= index) {
          return Promise.reject(new Error('next() called multiple times'))
        }
        index = i
        const handler = Composer.unwrap(middlewares[i]) || next
        if (!handler) {
          return Promise.resolve()
        }
        try {
          return Promise.resolve(
            handler(context, (ctx = context) => execute(i + 1, ctx))
          )
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
  }
}

function normalizeTriggers (triggers) {
  if (!Array.isArray(triggers)) {
    triggers = [triggers]
  }
  return triggers.map((trigger) => {
    if (!trigger) {
      throw new Error('Invalid trigger')
    }
    if (typeof trigger === 'function') {
      return trigger
    }
    if (trigger instanceof RegExp) {
      return (value) => {
        trigger.lastIndex = 0
        return trigger.exec(value || '')
      }
    }
    return (value) => trigger === value ? value : null
  })
}

module.exports = Composer
<commit_msg>add context type check<commit_after>const TelegrafContext = require('./context')

class Composer {
  constructor (...handlers) {
    this.handler = Composer.compose(handlers)
  }

  use (...fns) {
    this.handler = Composer.compose([this.handler, ...fns])
    return this
  }

  on (updateTypes, ...fns) {
    return this.use(Composer.mount(updateTypes, Composer.compose(fns)))
  }

  hears (triggers, ...fns) {
    return this.use(Composer.hears(triggers, Composer.compose(fns)))
  }

  command (commands, ...fns) {
    return this.use(Composer.command(commands, Composer.compose(fns)))
  }

  action (triggers, ...fns) {
    return this.use(Composer.action(triggers, Composer.compose(fns)))
  }

  gameQuery (...fns) {
    return this.use(Composer.gameQuery(Composer.compose(fns)))
  }

  drop (predicate) {
    return this.use(Composer.drop(predicate))
  }

  filter (predicate) {
    return this.use(Composer.filter(predicate))
  }

  middleware () {
    return this.handler
  }

  static reply (...args) {
    return (ctx) => ctx.reply(...args)
  }

  static fork (middleware) {
    return (ctx, next) => {
      setImmediate(Composer.unwrap(middleware), ctx, Composer.safePassThru())
      return next(ctx)
    }
  }

  static passThru () {
    return (ctx, next) => next(ctx)
  }

  static safePassThru () {
    return (ctx, next) => typeof next === 'function' ? next(ctx) : Promise.resolve()
  }

  static lazy (factoryFn) {
    if (typeof factoryFn !== 'function') {
      throw new Error('Argument must be a function')
    }
    return (ctx, next) => Promise.resolve(factoryFn(ctx))
      .then((middleware) => Composer.unwrap(middleware)(ctx, next))
  }

  static log (logFn = console.log) {
    return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2)))
  }

  static branch (predicate, trueMiddleware, falseMiddleware) {
    if (typeof predicate !== 'function') {
      return predicate ? trueMiddleware : falseMiddleware
    }
    return Composer.lazy((ctx) => Promise.resolve(predicate(ctx))
      .then((value) => value ? trueMiddleware : falseMiddleware))
  }

  static optional (predicate, ...fns) {
    return Composer.branch(predicate, Composer.compose(fns), Composer.safePassThru())
  }

  static filter (predicate) {
    return Composer.branch(predicate, Composer.safePassThru(), () => {})
  }

  static drop (predicate) {
    return Composer.branch(predicate, () => {}, Composer.safePassThru())
  }

  static dispatch (routeFn, handlers) {
    return typeof routeFn === 'function'
      ? Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value]))
      : handlers[routeFn]
  }

  static mount (updateType, middleware) {
    const predicate = Array.isArray(updateType)
      ? (ctx) => updateType.includes(ctx.updateType) || updateType.some(type => ctx.updateSubTypes.includes(type))
      : (ctx) => updateType === ctx.updateType || ctx.updateSubTypes.includes(updateType)
    return Composer.optional(predicate, middleware)
  }

  static hears (triggers, middleware) {
    return Composer.mount('text', Composer.match(normalizeTriggers(triggers), middleware))
  }

  static action (triggers, middleware) {
    return Composer.mount('callback_query', Composer.match(normalizeTriggers(triggers), middleware))
  }

  static match (triggers, middleware) {
    return Composer.lazy((ctx) => {
      const text = (
        (ctx.message && (ctx.message.caption || ctx.message.text)) ||
        (ctx.callbackQuery && ctx.callbackQuery.data)
      )
      for (let trigger of triggers) {
        ctx.match = trigger(text, ctx)
        if (ctx.match) {
          return middleware
        }
      }
      return Composer.safePassThru()
    })
  }

  static acl (userId, middleware) {
    if (typeof userId === 'function') {
      return Composer.optional(userId, middleware)
    }
    const allowed = Array.isArray(userId) ? userId : [userId]
    return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), middleware)
  }

  static gameQuery (middleware) {
    return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, middleware))
  }

  static command (command, middleware) {
    let commands = Array.isArray(command) ? command : [command]
    commands = commands.map((cmd) => cmd.startsWith('/') ? cmd : `/${cmd}`)
    return Composer.mount('text', Composer.lazy((ctx) => {
      const text = ctx.message.text
      const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup')
        ? commands.map((command) => `${command}@${ctx.me}`)
        : []
      const hasMatch = ctx.message.entities && ctx.message.entities.find((entity) => {
        const command = text.substring(entity.offset, entity.offset + entity.length)
        return entity.type === 'bot_command' && entity.offset === 0 && (commands.includes(command) || groupCommands.includes(command))
      })
      return hasMatch ? middleware : Composer.safePassThru()
    }))
  }

  static unwrap (handler) {
    return handler && typeof handler.middleware === 'function'
      ? handler.middleware()
      : handler
  }

  static compose (middlewares) {
    if (!Array.isArray(middlewares)) {
      throw new Error('Middlewares must be an array')
    }
    if (middlewares.length === 0) {
      return Composer.safePassThru()
    }
    if (middlewares.length === 1) {
      return Composer.unwrap(middlewares[0])
    }
    return (ctx, next) => {
      let index = -1
      return execute(0, ctx)
      function execute (i, context) {
        if (!(context instanceof TelegrafContext)) {
          return Promise.reject(new Error('next(ctx) called with invalid context'))
        }
        if (i <= index) {
          return Promise.reject(new Error('next() called multiple times'))
        }
        index = i
        const handler = Composer.unwrap(middlewares[i]) || next
        if (!handler) {
          return Promise.resolve()
        }
        try {
          return Promise.resolve(
            handler(context, (ctx = context) => execute(i + 1, ctx))
          )
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
  }
}

function normalizeTriggers (triggers) {
  if (!Array.isArray(triggers)) {
    triggers = [triggers]
  }
  return triggers.map((trigger) => {
    if (!trigger) {
      throw new Error('Invalid trigger')
    }
    if (typeof trigger === 'function') {
      return trigger
    }
    if (trigger instanceof RegExp) {
      return (value) => {
        trigger.lastIndex = 0
        return trigger.exec(value || '')
      }
    }
    return (value) => trigger === value ? value : null
  })
}

module.exports = Composer
<|endoftext|>