Repository: yargs/yargs-parser
Pull request #153 (2018-12-25 02:37:29)

fix: better handling of quoted strings
addresses #145 and related issues, by no longer consuming quotes during the string tokenization phase, we now instead cleanup strings post-hoc.

BREAKING CHANGE: quotes at beginning and end of argument values are now removed.

bcoe: CC: @iilei, @tristanMatthias



Patch:
diff --git a/example.js b/example.js
index 1c1ea63..cbfe167 100755
--- a/example.js
+++ b/example.js
@@ -1,3 +1,3 @@
 var parser = require('./')
-var parse = parser(['-cats', 'meow'])
+var parse = parser('--foo "-bar"')
 console.log(parse)
diff --git a/index.js b/index.js
index 69fa72d..b628e7d 100644
--- a/index.js
+++ b/index.js
@@ -9,6 +9,7 @@ function parse (args, opts) {
   // allow a string argument to be passed in rather
   // than an argv array.
   args = tokenizeArgString(args)
+
   // aliases might have transitive relationships, normalize this.
   var aliases = combineAliases(opts.alias || {})
   var configuration = assign({
@@ -450,6 +451,14 @@ function parse (args, opts) {
   }
 
   function processValue (key, val) {
+    // strings may be quoted, clean this up as we assign values.
+    if (typeof val === 'string' &&
+      (val[0] === "'" || val[0] === '"') &&
+      val[val.length - 1] === val[0]
+    ) {
+      val = val.substring(1, val.length - 1)
+    }
+
     // handle parsing boolean arguments --foo=true --bar false.
     if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) {
       if (typeof val === 'string') val = val === 'true'
diff --git a/lib/tokenize-arg-string.js b/lib/tokenize-arg-string.js
index 73e14cd..569f61a 100644
--- a/lib/tokenize-arg-string.js
+++ b/lib/tokenize-arg-string.js
@@ -27,10 +27,8 @@ module.exports = function (argString) {
     if (c === opening) {
       if (!args[i]) args[i] = ''
       opening = null
-      continue
     } else if ((c === "'" || c === '"') && !opening) {
       opening = c
-      continue
     }
 
     if (!args[i]) args[i] = ''


Test patch:
diff --git a/test/tokenize-arg-string.js b/test/tokenize-arg-string.js
index 8bb6a95..e33c246 100644
--- a/test/tokenize-arg-string.js
+++ b/test/tokenize-arg-string.js
@@ -15,42 +15,42 @@ describe('TokenizeArgString', function () {
   it('handles quoted string with no spaces', function () {
     var args = tokenizeArgString("--foo 'hello'")
     args[0].should.equal('--foo')
-    args[1].should.equal('hello')
+    args[1].should.equal("'hello'")
   })
 
   it('handles single quoted string with spaces', function () {
     var args = tokenizeArgString("--foo 'hello world' --bar='foo bar'")
     args[0].should.equal('--foo')
-    args[1].should.equal('hello world')
-    args[2].should.equal('--bar=foo bar')
+    args[1].should.equal("'hello world'")
+    args[2].should.equal("--bar='foo bar'")
   })
 
   it('handles double quoted string with spaces', function () {
     var args = tokenizeArgString('--foo "hello world" --bar="foo bar"')
     args[0].should.equal('--foo')
-    args[1].should.equal('hello world')
-    args[2].should.equal('--bar=foo bar')
+    args[1].should.equal('"hello world"')
+    args[2].should.equal('--bar="foo bar"')
   })
 
   it('handles single quoted empty string', function () {
     var args = tokenizeArgString('--foo \'\' --bar=\'\'')
     args[0].should.equal('--foo')
-    args[1].should.equal('')
-    args[2].should.equal('--bar=')
+    args[1].should.equal("''")
+    args[2].should.equal("--bar=''")
   })
 
   it('handles double quoted empty string', function () {
     var args = tokenizeArgString('--foo "" --bar=""')
     args[0].should.equal('--foo')
-    args[1].should.equal('')
-    args[2].should.equal('--bar=')
+    args[1].should.equal('""')
+    args[2].should.equal('--bar=""')
   })
 
   it('handles quoted string with embeded quotes', function () {
     var args = tokenizeArgString('--foo "hello \'world\'" --bar=\'foo "bar"\'')
     args[0].should.equal('--foo')
-    args[1].should.equal('hello \'world\'')
-    args[2].should.equal('--bar=foo "bar"')
+    args[1].should.equal('"hello \'world\'"')
+    args[2].should.equal('--bar=\'foo "bar"\'')
   })
 
   // https://github.com/yargs/yargs-parser/pull/100
@@ -59,6 +59,6 @@ describe('TokenizeArgString', function () {
     var args = tokenizeArgString('  foo  bar  "foo  bar"  ')
     args[0].should.equal('foo')
     expect(args[1]).equal('bar')
-    expect(args[2]).equal('foo  bar')
+    expect(args[2]).equal('"foo  bar"')
   })
 })
diff --git a/test/yargs-parser.js b/test/yargs-parser.js
index f642093..ea9f70e 100644
--- a/test/yargs-parser.js
+++ b/test/yargs-parser.js
@@ -2796,4 +2796,31 @@ describe('yargs-parser', function () {
       argv.foo[3].bla.should.equal('banana')
     })
   })
+
+  // see: https://github.com/yargs/yargs-parser/issues/145
+  describe('strings with quotes and dashes', () => {
+    it('handles double quoted strings', function () {
+      const args = parser('--foo "hello world" --bar="goodnight\'moon"')
+      args.foo.should.equal('hello world')
+      args.bar.should.equal('goodnight\'moon')
+      const args2 = parser(['--foo', '"hello world"', '--bar="goodnight\'moon"'])
+      args2.foo.should.equal('hello world')
+      args2.bar.should.equal('goodnight\'moon')
+    })
+
+    it('handles single quoted strings', function () {
+      const args = parser("--foo 'hello world' --bar='goodnight\"moon'")
+      args.foo.should.equal('hello world')
+      args.bar.should.equal('goodnight"moon')
+      const args2 = parser(['--foo', "'hello world'", "--bar='goodnight\"moon'"])
+      args2.foo.should.equal('hello world')
+      args2.bar.should.equal('goodnight"moon')
+    })
+
+    it('handles strings with dashes', function () {
+      const args = parser('--foo "-hello world" --bar="--goodnight moon"')
+      args.foo.should.equal('-hello world')
+      args.bar.should.equal('--goodnight moon')
+    })
+  })
 })