var repl = require("repl");
var expect = require("@truffle/expect");
var async = require("async");
var EventEmitter = require("events");
var inherits = require("util").inherits;

inherits(ReplManager, EventEmitter);

function ReplManager(options) {
  EventEmitter.call(this);

  expect.options(options, [
    "working_directory",
    "contracts_directory",
    "contracts_build_directory",
    "migrations_directory",
    "network",
    "network_id",
    "provider",
    "resolver",
    "build_directory"
  ]);

  this.options = options;
  this.repl = options.repl;

  this.contexts = [];
}

ReplManager.prototype.start = function(options) {
  var self = this;

  this.contexts.push({
    prompt: options.prompt,
    interpreter: options.interpreter,
    ignoreUndefined: options.ignoreUndefined || false,
    done: options.done
  });

  var currentContext = this.contexts[this.contexts.length - 1];

  if (!this.repl) {
    this.repl = repl.start({
      prompt: currentContext.prompt,
      eval: this.interpret.bind(this)
    });

    this.repl.on("exit", function() {
      // If we exit for some reason, call done functions for good measure
      // then ensure the process is completely killed. Once the repl exits,
      // the process is in a bad state and can't be recovered (e.g., stdin is closed).
      const doneFunctions = self.contexts.reduce((functions, context) => {
        if (context.done) functions.push(context.done);
        return functions;
      }, []);
      async.series(doneFunctions, process.exit);
    });
  }

  // Bubble the internal repl's exit event
  this.repl.on("exit", function() {
    self.emit("exit");
  });

  // Bubble the internal repl's reset event
  this.repl.on("reset", function() {
    process.stdout.write("\u001B[2J\u001B[0;0f");
    self.emit("reset");
  });

  this.repl.setPrompt(options.prompt);
  this.setContextVars(options.context || {});
  this.activate(options);
};

ReplManager.prototype.setContextVars = function(obj) {
  var self = this;
  if (this.repl) {
    Object.keys(obj || {}).forEach(function(key) {
      self.repl.context[key] = obj[key];
    });
  }
};

ReplManager.prototype.activate = function(session) {
  const { prompt, context, ignoreUndefined } = session;
  this.repl.setPrompt(prompt);
  this.repl.ignoreUndefined = ignoreUndefined;
  this.setContextVars(context);
};

ReplManager.prototype.stop = function(callback) {
  var oldContext = this.contexts.pop();

  if (oldContext.done) {
    oldContext.done();
  }

  var currentContext = this.contexts[this.contexts.length - 1];

  if (currentContext) {
    this.activate(currentContext);
  } else {
    // If there's no new context, stop the process altogether.
    // Though this might seem like an out of place process.exit(),
    // once the Node repl closes, the state of the process is not
    // recoverable; e.g., stdin is closed and can't be reopened.
    // Since we can't recover to a state before the repl was opened,
    // we should just exit. He're, we'll exit after we've popped
    // off the stack of all repl contexts.
    process.exit();
  }

  if (callback) {
    callback();
  }
};

ReplManager.prototype.interpret = function(
  replInput,
  context,
  filename,
  callback
) {
  const currentContext = this.contexts[this.contexts.length - 1];
  currentContext.interpreter(replInput, context, filename, callback);
};

module.exports = ReplManager;

@@ -1308,21 +1308,19 @@
 .%0A      
-const
+var
  doneFun
@@ -1346,15 +1346,11 @@
 xts.
-reduce(
+map
 (fun
@@ -1358,19 +1358,17 @@
 tion
-s, 
+(
 context)
  =%3E 
@@ -1367,11 +1367,8 @@
 ext)
- =%3E
  %7B%0A 
@@ -1374,20 +1374,23 @@
         
-if (
+return 
 context.
@@ -1397,25 +1397,48 @@
 done
-) functions.push(
+%0A          ? function() %7B%0A              
 cont
@@ -1445,16 +1445,17 @@
 ext.done
+(
 );%0A     
@@ -1457,22 +1457,33 @@
         
+  
-return
+  %7D%0A          :
  functio
@@ -1479,25 +1479,29 @@
   : function
-s
+() %7B%7D
 ;%0A      %7D, %5B
@@ -1501,12 +1501,8 @@
    %7D
-, %5B%5D
 );%0A 
@@ -1533,16 +1533,37 @@
 nctions,
+ function() %7B%0A       
  process
@@ -1567,16 +1567,27 @@
 ess.exit
+();%0A      %7D
 );%0A    %7D

Revert "Update repl.js improve readability & performance."