Repository: ember-cli/ember-cli
Pull request #8588 (2019-04-13 22:52:47)

[dx] Detail app / addon creation messages
**Description**
Creating an app (or an addon) will take a little time. This PR aims at improving the messaging displayed to the developer while the creation is ongoing.

I'm not a native speaker, so do not hesitate to correct / improve the messages' wording.

**Emoji support**
This PR uses a few emojis. By default, they won't be displayed in PowerShell and in non-TTY environments. This PR also introduces a `--no-emoji` flag that does what it tells (usefull to turn emojis off in CI if the user'd like to do so)!

**Manual Testing**
```sh
npx github:dcyriller/ember-cli#improve-create-new-app new ember-app
```

**Preview**
AFTER (timings and colors are not 100% faithful):
![render1555194608097](https://user-images.githubusercontent.com/6677373/56085975-1e93b480-5e4d-11e9-8c80-d0d0e783d6ee.gif)

BEFORE:
![render1555194219536](https://user-images.githubusercontent.com/6677373/56085908-08d1bf80-5e4c-11e9-89b6-7545b32aaf58.gif)



kellyselden: These look really nice! Do you happen to know how those images show up in Windows shells?
dcyriller: I've run these commands in Windows' Powershell!... And the emoji are not properly rendered (colors are fine though). But so does `yarn` emojis', so... 🤷‍♂️ 
dcyriller: PowerShell don't support 
dcyriller: @kellyselden you may be interested by the 2nd commit I've added (I've updated the PR description) 😉 
rwjblue: Nice! I like it!!

Patch:
diff --git a/blueprints/addon/index.js b/blueprints/addon/index.js
index 0ee08b4b7..6b0c62a74 100644
--- a/blueprints/addon/index.js
+++ b/blueprints/addon/index.js
@@ -3,6 +3,7 @@
 const fs = require('fs-extra');
 const path = require('path');
 const walkSync = require('walk-sync');
+const chalk = require('chalk');
 const stringUtil = require('ember-cli-string-utils');
 const uniq = require('ember-cli-lodash-subset').uniq;
 const SilentError = require('silent-error');
@@ -103,6 +104,15 @@ module.exports = {
     return new FileInfo(options);
   },
 
+  beforeInstall() {
+    const version = require('../../package.json').version;
+    const prependEmoji = require('../../lib/utilities/prepend-emoji');
+
+    this.ui.writeLine(chalk.blue(`Ember CLI v${version}`));
+    this.ui.writeLine('');
+    this.ui.writeLine(prependEmoji('✨', `Creating a new Ember addon in ${chalk.yellow(process.cwd())}:`));
+  },
+
   afterInstall() {
     let packagePath = path.join(this.path, 'files', 'package.json');
     let bowerPath = path.join(this.path, 'files', 'bower.json');
diff --git a/blueprints/app/index.js b/blueprints/app/index.js
index 9e8060e64..d8aaf3316 100644
--- a/blueprints/app/index.js
+++ b/blueprints/app/index.js
@@ -1,6 +1,7 @@
 'use strict';
 
 const stringUtil = require('ember-cli-string-utils');
+const chalk = require('chalk');
 
 module.exports = {
   description: 'The default blueprint for ember-cli projects.',
@@ -30,4 +31,13 @@ module.exports = {
       blueprint: 'app',
     };
   },
+
+  beforeInstall() {
+    const version = require('../../package.json').version;
+    const prependEmoji = require('../../lib/utilities/prepend-emoji');
+
+    this.ui.writeLine(chalk.blue(`Ember CLI v${version}`));
+    this.ui.writeLine('');
+    this.ui.writeLine(prependEmoji('✨', `Creating a new Ember app in ${chalk.yellow(process.cwd())}:`));
+  },
 };
diff --git a/lib/commands/init.js b/lib/commands/init.js
index 0890a9006..4f2bb1505 100644
--- a/lib/commands/init.js
+++ b/lib/commands/init.js
@@ -5,6 +5,7 @@ const merge = require('ember-cli-lodash-subset').merge;
 const Command = require('../models/command');
 const Promise = require('rsvp').Promise;
 const SilentError = require('silent-error');
+const chalk = require('chalk');
 const validProjectName = require('../utilities/valid-project-name');
 const normalizeBlueprint = require('../utilities/normalize-blueprint-option');
 const mergeBlueprintOptions = require('../utilities/merge-blueprint-options');
@@ -104,6 +105,20 @@ module.exports = Command.extend({
         if (commandOptions.skipGit === false) {
           return this.runTask('GitInit', commandOptions, rawArgs);
         }
+      })
+      .then(() => {
+        const projectName = this.project.name();
+        const prependEmoji = require('../../lib/utilities/prepend-emoji');
+
+        this.ui.writeLine('');
+        this.ui.writeLine(prependEmoji('🎉', `Successfully created project ${chalk.yellow(projectName)}.`));
+        this.ui.writeLine(prependEmoji('👉 ', 'We suggest you that you get started by typing:'));
+        this.ui.writeLine('');
+        const command = `cd ${projectName}`;
+        this.ui.writeLine(`  ${chalk.gray('$')} ${chalk.cyan(command)}`);
+        this.ui.writeLine(`  ${chalk.gray('$')} ${chalk.cyan('yarn start')}`);
+        this.ui.writeLine('');
+        this.ui.writeLine('Happy coding!');
       });
   },
 });
