Repository: ember-cli/ember-cli (js)

Issue:
Formatting empty string arguments (and other things that requires shell escape)
The `--help` for a command like this...

```js
module.exports = {
  description: 'Generates a thing.',

  availableOptions: [
    {
      name: 'something',
      type: ['', 'foo', 'bar'], // setting to empty string is allowed
      default: '',
      aliases: [{ 'no-something': '' }],
    }
  ],

  // ...
};
```

Looks like...

```
        Generates a thing.
        --something (, foo, bar) (Default: )
          aliases: --no-something (--something=)
```

This is a bit hard to read. I suggest that we do this:

```
        Generates a thing.
        --something ("", foo, bar) (Default: "")
          aliases: --no-something (--something="")
```

Technically, this problem applies for other special characters that needs escaping too... but empty string is probably much more common so that would be a start. `ember generate component --help` has this problem today (`--path=`), but we are about this introduce more instances of this problem with [RFC #481](https://emberjs.github.io/rfcs/0481-component-templates-co-location.html#generator).

Pull request description:
Quote empty strings in printCommand
Fixes #8741 (the empty string case)

Patch:
diff --git a/lib/utilities/print-command.js b/lib/utilities/print-command.js
index 229cb0903..9230c8232 100644
--- a/lib/utilities/print-command.js
+++ b/lib/utilities/print-command.js
@@ -67,7 +67,7 @@ module.exports = function(initialMargin, shouldDescriptionBeGrey) {
     }
 
     if (option.default !== undefined) {
-      output += ` ${chalk.cyan(`(Default: ${option.default})`)}`;
+      output += ` ${chalk.cyan(`(Default: ${formatValue(option.default)})`)}`;
     }
 
     if (option.description) {
@@ -82,7 +82,7 @@ module.exports = function(initialMargin, shouldDescriptionBeGrey) {
               return (a.length > 4 ? '--' : '-') + a + (option.type === Boolean ? '' : ' <value>');
             } else {
               let key = Object.keys(a)[0];
-              return `${(key.length > 4 ? '--' : '-') + key} (--${option.name}=${a[key]})`;
+              return `${(key.length > 4 ? '--' : '-') + key} (--${option.name}=${formatValue(a[key])})`;
             }
           })
           .join(', ')}`
@@ -94,5 +94,9 @@ module.exports = function(initialMargin, shouldDescriptionBeGrey) {
 };
 
 function formatType(type) {
-  return typeof type === 'string' ? type : type.name;
+  return typeof type === 'string' ? formatValue(type) : type.name;
+}
+
+function formatValue(val) {
+  return val === '' ? '""' : val;
 }


Test patch:
diff --git a/tests/fixtures/help/help-with-addon.txt b/tests/fixtures/help/help-with-addon.txt
index 708c2f534..1f10bbd5a 100644
--- a/tests/fixtures/help/help-with-addon.txt
+++ b/tests/fixtures/help/help-with-addon.txt
@@ -93,7 +93,7 @@ ember init \u001b[33m<glob-pattern>\u001b[39m \u001b[36m<options...>\u001b[39m
     \u001b[90maliases: -sb\u001b[39m
   \u001b[36m--welcome\u001b[39m \u001b[36m(Boolean)\u001b[39m \u001b[36m(Default: true)\u001b[39m Installs and uses {{ember-welcome-page}}. Use --no-welcome to skip it.
   \u001b[36m--yarn\u001b[39m \u001b[36m(Boolean)\u001b[39m
-  \u001b[36m--name\u001b[39m \u001b[36m(String)\u001b[39m \u001b[36m(Default: )\u001b[39m
+  \u001b[36m--name\u001b[39m \u001b[36m(String)\u001b[39m \u001b[36m(Default: "")\u001b[39m
     \u001b[90maliases: -n <value>\u001b[39m
 
 ember install \u001b[33m<addon-name>\u001b[39m \u001b[36m<options...>\u001b[39m
diff --git a/tests/fixtures/help/help.txt b/tests/fixtures/help/help.txt
index 3567d205f..555f5d315 100644
--- a/tests/fixtures/help/help.txt
+++ b/tests/fixtures/help/help.txt
@@ -93,7 +93,7 @@ ember init \u001b[33m<glob-pattern>\u001b[39m \u001b[36m<options...>\u001b[39m
     \u001b[90maliases: -sb\u001b[39m
   \u001b[36m--welcome\u001b[39m \u001b[36m(Boolean)\u001b[39m \u001b[36m(Default: true)\u001b[39m Installs and uses {{ember-welcome-page}}. Use --no-welcome to skip it.
   \u001b[36m--yarn\u001b[39m \u001b[36m(Boolean)\u001b[39m
-  \u001b[36m--name\u001b[39m \u001b[36m(String)\u001b[39m \u001b[36m(Default: )\u001b[39m
+  \u001b[36m--name\u001b[39m \u001b[36m(String)\u001b[39m \u001b[36m(Default: "")\u001b[39m
     \u001b[90maliases: -n <value>\u001b[39m
 
 ember install \u001b[33m<addon-name>\u001b[39m \u001b[36m<options...>\u001b[39m
diff --git a/tests/unit/utilities/print-command-test.js b/tests/unit/utilities/print-command-test.js
index c11765424..5795e38ed 100644
--- a/tests/unit/utilities/print-command-test.js
+++ b/tests/unit/utilities/print-command-test.js
@@ -84,4 +84,25 @@ describe('printCommand', function() {
 
     expect(output).to.equal(testString);
   });
+
+  it('quotes empty strings', function() {
+    let output = printCommand.call({
+      availableOptions: [
+        {
+          name: 'type',
+          type: ['', 'foo', 'bar'], // setting to empty string is allowed
+          default: '',
+          aliases: [{ 'no-type': '' }],
+        },
+      ],
+      anonymousOptions: [],
+    });
+
+    let testString = processHelpString(`\
+ \u001b[36m<options...>\u001b[39m${EOL}\
+  \u001b[36m--type\u001b[39m \u001b[36m("", foo, bar)\u001b[39m \u001b[36m(Default: "")\u001b[39m${EOL}\
+    \u001b[90maliases: --no-type (--type="")\u001b[39m`);
+
+    expect(output).to.equal(testString);
+  });
 });