Repository: yargs/yargs
Pull request #670 (2016-10-15 16:30:19)

perf: remove one call to reset(), fix: freeze was not resetting config objects.
* perf: addresses the performance issue @nexdrew noted, related to calling `reset()` more times than necessary.
* fix: we now freeze config-objects, allowing a new context to be passed to consecutive runs to `parse()`.

CC: @nexdrew 


Patch:
diff --git a/yargs.js b/yargs.js
index 373bfbe..9b23817 100644
--- a/yargs.js
+++ b/yargs.js
@@ -138,6 +138,7 @@ function Yargs (processArgs, cwd, parentRequire) {
   function freeze () {
     frozen = {}
     frozen.options = options
+    frozen.configObjects = options.configObjects.slice(0)
     frozen.exitProcess = exitProcess
     frozen.groups = groups
     usage.freeze()
@@ -145,11 +146,20 @@ function Yargs (processArgs, cwd, parentRequire) {
     command.freeze()
     frozen.strict = strict
     frozen.completionCommand = completionCommand
+    frozen.output = output
+    frozen.exitError = exitError
+    frozen.hasOutput = hasOutput
+    frozen.parsed = self.parsed
   }
   function unfreeze () {
     options = frozen.options
+    options.configObjects = frozen.configObjects
     exitProcess = frozen.exitProcess
     groups = frozen.groups
+    output = frozen.output
+    exitError = frozen.exitError
+    hasOutput = frozen.hasOutput
+    self.parsed = frozen.parsed
     usage.unfreeze()
     validation.unfreeze()
     command.unfreeze()
@@ -413,10 +423,12 @@ function Yargs (processArgs, cwd, parentRequire) {
 
   var parseFn = null
   self.parse = function (args, shortCircuit, _parseFn) {
+    var config = null
+
     // a context object can optionally be provided, this allows
     // additional information to be passed to a command handler.
     if (typeof shortCircuit === 'object') {
-      self.config(shortCircuit)
+      config = shortCircuit
       shortCircuit = _parseFn
     }
 
@@ -435,10 +447,11 @@ function Yargs (processArgs, cwd, parentRequire) {
       freeze()
       exitProcess = false
     }
+    if (config) self.config(config)
+
     var parsed = parseArgs(args, shortCircuit)
     if (parseFn) {
       parseFn(exitError, parsed, output)
-      self.reset()
       unfreeze()
       parseFn = null
     }


Test patch:
diff --git a/test/yargs.js b/test/yargs.js
index 71f5ef0..d20f171 100644
--- a/test/yargs.js
+++ b/test/yargs.js
@@ -851,6 +851,40 @@ describe('yargs dsl tests', function () {
       r2.logs[0].should.match(/--help.*Show help.*\[boolean\]/)
     })
 
+    it('resets error state between calls to parse', function () {
+      var y = yargs()
+        .demand(2)
+
+      var err1 = null
+      var out1 = null
+      var argv1 = null
+      y.parse('foo', function (err, argv, output) {
+        err1 = err
+        argv1 = argv
+        out1 = output
+      })
+
+      err1.message.should.match(/Not enough non-option arguments/)
+      argv1._.should.include('foo')
+      out1.should.match(/Not enough non-option arguments/)
+
+      var err2 = null
+      var argv2 = null
+      var out2 = null
+      y.parse('foo bar', function (err, argv, output) {
+        err2 = err
+        argv2 = argv
+        out2 = output
+      })
+
+      expect(err2).to.equal(null)
+      argv2._.should.deep.equal([
+        'foo',
+        'bar'
+      ])
+      expect(out2).to.equal('')
+    })
+
     describe('commands', function () {
       it('does not invoke command handler if output is populated', function () {
         var err = null
@@ -904,6 +938,24 @@ describe('yargs dsl tests', function () {
         argv.what.should.equal(true)
       })
 
+      it('overwrites the prior context object, when parse is called multiple times', function () {
+        var argv = null
+        var parser = yargs()
+          .command('batman <api-token>', 'batman command', function () {}, function (_argv) {})
+
+        parser.parse('batman robin --what', {
+          state: 'grumpy but rich'
+        }, function (_err, _argv, _output) {})
+
+        parser.parse('batman robin --what', {
+          state: 'the hero we need'
+        }, function (_err, _argv, _output) {
+          argv = _argv
+        })
+
+        argv.state.should.equal('the hero we need')
+      })
+
       it('populates argv appropriately when parse is called multiple times', function () {
         var parser = yargs()
           .command('batman <api-token>', 'batman command', function () {}, function (_argv) {})