diff --git a/lib/tasks/git-init.js b/lib/tasks/git-init.js
index 5e5f19d53..a120906c4 100644
--- a/lib/tasks/git-init.js
+++ b/lib/tasks/git-init.js
@@ -25,10 +25,13 @@ module.exports = class GitInitTask extends Task {
     if (!hasGit) {
       return;
     }
+    const prependEmoji = require('../../lib/utilities/prepend-emoji');
+    ui.writeLine('');
+    ui.writeLine(prependEmoji('🎥', 'Initializing git repository.'));
     await this._gitInit();
     await this._gitAdd();
     await this._gitCommit();
-    ui.writeLine(chalk.green('Successfully initialized git.'));
+    ui.writeLine(chalk.green('Git: successfully initialized.'));
   }
 
   _gitVersion() {
diff --git a/lib/tasks/npm-task.js b/lib/tasks/npm-task.js
index d49642790..29f2d0033 100644
--- a/lib/tasks/npm-task.js
+++ b/lib/tasks/npm-task.js
@@ -151,6 +151,10 @@ class NpmTask extends Task {
       let startMessage = this.formatStartMessage(options.packages);
       let completeMessage = this.formatCompleteMessage(options.packages);
 
+      const prependEmoji = require('../../lib/utilities/prepend-emoji');
+
+      ui.writeLine('');
+      ui.writeLine(prependEmoji('🚧', 'Installing packages... This might take a couple of minutes.'));
       ui.startProgress(chalk.green(startMessage));
 
       let promise;
diff --git a/lib/utilities/prepend-emoji.js b/lib/utilities/prepend-emoji.js
new file mode 100644
index 000000000..bb10999f4
--- /dev/null
+++ b/lib/utilities/prepend-emoji.js
@@ -0,0 +1,12 @@
+'use strict';
+
+function supportEmoji() {
+  const hasEmojiTurnedOff = process.argv.indexOf('--no-emoji') > -1;
+  return process.stdout.isTTY && process.platform !== 'win32' && !hasEmojiTurnedOff;
+}
+
+const areEmojiSupported = supportEmoji();
+
+module.exports = function prependEmoji(emoji, msg) {
+  return areEmojiSupported ? `${emoji}  ${msg}` : msg;
+};


Test patch:
diff --git a/tests/unit/tasks/git-init-test.js b/tests/unit/tasks/git-init-test.js
index 5591b1e6e..104d29bdc 100644
--- a/tests/unit/tasks/git-init-test.js
+++ b/tests/unit/tasks/git-init-test.js
@@ -38,7 +38,7 @@ describe('git-init', function() {
       await task.run({
         skipGit: true,
       });
-      expect(task.ui.output).to.not.include('Successfully initialized git.');
+      expect(task.ui.output).to.not.include('Git:.');
       td.verify(task._gitVersion(), { times: 0 });
     });
   });
@@ -55,7 +55,7 @@ describe('git-init', function() {
     td.verify(task._gitAdd());
     td.verify(task._gitCommit());
 
-    expect(task.ui.output).to.contain('Successfully initialized git.');
+    expect(task.ui.output).to.contain('Git: successfully initialized.');
     expect(task.ui.errors).to.equal('');
   });