/ bin / embark
embark
   1  #!/usr/bin/env node
   2  
   3  /* global __dirname __filename process require */
   4  
   5  // This script doesn't use JS syntax, packages, or APIs *un*supported by any
   6  // node version >=4.0.0, so unsupported versions from v4.0.0+ will get the
   7  // intended error messages. A future version of this script could instead rely
   8  // on babel to achieve the same goal.
   9  // See: https://node.green/
  10  
  11  // KEY ASSUMPTION: for a DApp to be valid, from embark's perspective, it must
  12  // have a parsable embark.json file in its top-level directory; if that
  13  // requirement changes in the future then this script must be revised.
  14  // Hypothetical example of such a change: embark config info may be included in
  15  // package.json under `{"embark": {...}}` -or- stored in an embark.json file.
  16  
  17  function main() {
  18    whenNoShim();
  19    var invoked = thisEmbark();
  20    var embarkJson = findEmbarkJson();
  21    var dappPath = embarkJson.dirname;
  22    process.chdir(dappPath);
  23    process.env.DAPP_PATH = dappPath;
  24    process.env.PWD = dappPath;
  25  
  26    /* attempt to find a "local" embark in or above but not below dappPath
  27  
  28       let `dappPath/(([../])*)bin/embark` be a "containing" embark
  29  
  30       let `dappPath/(([../])*)node_modules/embark/bin/embark` be an "installed"
  31       embark
  32  
  33       if containing and installed embarks are both found, and if containing
  34       embark is higher in the dir structure than installed embark, then
  35       containing embark will be selected
  36  
  37       according to the rule above: if an installed embark is found within a
  38       containing embark's own node_modules (that would be odd), installed embark
  39       will be selected
  40  
  41       invoked embark may find itself as local embark, but that is detected by
  42       comparing `binrealpath` props to avoid double-checking and infinite loops
  43  
  44       if no local embark is found then cmd execution will use invoked embark */
  45  
  46    var containing = findBinContaining(dappPath, invoked);
  47    var installed = findBinInstalled(dappPath, invoked);
  48    var local = selectLocal(containing, installed);
  49    var pkgJson = findPkgJson(dappPath, embarkJson, local);
  50    process.env.PKG_PATH = pkgJson.dirname;
  51    var embark = select(invoked, local);
  52    process.env.EMBARK_PATH = embark.pkgDir;
  53    embark.exec();
  54  }
  55  
  56  // -----------------------------------------------------------------------------
  57  
  58  var checkDeps = require('check-dependencies');
  59  var npmlog = require('npmlog');
  60  var findUp = require('find-up');
  61  var fs = require('fs');
  62  var path = require('path');
  63  var parseJsonWithErrors = require('json-parse-better-errors');
  64  var pkgUp = require('pkg-up');
  65  var semver = require('semver');
  66  var subdir = require('subdir');
  67  
  68  // -- embark bins --------------------------------------------------------------
  69  
  70  function EmbarkBin(binpath, kind) {
  71    this.binpath = binpath;
  72    this.binrealpath = undefined;
  73    this.kind = kind || 'invoked';
  74    this.pkgDir = undefined;
  75    this.pkgJson = undefined;
  76  }
  77  
  78  EmbarkBin.prototype.exec = function () {
  79    var Cmd = require('../cmd/cmd');
  80    var cli = new Cmd();
  81    if(_logged) { console[this.loglevel](); }
  82    cli.process(process.argv);
  83  };
  84  
  85  EmbarkBin.prototype.handle = function () {
  86    this.setup();
  87    this.log();
  88    return this;
  89  };
  90  
  91  EmbarkBin.prototype.log = function () {
  92    this.logMissingBin();
  93    this.pkgJson.log();
  94  };
  95  
  96  EmbarkBin.prototype.loglevel = 'info';
  97  
  98  EmbarkBin.prototype.logMissingBin = function () {
  99    var oldlevel = this.loglevel;
 100    this.loglevel = 'error';
 101    if (!this.binrealpath) {
 102      reportMissingFile_EmbarkBin(this.binpath, this.kind, this.loglevel);
 103      exitWithError();
 104    }
 105    this.loglevel = oldlevel;
 106  };
 107  
 108  EmbarkBin.prototype.setBinrealpath = function () {
 109    if (this.binpath) {
 110      this.binrealpath = realpath(this.binpath);
 111    }
 112  };
 113  
 114  EmbarkBin.prototype.setPkgDir = function () {
 115    if (this.binpath) {
 116      this.pkgDir = path.join(path.dirname(this.binpath), '..');
 117    }
 118  };
 119  
 120  EmbarkBin.prototype.setPkgJson = function () {
 121    if (this.binrealpath) {
 122      this.pkgJson = (
 123        new PkgJsonEmbark(
 124          path.join(this.pkgDir, 'package.json'),
 125          this.kind
 126        )
 127      );
 128      var upNodeModules = findUp.sync(
 129        'node_modules',
 130        {cwd: path.join(path.dirname(this.binrealpath), '../..')}
 131      );
 132      this.pkgJson.noCheck = (
 133        upNodeModules ? subdir(upNodeModules, this.binrealpath) : false
 134      );
 135      this.pkgJson.setup();
 136    }
 137  };
 138  
 139  EmbarkBin.prototype.setup = function () {
 140    this.setBinrealpath();
 141    this.setPkgDir();
 142    this.setPkgJson();
 143    return this;
 144  };
 145  
 146  // -- bin/embark :: local ------------------------------------------------------
 147  
 148  function EmbarkBinLocal(binpath, invokedEmbark) {
 149    EmbarkBin.call(this, binpath, 'local');
 150    this.invokedEmbark = invokedEmbark;
 151  }
 152  setupProto(EmbarkBinLocal, EmbarkBin);
 153  
 154  EmbarkBinLocal.prototype.exec = function () {
 155    process.argv[1] = this.binpath;
 156    process.env.EMBARK_NO_SHIM = true;
 157    console[this.loglevel]();
 158    require(this.binpath);
 159  };
 160  
 161  EmbarkBinLocal.prototype.log = function () {
 162    if (this.binrealpath !== this.invokedEmbark.binrealpath) {
 163      this.logSwitching();
 164      EmbarkBin.prototype.log.call(this);
 165    }
 166  };
 167  
 168  EmbarkBinLocal.prototype.logSwitching = function () {
 169    reportSwitching(
 170      this.invokedEmbark.binpath,
 171      this.binpath,
 172      this.loglevel,
 173      this.invokedEmbark.pkgJson.pkg,
 174      this.pkgJson.pkg
 175    );
 176  };
 177  
 178  EmbarkBinLocal.prototype.setup = function () {
 179    this.setBinrealpath();
 180    this.setPkgDir();
 181    if (this.binrealpath !== this.invokedEmbark.binrealpath) {
 182      this.setPkgJson();
 183    }
 184    return this;
 185  };
 186  
 187  // -- bin/embark :: local containing -------------------------------------------
 188  
 189  function EmbarkBinLocalContaining(binpath, invokedEmbark) {
 190    EmbarkBinLocal.call(this, binpath, invokedEmbark);
 191  }
 192  setupProto(EmbarkBinLocalContaining, EmbarkBinLocal);
 193  
 194  EmbarkBinLocalContaining.prototype.setPkgJson = function () {
 195    if (this.binrealpath) {
 196      this.pkgJson = (
 197        new PkgJsonEmbark(
 198          path.join(this.pkgDir, 'package.json'),
 199          this.kind
 200        )
 201      );
 202      this.pkgJson.noCheck = false;
 203      this.pkgJson.setup();
 204    }
 205  };
 206  
 207  // -- bin/embark :: local installed --------------------------------------------
 208  
 209  function EmbarkBinLocalInstalled(binpath, invokedEmbark) {
 210    EmbarkBinLocal.call(this, binpath, invokedEmbark);
 211  }
 212  setupProto(EmbarkBinLocalInstalled, EmbarkBinLocal);
 213  
 214  EmbarkBinLocalInstalled.prototype.log = function () {
 215    EmbarkBinLocal.prototype.log.call(this);
 216    this.pkgJsonLocalExpected.log();
 217  };
 218  
 219  EmbarkBinLocalInstalled.prototype.setPkgJson = function () {
 220    if (this.binrealpath) {
 221      this.pkgJson = (
 222        new PkgJsonEmbark(
 223          path.join(this.pkgDir, 'package.json'),
 224          this.kind
 225        )
 226      ).setup();
 227    }
 228  };
 229  
 230  EmbarkBinLocalInstalled.prototype.setPkgJsonLocalExpected = function () {
 231    if (this.binrealpath) {
 232      this.pkgJsonLocalExpected = (
 233        new PkgJsonLocalExpected(path.join(this.pkgDir, '../../package.json'))
 234      ).setup();
 235    }
 236  };
 237  
 238  EmbarkBinLocalInstalled.prototype.setup = function () {
 239    EmbarkBinLocal.prototype.setup.call(this);
 240    this.setPkgJsonLocalExpected();
 241    return this;
 242  };
 243  
 244  // -- finders ------------------------------------------------------------------
 245  
 246  function findBin(dappPath, find, invoked, Kind) {
 247    return (
 248      new Kind(
 249        findUp.sync(find, {cwd: dappPath}),
 250        invoked
 251      )
 252    ).setup();
 253  }
 254  
 255  function findBinContaining(dappPath, invoked) {
 256    return findBin(
 257      dappPath,
 258      'bin/embark',
 259      invoked,
 260      EmbarkBinLocalContaining
 261    );
 262  }
 263  
 264  function findBinInstalled(dappPath, invoked) {
 265    return findBin(
 266      dappPath,
 267      'node_modules/embark/bin/embark',
 268      invoked,
 269      EmbarkBinLocalInstalled
 270    );
 271  }
 272  
 273  function findEmbarkJson() {
 274    // findUp search begins in process.cwd() by default, but embark.json could
 275    // be in a subdir if embark was invoked via `npm run` (which changes cwd to
 276    // package.json's dir) and the package.json is in a dir above the top-level
 277    // DApp dir; so start at INIT_CWD if that has been set (by npm, presumably)
 278    // See: https://docs.npmjs.com/cli/run-script
 279    var startDir = initCwd();
 280    return (new EmbarkJson(
 281      findUp.sync('embark.json', {cwd: startDir}) ||
 282        path.join(startDir, 'embark.json'),
 283      process.argv[2]
 284    )).handle();
 285  }
 286  
 287  function findPkgJson(dappPath, embarkJson, local) {
 288    var skipDirs = [];
 289    if (local) {
 290      if (local instanceof EmbarkBinLocalContaining) {
 291        skipDirs.push(local.pkgDir);
 292      }
 293      if (local instanceof EmbarkBinLocalInstalled) {
 294        skipDirs.push(local.pkgJsonLocalExpected.dirname);
 295      }
 296    }
 297    var closest, dir, found;
 298    var startDir = dappPath;
 299  
 300    /* let `dappPath/(([../])*)package.json` be a "local" package.json */
 301  
 302    // look for local package.json files starting from dappPath
 303    function stop() {
 304      found = pkgUp.sync(startDir);
 305      if (found && !closest) {
 306        closest = found;
 307      }
 308      dir = found ? path.dirname(found) : found;
 309      var stop = !dir || !isDappCmd(embarkJson.cmd);
 310      if (!stop) {
 311        startDir = path.join(dir, '..');
 312      }
 313      return stop;
 314    }
 315    while (!stop()) {
 316      if (skipDirs.indexOf(dir) === -1) {
 317        (new PkgJsonLocal(found)).handle();
 318      }
 319    }
 320    if (isDappCmd(embarkJson.cmd) && !closest) {
 321      var loglevel = 'error';
 322      reportMissingFile(path.join(dappPath, 'package.json'), loglevel);
 323      reportMissingFile_DappJson(embarkJson.cmd, loglevel, 'package', 'in or above');
 324      exitWithError();
 325    }
 326    return (
 327      closest || (new PkgJsonLocal(path.join(startDir, 'package.json'))).setup()
 328    );
 329  }
 330  
 331  // -- json files ---------------------------------------------------------------
 332  
 333  function Json(filepath) {
 334    this.filepath = filepath;
 335    this.dirname = undefined;
 336    this.json = undefined;
 337    this.realpath = undefined;
 338  }
 339  
 340  Json.prototype.handle = function () {
 341    this.setup();
 342    this.log();
 343    return this;
 344  };
 345  
 346  Json.prototype.log = function () {
 347    this.logMissingFile();
 348    this.logUnparsable();
 349  };
 350  
 351  Json.prototype.loglevel = 'warn';
 352  
 353  Json.prototype.logMissingFile = function () {
 354    var missing;
 355    if (!this.realpath) {
 356      missing = true;
 357      reportMissingFile(this.filepath, this.loglevel);
 358    }
 359    return missing;
 360  };
 361  
 362  Json.prototype.logUnparsable = function () {
 363    var unparsable;
 364    if (this.realpath && !this.json) {
 365      unparsable = true;
 366      reportUnparsable(this.filepath, this.loglevel);
 367    }
 368    return unparsable;
 369  };
 370  
 371  Json.prototype.setDirname = function () {
 372    if (this.filepath) {
 373      this.dirname = path.dirname(this.filepath);
 374    }
 375  };
 376  
 377  Json.prototype.setJson = function () {
 378    if (this.realpath) {
 379      this.json = parseJson(this.filepath);
 380    }
 381  };
 382  
 383  Json.prototype.setRealpath = function () {
 384    if (this.filepath) {
 385      this.realpath = realpath(this.filepath);
 386    }
 387  };
 388  
 389  Json.prototype.setup = function () {
 390    this.setDirname();
 391    this.setRealpath();
 392    this.setJson();
 393    return this;
 394  };
 395  
 396  // -- embark.json --------------------------------------------------------------
 397  
 398  function EmbarkJson(filepath, cmd) {
 399    Json.call(this, filepath);
 400    this.cmd = cmd;
 401  }
 402  setupProto(EmbarkJson, Json);
 403  
 404  EmbarkJson.prototype.loglevel = 'error';
 405  
 406  EmbarkJson.prototype.log = function () {
 407    this.logMissingFile();
 408    this.logUnparsable();
 409  };
 410  
 411  EmbarkJson.prototype.logMissingFile = function () {
 412    if (isDappCmd(this.cmd) && Json.prototype.logMissingFile.call(this)) {
 413      reportMissingFile_DappJson(this.cmd, this.loglevel, 'embark', 'in');
 414      exitWithError();
 415    }
 416  };
 417  
 418  EmbarkJson.prototype.logUnparsable = function () {
 419    if (isDappCmd(this.cmd) && Json.prototype.logUnparsable.call(this)) {
 420      reportUnparsable_EmbarkJson(this.loglevel);
 421      exitWithError();
 422    }
 423  };
 424  
 425  // -- package.json -------------------------------------------------------------
 426  
 427  function PkgJson(filepath) {
 428    Json.call(this, filepath);
 429  }
 430  setupProto(PkgJson, Json);
 431  
 432  PkgJson.prototype.log = function () {
 433    Json.prototype.log.call(this);
 434    this.logPkgErrors();
 435  };
 436  
 437  PkgJson.prototype.logPkgErrors = function () {
 438    var pkgErrors;
 439    if (this.json && !this.noCheck) {
 440      pkgErrors = checkPkg(this.dirname);
 441    }
 442    if (pkgErrors) {
 443      reportPkgErrors(pkgErrors, this.filepath, this.loglevel);
 444    }
 445    return !!pkgErrors;
 446  };
 447  
 448  PkgJson.prototype.noCheck = false;
 449  
 450  // -- package.json :: of an embark pkg -----------------------------------------
 451  
 452  function PkgJsonEmbark(filepath, kind) {
 453    PkgJson.call(this, filepath);
 454    this.kind = kind || 'invoked';
 455    this.nodeRange = undefined;
 456    this.pkg = undefined;
 457    this.version = undefined;
 458  }
 459  setupProto(PkgJsonEmbark, PkgJson);
 460  
 461  PkgJsonEmbark.prototype.log = function () {
 462    PkgJson.prototype.log.call(this);
 463    this.logMissingVersion();
 464    this.logUnsupportedNode();
 465  };
 466  
 467  PkgJsonEmbark.prototype.loglevel = 'error';
 468  
 469  PkgJsonEmbark.prototype.logMissingFile = function () {
 470    if (PkgJson.prototype.logMissingFile.call(this)) {
 471      reportMissingFile_PkgJsonEmbark(this.kind, this.loglevel);
 472      exitWithError();
 473    }
 474  };
 475  
 476  PkgJsonEmbark.prototype.logMissingVersion = function () {
 477    var missing;
 478    var oldlevel = this.loglevel;
 479    this.loglevel = 'warn';
 480    if (this.json && this.version === '???') {
 481      missing = true;
 482      reportMissingVersion(this.filepath, this.kind, this.loglevel);
 483    }
 484    this.loglevel = oldlevel;
 485    return missing;
 486  };
 487  
 488  PkgJsonEmbark.prototype.logPkgErrors = function () {
 489    if (PkgJson.prototype.logPkgErrors.call(this)) {
 490      reportPkgErrors_PkgJsonEmbark(this.dirname, this.kind, this.loglevel);
 491      exitWithError();
 492    }
 493  };
 494  
 495  PkgJsonEmbark.prototype.logUnparsable = function () {
 496    if (PkgJson.prototype.logUnparsable.call(this)) {
 497      reportUnparsable_PkgJsonEmbark(this.kind, this.loglevel);
 498      exitWithError();
 499    }
 500  };
 501  
 502  PkgJsonEmbark.prototype.logUnsupportedNode = function () {
 503    var missing;
 504    var range = this.nodeRange;
 505    if (typeof range === 'undefined') {
 506      missing = true;
 507      range = this.nodeRangeDefault;
 508    }
 509    var bad;
 510    range = parseRange(range);
 511    if (!range) {
 512      bad = true;
 513      range = this.nodeRangeDefault;
 514    }
 515    var procVer = semver.clean(process.version);
 516    this.loglevel = 'error';
 517    if (!semver.satisfies(procVer, range)) {
 518      reportUnsupportedNode(
 519        bad,
 520        this.filepath,
 521        this.kind,
 522        this.loglevel,
 523        missing,
 524        this.nodeRangeDefault,
 525        this.nodeRange,
 526        this.pkg,
 527        procVer,
 528        range
 529      );
 530      exitWithError();
 531    }
 532  };
 533  
 534  PkgJsonEmbark.prototype.noCheck = true;
 535  
 536  // if changing to the `nodeRangeDefault` value, make sure to manually check
 537  // that it's a valid semver range, otherwise fallback logic in the prototype
 538  // methods won't be reliable
 539  PkgJsonEmbark.prototype.nodeRangeDefault = semver.Range('>=8.11.3').range;
 540  
 541  PkgJsonEmbark.prototype.setNodeRange = function () {
 542    if (isObject(this.json) &&
 543        this.json.hasOwnProperty('engines') &&
 544        this.json.engines.hasOwnProperty('node')) {
 545      this.nodeRange = this.json.engines.node;
 546    }
 547  };
 548  
 549  PkgJsonEmbark.prototype.setPkg = function () {
 550    this.pkg = `embark@${this.version}`;
 551  };
 552  
 553  PkgJsonEmbark.prototype.setVersion = function () {
 554    if (isObject(this.json) && this.json.version) {
 555      this.version = this.json.version;
 556    } else {
 557      this.version = '???';
 558    }
 559  };
 560  
 561  PkgJsonEmbark.prototype.setup = function () {
 562    PkgJson.prototype.setup.call(this);
 563    this.setVersion();
 564    this.setPkg();
 565    this.setNodeRange();
 566    return this;
 567  };
 568  
 569  // -- package.json :: local to DApp --------------------------------------------
 570  
 571  function PkgJsonLocal(filepath) {
 572    PkgJson.call(this, filepath);
 573  }
 574  setupProto(PkgJsonLocal, PkgJson);
 575  
 576  PkgJsonLocal.prototype.loglevel = 'error';
 577  
 578  PkgJsonLocal.prototype.logMissingFile = function () {
 579    if (PkgJson.prototype.logMissingFile.call(this)) {
 580      reportMissingFile_PkgJsonLocal(this.loglevel);
 581      exitWithError();
 582    }
 583  };
 584  
 585  PkgJsonLocal.prototype.logPkgErrors = function () {
 586    if (PkgJson.prototype.logPkgErrors.call(this)) {
 587      reportPkgErrors_PkgJsonLocal(this.dirname, this.loglevel);
 588      exitWithError();
 589    }
 590  };
 591  
 592  PkgJsonLocal.prototype.logUnparsable = function () {
 593    if (PkgJson.prototype.logUnparsable.call(this)) {
 594      reportUnparsable_PkgJsonLocal(this.loglevel);
 595      exitWithError();
 596    }
 597  };
 598  
 599  // -- package.json :: local to DApp, expected by local installed embark --------
 600  
 601  function PkgJsonLocalExpected(filepath) {
 602    PkgJsonLocal.call(this, filepath);
 603    this.embarkDep = undefined;
 604  }
 605  setupProto(PkgJsonLocalExpected, PkgJsonLocal);
 606  
 607  PkgJsonLocalExpected.prototype.log = function () {
 608    PkgJsonLocal.prototype.log.call(this);
 609    this.logMissingEmbarkDep();
 610  };
 611  
 612  PkgJsonLocalExpected.prototype.logMissingEmbarkDep = function () {
 613    if (this.json && !this.embarkDep) {
 614      reportMissingEmbarkDep(this.filepath, this.dirname, this.loglevel);
 615      exitWithError();
 616    }
 617  };
 618  
 619  PkgJsonLocalExpected.prototype.logMissingFile = function () {
 620    // PkgJson.prototype NOT PkgJsonLocal.prototype
 621    if (PkgJson.prototype.logMissingFile.call(this)) {
 622      reportMissingFile_PkgJsonLocalExpected(this.dirname, this.loglevel);
 623      exitWithError();
 624    }
 625  };
 626  
 627  PkgJsonLocalExpected.prototype.setEmbarkDep = function () {
 628    if (isObject(this.json)) {
 629      if (this.json.dependencies) {
 630        this.embarkDep = this.json.dependencies.embark;
 631      } else if (this.json.devDependencies) {
 632        this.embarkDep = this.json.devDependencies.embark;
 633      }
 634    }
 635  };
 636  
 637  PkgJsonLocalExpected.prototype.setup = function () {
 638    PkgJsonLocal.prototype.setup.call(this);
 639    this.setEmbarkDep();
 640    return this;
 641  };
 642  
 643  // -- loggers ------------------------------------------------------------------
 644  
 645  var embarklog = npmlog;
 646  embarklog.heading = 'embark';
 647  
 648  var _logged = false;
 649  function logged(which) {
 650    var embarklog_which = embarklog[which];
 651    return function () {
 652      _logged = true;
 653      embarklog_which.apply(embarklog, arguments);
 654    };
 655  }
 656  
 657  embarklog.error = logged('error');
 658  embarklog.info = logged('info');
 659  embarklog.warn = logged('warn');
 660  
 661  function blankLineMaybe(which) {
 662    if (_logged) {
 663      console[which]();
 664    }
 665  }
 666  
 667  var isNpmRun = process.env.hasOwnProperty('npm_lifecycle_script');
 668  function blankLineTrailingMaybe(which) {
 669    if (isNpmRun) {
 670      console[which]();
 671    }
 672  }
 673  
 674  // -- processors ---------------------------------------------------------------
 675  
 676  function checkPkg(pkgDir, scopes) {
 677    var errors;
 678    try {
 679      var config = {packageDir: pkgDir};
 680      if (scopes) {
 681        config.scopeList = scopes;
 682      }
 683      var checked = checkDeps.sync(config);
 684      if (checked.error.length) {
 685        errors = checked.error;
 686      }
 687    } finally {
 688      // eslint-disable-next-line no-unsafe-finally
 689      return errors;
 690    }
 691  }
 692  
 693  function parseJson(filepath) {
 694    var parsed;
 695    try {
 696      parsed = require(filepath);
 697    } finally {
 698      // eslint-disable-next-line no-unsafe-finally
 699      return parsed;
 700    }
 701  }
 702  
 703  function parseRange(range) {
 704    var parsed;
 705    try {
 706      parsed = semver.Range(range).range;
 707    } finally {
 708      // eslint-disable-next-line no-unsafe-finally
 709      return parsed;
 710    }
 711  }
 712  
 713  function realpath(filepath) {
 714    var resolved;
 715    try {
 716      resolved = fs.realpathSync(filepath);
 717    } finally {
 718      // eslint-disable-next-line no-unsafe-finally
 719      return resolved;
 720    }
 721  }
 722  
 723  function thisEmbark() {
 724    return (new EmbarkBin(__filename)).handle();
 725  }
 726  
 727  function whenNoShim() {
 728    var noShim = false;
 729    if (process.env.EMBARK_NO_SHIM) {
 730      noShim = true;
 731    }
 732    if (noShim) {
 733      EmbarkBin.prototype.exec();
 734    }
 735  }
 736  
 737  // -- reporters ----------------------------------------------------------------
 738  
 739  function reportMissingEmbarkDep(filepath, dirname, loglevel) {
 740    blankLineMaybe(loglevel);
 741    embarklog[loglevel]('file', filepath);
 742    embarklog[loglevel](
 743      '',
 744      [
 745        `Could not find embark specified in "dependencies" or "devDependencies" of local package.json file`,
 746        `But embark was found in node_modules relative to that file:`,
 747        `${dirname}/node_modules/embark/`
 748      ].join('\n')
 749    );
 750  }
 751  
 752  function reportMissingFile(filepath, loglevel) {
 753    try {
 754      // force the exception
 755      fs.realpathSync(filepath);
 756    } catch (e) {
 757      blankLineMaybe(loglevel);
 758      embarklog[loglevel]('path', e.path);
 759      embarklog[loglevel]('code', e.code);
 760      embarklog[loglevel]('errno', e.errno);
 761      embarklog[loglevel]('syscall', e.syscall);
 762      embarklog[loglevel](e.code.toLowerCase(), e.message);
 763    }
 764  }
 765  
 766  function reportMissingFile_DappJson(cmd, loglevel, kind, where) {
 767    blankLineMaybe(loglevel);
 768    embarklog[loglevel](
 769      '',
 770      `Could not locate your DApp's ${kind}.json file`
 771    );
 772    embarklog[loglevel](
 773      '',
 774      `Make sure a valid ${kind}.json file exists ${where} your DApp's top-level directory`
 775    );
 776    embarklog[loglevel](
 777      '',
 778      `Embark command '${cmd}' can only be used inside a valid DApp directory structure`
 779    );
 780  }
 781  
 782  function reportMissingFile_EmbarkBin(binpath, kind, loglevel) {
 783    reportMissingFile(binpath, loglevel);
 784    console[loglevel]();
 785    embarklog[loglevel](
 786      '',
 787      [
 788        `Could not resolve ${kind} embark command path with require('fs').realpathSync`,
 789        `Maybe a broken symbolic link?`
 790      ].join('\n')
 791    );
 792  }
 793  
 794  function reportMissingFile_PkgJsonEmbark(kind, loglevel) {
 795    console[loglevel]();
 796    embarklog[loglevel](
 797      '',
 798      `Could not locate ${kind} embark's package.json file`
 799    );
 800  }
 801  
 802  function reportMissingFile_PkgJsonLocal(loglevel) {
 803    console[loglevel]();
 804    embarklog[loglevel](
 805      '',
 806      [
 807        `Could not resolve local package.json path with require('fs').realpathSync`,
 808        `Maybe a broken symbolic link?`
 809      ].join('\n')
 810    );
 811  }
 812  
 813  function reportMissingFile_PkgJsonLocalExpected(dirname, loglevel) {
 814    console[loglevel]();
 815    embarklog[loglevel](
 816      '',
 817      [
 818        `Could not find expected local package.json relative to embark found in:`,
 819        `${dirname}/node_modules/embark/`
 820      ].join('\n')
 821    );
 822  }
 823  
 824  function reportMissingVersion(filepath, kind, loglevel) {
 825    blankLineMaybe(loglevel);
 826    embarklog[loglevel]('file', filepath);
 827    embarklog[loglevel](
 828      '',
 829      `No version is specified in ${kind} embark's package.json file`
 830    );
 831  }
 832  
 833  function reportPkgErrors(errors, filepath, loglevel) {
 834    blankLineMaybe(loglevel);
 835    embarklog[loglevel]('file', filepath);
 836    embarklog[loglevel]('code', `EPKGCHK`);
 837    embarklog[loglevel]('package', errors.join('\n'));
 838  }
 839  
 840  function reportPkgErrors_PkgJsonEmbark(dirname, kind, loglevel) {
 841    console[loglevel]();
 842    embarklog[loglevel](
 843      '',
 844      [
 845        `Dependencies are missing relative to ${kind} embark's package.json in:`,
 846        `${dirname}/`
 847      ].join('\n')
 848    );
 849  }
 850  
 851  function reportPkgErrors_PkgJsonLocal(dirname, loglevel) {
 852    console[loglevel]();
 853    embarklog[loglevel](
 854      '',
 855      [
 856        `Dependencies are missing relative to local package.json in:`,
 857        `${dirname}/`
 858      ].join('\n')
 859    );
 860  }
 861  
 862  function reportSwitching(binpathFrom, binpathTo, loglevel, pkgFrom, pkgTo) {
 863    blankLineMaybe(loglevel);
 864    embarklog[loglevel]('invoked', binpathFrom);
 865    embarklog[loglevel]('located', binpathTo);
 866    embarklog[loglevel](
 867      '',
 868      `Switching from ${pkgFrom} to ${pkgTo}`
 869    );
 870  }
 871  
 872  function reportUnparsable(filepath, loglevel) {
 873    try {
 874      // force the exception
 875      parseJsonWithErrors(stripBOM(fs.readFileSync(filepath)));
 876    } catch (e) {
 877      var basename = path.basename(filepath);
 878      blankLineMaybe(loglevel);
 879      embarklog[loglevel]('file', filepath);
 880      embarklog[loglevel]('code', `EJSONPARSE`);
 881      embarklog[loglevel]('JSON parse', `Failed to parse json`);
 882      embarklog[loglevel]('JSON parse', e.message);
 883      embarklog[loglevel]('JSON parse', `Failed to parse ${basename} data.`);
 884      embarklog[loglevel]('JSON parse', `${basename} must be actual JSON, not just JavaScript.`);
 885    }
 886  }
 887  
 888  function reportUnparsable_EmbarkJson(loglevel) {
 889    console[loglevel]();
 890    embarklog[loglevel]('', `Could not parse your DApp's embark.json file`);
 891  }
 892  
 893  function reportUnparsable_PkgJsonEmbark(kind, loglevel) {
 894    console[loglevel]();
 895    embarklog[loglevel](
 896      `Could not parse ${kind} embark's package.json file`
 897    );
 898  }
 899  
 900  function reportUnparsable_PkgJsonLocal(loglevel) {
 901    embarklog[loglevel]('', `Could not parse a local package.json file`);
 902  }
 903  
 904  function reportUnsupportedNode(
 905    bad, filepath, kind, loglevel, missing, rangeDefault, rangeSupplied, pkg,
 906    procVer, range) {
 907    blankLineMaybe(loglevel);
 908    function report(qual, invalid) {
 909      embarklog[loglevel]('file', filepath);
 910      embarklog[loglevel](
 911        'engine',
 912        `package.json of ${kind} ${pkg} does not specify ${qual ? qual : ''}%j`,
 913        {engines: {node: '[semver]'}}
 914      );
 915      if (invalid) {
 916        embarklog[loglevel](
 917          'engine',
 918          `Specified: %j`, {engines: {node: rangeSupplied}}
 919        );
 920      }
 921      embarklog[loglevel](
 922        'engine',
 923        `Defaulting to: %j`, {engines: {node: rangeDefault}}
 924      );
 925      console[loglevel]();
 926    }
 927    if (missing) { report(); }
 928    if (bad) { report('a valid ', true); }
 929    embarklog[loglevel]('notsup', `Unsupported runtime`);
 930    embarklog[loglevel](
 931      'notsup',
 932      `${kind} ${pkg} is not compatible with your version of node`
 933    );
 934    embarklog[loglevel]('notsup', `Required:`, range);
 935    embarklog[loglevel]('notsup', `Actual:`, procVer);
 936  }
 937  
 938  // -- selectors ----------------------------------------------------------------
 939  
 940  function select(invoked, local) {
 941    var embark;
 942    if (local && local.binrealpath !== invoked.binrealpath) {
 943      embark = local;
 944    } else {
 945      embark = invoked;
 946    }
 947    return embark;
 948  }
 949  
 950  function selectLocal(containing, installed) {
 951    var local;
 952    if (containing.binrealpath &&
 953        (!installed.binrealpath ||
 954         subdir(
 955           installed.pkgJsonLocalExpected.dirname,
 956           containing.pkgDir
 957         ))) {
 958      local = containing;
 959    }
 960    if (installed.binrealpath &&
 961        (!containing.binrealpath ||
 962         subdir(containing.pkgDir, installed.pkgDir))) {
 963      local = installed;
 964    }
 965    if (local) { local.log(); }
 966    return local;
 967  }
 968  
 969  // -- utils --------------------------------------------------------------------
 970  
 971  function exitWithError(code) {
 972    blankLineTrailingMaybe('error');
 973    process.exit(code || 1);
 974  }
 975  
 976  function initCwd() {
 977    var initCwd = process.env.INIT_CWD || process.cwd();
 978    // allow for env override
 979    initCwd = process.env.DAPP_PATH || initCwd;
 980    return initCwd;
 981  }
 982  
 983  function isDappCmd(cmd) {
 984    return [
 985      undefined,
 986      '-V',
 987      '--version',
 988      '-h',
 989      '--help',
 990      'new',
 991      'demo',
 992      'version',
 993      'help'
 994    ].indexOf(cmd) === -1;
 995  }
 996  
 997  function isObject(val) {
 998    // eslint-disable-next-line no-eq-null
 999    return val != null && typeof val === 'object' && Array.isArray(val) === false;
1000  }
1001  
1002  function setupProto(Sub, Par) {
1003    Sub.prototype = Object.create(Par.prototype);
1004    Sub.prototype.constructor = Sub;
1005  }
1006  
1007  // See: https://github.com/npm/cli/blob/v6.4.1/lib/utils/parse-json.js#L16
1008  function stripBOM (content) {
1009    content = content.toString();
1010    if (content.charCodeAt(0) === 0xFEFF) {
1011      content = content.slice(1);
1012    }
1013    return content;
1014  }
1015  
1016  // -----------------------------------------------------------------------------
1017  
1018  main();