Repository: yargs/yargs
Pull request #675 (2016-10-16 17:35:53)

fix: stop applying parser to context object
We were applying yargs' parser to the context object (finding aliases, etc.). Unfortunately, folks often want to pass in objects with cyclical relationships to the parser, e.g., `http.request` which makes the parser blow up quite spectacularly.

We now carve out an exception for the `parse()` context object, such that it doesn't run through yargs' parser.

@ceejbot @zkat 

bcoe: fixes #671 

Patch:
diff --git a/locales/it.json b/locales/it.json
index bd9573a..f9eb375 100644
--- a/locales/it.json
+++ b/locales/it.json
@@ -10,6 +10,7 @@
   "required": "richiesto",
   "default:": "predefinito:",
   "choices:": "scelte:",
+  "aliases:": "alias:",
   "generated-value": "valore generato",
   "Not enough non-option arguments: got %s, need at least %s": "Numero insufficiente di argomenti non opzione: inseriti %s, richiesti almeno %s",
   "Too many non-option arguments: got %s, maximum of %s": "Troppi argomenti non opzione: inseriti %s, massimo possibile %s",
@@ -33,5 +34,6 @@
   "Invalid JSON config file: %s": "File di configurazione JSON non valido: %s",
   "Path to JSON config file": "Percorso del file di configurazione JSON",
   "Show help": "Mostra la schermata di aiuto",
-  "Show version number": "Mostra il numero di versione"
+  "Show version number": "Mostra il numero di versione",
+  "Did you mean %s?": "Intendi forse %s?"
 }
diff --git a/locales/pt_BR.json b/locales/pt_BR.json
index f0aec14..96d31e7 100644
--- a/locales/pt_BR.json
+++ b/locales/pt_BR.json
@@ -10,6 +10,7 @@
   "required": "obrigatório",
   "default:": "padrão:",
   "choices:": "opções:",
+  "aliases:": "sinônimos:",
   "generated-value": "valor-gerado",
   "Not enough non-option arguments: got %s, need at least %s": "Argumentos insuficientes: Argumento %s, necessário pelo menos %s",
   "Too many non-option arguments: got %s, maximum of %s": "Excesso de argumentos: recebido %s, máximo de %s",
@@ -33,5 +34,6 @@
   "Invalid JSON config file: %s": "Arquivo JSON de configuração inválido: %s",
   "Path to JSON config file": "Caminho para o arquivo JSON de configuração",
   "Show help": "Exibe ajuda",
-  "Show version number": "Exibe a versão"
+  "Show version number": "Exibe a versão",
+  "Did you mean %s?": "Você quis dizer %s?"
 }
diff --git a/yargs.js b/yargs.js
index 9b23817..4c53ed3 100644
--- a/yargs.js
+++ b/yargs.js
@@ -1,3 +1,4 @@
+const assign = require('./lib/assign')
 const Command = require('./lib/command')
 const Completion = require('./lib/completion')
 const Parser = require('yargs-parser')
@@ -165,6 +166,8 @@ function Yargs (processArgs, cwd, parentRequire) {
     command.unfreeze()
     strict = frozen.strict
     completionCommand = frozen.completionCommand
+    parseFn = null
+    parseContext = null
     frozen = undefined
   }
 
@@ -422,13 +425,12 @@ function Yargs (processArgs, cwd, parentRequire) {
   }
 
   var parseFn = null
+  var parseContext = 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') {
-      config = shortCircuit
+      parseContext = shortCircuit
       shortCircuit = _parseFn
     }
 
@@ -447,13 +449,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)
       unfreeze()
-      parseFn = null
     }
 
     return parsed
@@ -580,7 +580,7 @@ function Yargs (processArgs, cwd, parentRequire) {
   }
   self.getGroups = function () {
     // combine explicit and preserved groups. explicit groups should be first
-    return require('./lib/assign')(groups, preservedGroups)
+    return assign(groups, preservedGroups)
   }
 
   // as long as options.envPrefix is not undefined,
@@ -823,7 +823,8 @@ function Yargs (processArgs, cwd, parentRequire) {
     options.__ = y18n.__
     options.configuration = pkgUp(cwd)['yargs'] || {}
     const parsed = Parser.detailed(args, options)
-    const argv = parsed.argv
+    var argv = parsed.argv
+    if (parseContext) argv = assign(parseContext, argv)
     var aliases = parsed.aliases
 
     argv.$0 = self.$0


Test patch:
diff --git a/test/yargs.js b/test/yargs.js
index d20f171..a28fcac 100644
--- a/test/yargs.js
+++ b/test/yargs.js
@@ -938,6 +938,22 @@ describe('yargs dsl tests', function () {
         argv.what.should.equal(true)
       })
 
+      // see: https://github.com/yargs/yargs/issues/671
+      it('does not fail if context object has cyclical reference', function () {
+        var argv = null
+        var context = {state: 'grumpy but rich'}
+        context.res = context
+        yargs()
+          .command('batman <api-token>', 'batman command', function () {}, function (_argv) {
+            argv = _argv
+          })
+          .parse('batman robin --what', context, function (_err, argv, _output) {})
+
+        argv.state.should.equal('grumpy but rich')
+        argv['api-token'].should.equal('robin')
+        argv.what.should.equal(true)
+      })
+
       it('overwrites the prior context object, when parse is called multiple times', function () {
         var argv = null
         var parser = yargs()