Repository: ember-cli/ember-cli
Pull request #7488 (2017-12-06 14:49:43)

Add `module-unification-app` blueprint
This is the first step in a series of PRs which will bring support for Module Unification blueprints and generators.

An app using the module unification layout can now be created as follows:

`MODULE_UNIFICATION=true ember new my-app`
`MODULE_UNIFICATION=true ember new my-app --yarn`

Once this lands, https://github.com/emberjs/ember-module-unification-blueprint can be removed

TODO:

 * [x] add module unification `app` blueprint (extracted from https://github.com/emberjs/ember-module-unification-blueprint)
 * [x] tests
 * [x] address feedback

TODO in future PRs:

 * support `init` command
 * support `generate` commands
 * support addons
 * reduce the duplication between classic and MU blueprints (see https://github.com/ember-cli/ember-cli/pull/7488#issuecomment-349715930)
 * remove bower and user ember canary in package.json
 * replace `{{welcome-page}}` addon with component example

rwjblue: > support addons

@mansona and I have a spike of what the output for addons should look like in https://github.com/stonecircle/ember-addon-output/tree/feature/module-unification-initial-work, hopefully that will help with adding addon support with the env flag...
GavinJoyce: Thanks for the feedback @rwjblue. I copied over the content of https://github.com/emberjs/ember-module-unification-blueprint directly and didn't consider making any changes to the content of those files. I'll address your feedback this evening, thanks
GavinJoyce: > I am getting concerned about maintenance of both blueprints over time with the structure here in the PR. Do you think we could refactor things a bit such that we only have to maintain the differences from the main app blueprint? I think that would dramatically reduce the total maintenance burden if we can swing it.

That's interesting, I'll have a think about how we might solve this. Do we know how long do we expect to have to maintain the two blueprints for? 
GavinJoyce: A possible solution would be to define the shared files in one path and the specific files in another, something like:

```
blueprints/shared/app
blueprints/app
blueprints/module-unification-app
```

The `app` blueprint would be the merge of `blueprints/shared/app` & `blueprints/app`. The `module-unification-app` blueprint would be the merge of `blueprints/shared/app` & `blueprints/module-unification-app`.

We'd need to make some changes to how we test too.

@rwjblue what do you think of this approach? 
rwjblue: In general, it seems good to me.

FWIW,  I do agree with your earlier point too (around adding complexity for a temp situation), I’m just not sure how to guess how long we will need to support both. I suspect it will be at least a couple of releases...

 I’m mostly just trying to set us up for success and I worry that during the time where we maintain both we might accidentally forget to keep them in sync properly and therefore make the end-user tester experience much worse...
GavinJoyce: How about we live with the duplication for the short term and build out support for module unification `app`, `addon` and generators first. Once we have that, we'll have a better picture of the overlap and we can then make a decision to either to begin work on removing the duplication or to defer the work until a little later.

This will allow me to initially focus on landing mu support in blueprints
rwjblue: Ok, sounds good to me. 
GavinJoyce: @rwjblue I've addressed your feedback. 

Here's a repo with the current output of `MODULE_UNIFICATION=true ember new module-unification-blueprint-app-output`:

https://github.com/GavinJoyce/module-unification-blueprint-app-output

<img width="1438" alt="screen shot 2017-12-07 at 13 54 03" src="https://user-images.githubusercontent.com/2526/33718668-24aa7712-db56-11e7-97c2-a213bad1f354.png">
<img width="1440" alt="screen shot 2017-12-07 at 13 54 09" src="https://user-images.githubusercontent.com/2526/33718669-24caeccc-db56-11e7-90b8-282563c9b739.png">

(NOTE: `{{welcome-message}}` component doesn't seem to render)
GavinJoyce: @rwjblue I've addressed your feedback in https://github.com/ember-cli/ember-cli/pull/7488/commits/f8157a5786a0fc3778d36465c7eb3c55fd61d255 (I'll squash when we're happy).

I've tested the output with and without yarn and all looks good:

`MODULE_UNIFICATION=true .ember new module-unification-blueprint-app-output --yarn`

https://travis-ci.org/GavinJoyce/module-unification-blueprint-app-output (🍏 )
https://github.com/GavinJoyce/module-unification-blueprint-app-output

`MODULE_UNIFICATION=true .ember new module-unification-blueprint-app-yarn-output --yarn`

https://travis-ci.org/GavinJoyce/module-unification-blueprint-app-yarn-output (🍏 )
https://github.com/GavinJoyce/module-unification-blueprint-app-yarn-output
ro0gr: @GavinJoyce looks like "[blueprints/module-unification-app/files/yarn.lock](https://github.com/GavinJoyce/ember-cli/blob/f8157a5786a0fc3778d36465c7eb3c55fd61d255/blueprints/module-unification-app/files/yarn.lock)" added by mistake(couldn't find a way to inline comment in empty file)
ro0gr: > Do you think we could refactor things a bit such that we only have to maintain the differences from the main app blueprint? I think that would dramatically reduce the total maintenance burden if we can swing it.

We already have a [nice example](https://github.com/ember-cli/ember-cli/blob/bfb153d703e28b608a8252c702e8d6a011a76393/blueprints/addon/index.js#L128) for sharing files between `app` and `addon` blueprints. 
I think if we share at least files root files(.eslintrc, .editorconfig, testem.js, etc) it would be a win.

I'll take a look in this direction and report back.

ro0gr: I've done some experimentation to reduce amount files to maintain #7500
Implementation still can be improved but it looks like `addon` blueprint's approach works for MU blueprint as well.
GavinJoyce: > I've done some experimentation to reduce amount of files to maintain #7500
Implementation still can be improved but it looks like addon blueprint's approach works for MU blueprint as well.

@ro0gr nice one, this will make sharing files across blueprints easier (we'll still need to change how we test the output, as you noted in your PR). I'm still not clear which is better, 1) maintaining two separate trees and living with duplication for a relatively short amount of time or 2) using your technique with `this.lookupBlueprint('app').files();` to share a common base. I think it would be good to continue to use 1) in the short term and wait for some data before making an informed decision to jump to 2).
ro0gr: >  I'm still not clear which is better, 1) maintaining two separate trees and living with duplication for a relatively short amount of time or 2) using your technique with this.lookupBlueprint('app').files(); to share a common base

@GavinJoyce I'm not sure how short is the "short term" you refer to. But it's obvious that duplication of large amount of files rarely leads to a good consequences. It's quite complicated to sync changes in +24 files(that's an amount of files we drop if share app blueprint files).

I think even now we might have syncing issues. Here is the list of files that are different between `app` and `module-unification-app` blueprints:

- https://github.com/GavinJoyce/ember-cli/blob/9202add03f1fd398abae3cf54e73b225991ab583/blueprints/module-unification-app/files/.travis.yml
- https://github.com/GavinJoyce/ember-cli/blob/9202add03f1fd398abae3cf54e73b225991ab583/blueprints/module-unification-app/files/testem.js

I'm not sure if that is intentional but it's much more convenient to have only those parts which are actually different even in a short term.

So I would vote to consider sharing files at least because technically it's the same approach we already use for `addon` blueprint, it seems to work and I can't see any obvious drawbacks with this approach. 


Patch:
diff --git a/.gitignore b/.gitignore
index ce4b021c3..fd73cc489 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ npm-debug.log
 .deps-tmp
 /coverage
 *.pem
+.DS_Store
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e368fd34e..a2e5e0ca5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,29 +1,5 @@
 # ember-cli Changelog
 
-## 2.18.0-beta.2
-
-The following changes are required if you are upgrading from the previous
-version:
-
-- Users
-  + [`ember new` diff](https://github.com/ember-cli/ember-new-output/compare/v2.18.0-beta.1...v2.18.0-beta.2)
-  + Upgrade your project's ember-cli version - [docs](https://ember-cli.com/user-guide/#upgrading)
-- Addon Developers
-  + [`ember new` diff](https://github.com/ember-cli/ember-addon-output/compare/v2.18.0-beta.1...v2.18.0-beta.2)
-- Core Contributors
-  + No changes required
-
-#### Community Contributions
-
-- [#7474](https://github.com/ember-cli/ember-cli/pull/7474) Give plugins and extends their own lines [@rwwagner90](https://github.com/rwwagner90)
-- [#7475](https://github.com/ember-cli/ember-cli/pull/7475) don't treat strings as regex in insertIntoFile [@kellyselden](https://github.com/kellyselden)
-- [#7477](https://github.com/ember-cli/ember-cli/pull/7477) Restore `separator: '\n;'` to vendor JS concat [@kellyselden/lenny](https://github.com/kellyselden/lenny)
-- [#7478](https://github.com/ember-cli/ember-cli/pull/7478) Remove obsolete CONFIG_CACHING feature flag [@Turbo87](https://github.com/Turbo87)
-- [#7481](https://github.com/ember-cli/ember-cli/pull/7481) NpmInstallTask: `useYarn` from constructor args [@lennyburdette](https://github.com/lennyburdette)
-
-Thank you to all who took the time to contribute!
-
-
 ## 2.18.0-beta.1
 
 The following changes are required if you are upgrading from the previous
@@ -61,30 +37,6 @@ version:
 Thank you to all who took the time to contribute!
 
 
-## 2.17.1
-
-The following changes are required if you are upgrading from the previous
-version:
-
-- Users
-  + [`ember new` diff](https://github.com/ember-cli/ember-new-output/compare/v2.17.0...v2.17.1)
-  + Upgrade your project's ember-cli version - [docs](https://ember-cli.com/user-guide/#upgrading)
-- Addon Developers
-  + [`ember new` diff](https://github.com/ember-cli/ember-addon-output/compare/v2.17.0...v2.17.1)
-  + No changes required
-- Core Contributors
-  + No changes required
-
-#### Community Contributions
-
-- [#7475](https://github.com/ember-cli/ember-cli/pull/7475) don't treat strings as regex in insertIntoFile [@kellyselden](https://github.com/kellyselden)
-- [#7477](https://github.com/ember-cli/ember-cli/pull/7477) Restore `separator: '\n;'` to vendor JS concat [@lennyburdette](https://github.com/lennyburdette)
-- [#7478](https://github.com/ember-cli/ember-cli/pull/7478) Remove obsolete CONFIG_CACHING feature flag [@Turbo87](https://github.com/Turbo87)
-- [#7481](https://github.com/ember-cli/ember-cli/pull/7481) NpmInstallTask: `useYarn` from constructor args [@lennyburdette](https://github.com/lennyburdette)
-
-Thank you to all who took the time to contribute!
-
-
 ## 2.17.0
 
 The following changes are required if you are upgrading from the previous
diff --git a/blueprints/addon/files/README.md b/blueprints/addon/files/README.md
index 292815c90..bed39d157 100644
--- a/blueprints/addon/files/README.md
+++ b/blueprints/addon/files/README.md
@@ -1,46 +1,26 @@
-<%= addonName %>
-==============================================================================
+# <%= addonName %>
 
-[Short description of the addon.]
+This README outlines the details of collaborating on this Ember addon.
 
-Installation
-------------------------------------------------------------------------------
+## Installation
 
-```
-ember install <%= addonName %>
-```
-
-
-Usage
-------------------------------------------------------------------------------
-
-[Longer description of how to use the addon in apps.]
-
-
-Contributing
-------------------------------------------------------------------------------
-
-### Installation
-
-* `git clone <repository-url>`
+* `git clone <repository-url>` this repository
 * `cd <%= addonName %>`
 * `<% if (yarn) { %>yarn<% } else { %>npm<% } %> install`
 
-### Running tests
-
-* `ember test` – Runs the test suite on the current Ember version
-* `ember test --server` – Runs the test suite in "watch mode"
-* `<% if (yarn) { %>yarn<% } else { %>npm<% } %> test` – Runs `ember try:each` to test your addon against multiple Ember versions
-
-### Running the dummy application
+## Running
 
 * `ember serve`
-* Visit the dummy application at [http://localhost:4200](http://localhost:4200).
+* Visit your app at [http://localhost:4200](http://localhost:4200).
 
-For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
+## Running Tests
+
+* `<% if (yarn) { %>yarn<% } else { %>npm<% } %> test` (Runs `ember try:each` to test your addon against multiple Ember versions)
+* `ember test`
+* `ember test --server`
 
+## Building
 
-License
-------------------------------------------------------------------------------
+* `ember build`
 
-This project is licensed under the [MIT License](LICENSE.md).
+For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
diff --git a/blueprints/app/files/config/environment.js b/blueprints/app/files/config/environment.js
index 413420ce4..1e14621c9 100644
--- a/blueprints/app/files/config/environment.js
+++ b/blueprints/app/files/config/environment.js
@@ -40,7 +40,6 @@ module.exports = function(environment) {
     ENV.APP.LOG_VIEW_LOOKUPS = false;
 
     ENV.APP.rootElement = '#ember-testing';
-    ENV.APP.autoboot = false;
   }
 
   if (environment === 'production') {
diff --git a/blueprints/app/files/package.json b/blueprints/app/files/package.json
index 431c05005..a3be14cd8 100644
--- a/blueprints/app/files/package.json
+++ b/blueprints/app/files/package.json
@@ -34,7 +34,7 @@
     "ember-export-application-global": "^2.0.0",
     "ember-load-initializers": "^1.0.0",
     "ember-resolver": "^4.0.0",
-    "ember-source": "~2.18.0-beta.3<% if (welcome) { %>",
+    "ember-source": "~2.18.0-beta.1<% if (welcome) { %>",
     "ember-welcome-page": "^3.0.0<% } %>",
     "eslint-plugin-ember": "^5.0.0",
     "loader.js": "^4.2.3"
diff --git a/blueprints/module-unification-app/files/.editorconfig b/blueprints/module-unification-app/files/.editorconfig
new file mode 100644
index 000000000..219985c22
--- /dev/null
+++ b/blueprints/module-unification-app/files/.editorconfig
@@ -0,0 +1,20 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.hbs]
+insert_final_newline = false
+
+[*.{diff,md}]
+trim_trailing_whitespace = false
diff --git a/blueprints/module-unification-app/files/.ember-cli b/blueprints/module-unification-app/files/.ember-cli
new file mode 100644
index 000000000..ee64cfed2
--- /dev/null
+++ b/blueprints/module-unification-app/files/.ember-cli
@@ -0,0 +1,9 @@
+{
+  /**
+    Ember CLI sends analytics information by default. The data is completely
+    anonymous, but there are times when you might want to disable this behavior.
+
+    Setting `disableAnalytics` to true will prevent any data from being sent.
+  */
+  "disableAnalytics": false
+}
diff --git a/blueprints/module-unification-app/files/.eslintrc.js b/blueprints/module-unification-app/files/.eslintrc.js
new file mode 100644
index 000000000..7d328870e
--- /dev/null
+++ b/blueprints/module-unification-app/files/.eslintrc.js
@@ -0,0 +1,48 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    ecmaVersion: 2017,
+    sourceType: 'module'
+  },
+  plugins: [
+    'ember'
+  ],
+  extends: [
+    'eslint:recommended',
+    'plugin:ember/recommended'
+  ],
+  env: {
+    browser: true
+  },
+  rules: {
+  },
+  overrides: [
+    // node files
+    {
+      files: [
+        'index.js',
+        'testem.js',
+        'ember-cli-build.js',
+        'config/**/*.js',
+        'tests/dummy/config/**/*.js'
+      ],
+      parserOptions: {
+        sourceType: 'script',
+        ecmaVersion: 2015
+      },
+      env: {
+        browser: false,
+        node: true
+      }
+    },
+
+    // test files
+    {
+      files: ['tests/**/*.js'],
+      excludedFiles: ['tests/dummy/**/*.js'],
+      env: {
+        embertest: true
+      }
+    }
+  ]
+};
diff --git a/blueprints/module-unification-app/files/.travis.yml b/blueprints/module-unification-app/files/.travis.yml
new file mode 100644
index 000000000..9fa264afc
--- /dev/null
+++ b/blueprints/module-unification-app/files/.travis.yml
@@ -0,0 +1,39 @@
+---
+language: node_js
+node_js:
+  - "6"
+
+sudo: false
+dist: trusty
+
+addons:
+  chrome: stable
+<% if (yarn) { %>
+cache:
+  yarn: true
+
+before_install:
+  - curl -o- -L https://yarnpkg.com/install.sh | bash
+  - export PATH=$HOME/.yarn/bin:$PATH
+
+install:
+  - yarn install --non-interactive
+
+before_script:
+  - npm install -g bower
+  - bower install
+
+script:
+  - yarn test
+<% } else { %>
+cache:
+  directories:
+    - $HOME/.npm
+
+before_install:
+  - npm config set spin false
+
+before_script:
+  - npm install -g bower
+  - bower install
+<% } %>
diff --git a/blueprints/module-unification-app/files/.watchmanconfig b/blueprints/module-unification-app/files/.watchmanconfig
new file mode 100644
index 000000000..e7834e3e4
--- /dev/null
+++ b/blueprints/module-unification-app/files/.watchmanconfig
@@ -0,0 +1,3 @@
+{
+  "ignore_dirs": ["tmp", "dist"]
+}
diff --git a/blueprints/module-unification-app/files/README.md b/blueprints/module-unification-app/files/README.md
new file mode 100644
index 000000000..0223e5eb5
--- /dev/null
+++ b/blueprints/module-unification-app/files/README.md
@@ -0,0 +1,51 @@
+# <%= name %>
+
+This README outlines the details of collaborating on this Ember application.
+A short introduction of this app could easily go here.
+
+## Prerequisites
+
+You will need the following things properly installed on your computer.
+
+* [Git](https://git-scm.com/)
+* [Node.js](https://nodejs.org/)<% if (yarn) { %>
+* [Yarn](https://yarnpkg.com/)<% } else { %> (with NPM)<% } %>
+* [Ember CLI](https://ember-cli.com/)
+* [Google Chrome](https://google.com/chrome/)
+
+## Installation
+
+* `git clone <repository-url>` this repository
+* `cd <%= name %>`
+* `<% if (yarn) { %>yarn<% } else { %>npm<% } %> install`
+
+## Running / Development
+
+* `ember serve`
+* Visit your app at [http://localhost:4200](http://localhost:4200).
+
+### Code Generators
+
+Make use of the many generators for code, try `ember help generate` for more details
+
+### Running Tests
+
+* `ember test`
+* `ember test --server`
+
+### Building
+
+* `ember build` (development)
+* `ember build --environment production` (production)
+
+### Deploying
+
+Specify what it takes to deploy your app.
+
+## Further Reading / Useful Links
+
+* [ember.js](https://emberjs.com/)
+* [ember-cli](https://ember-cli.com/)
+* Development Browser Extensions
+  * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
+  * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)
diff --git a/blueprints/module-unification-app/files/bower.json b/blueprints/module-unification-app/files/bower.json
new file mode 100644
index 000000000..14f746284
--- /dev/null
+++ b/blueprints/module-unification-app/files/bower.json
@@ -0,0 +1,6 @@
+{
+  "name": "<%= name %>",
+  "dependencies": {
+    "ember": "components/ember#canary"
+  }
+}
diff --git a/blueprints/module-unification-app/files/config/environment.js b/blueprints/module-unification-app/files/config/environment.js
new file mode 100644
index 000000000..6ef31e7fa
--- /dev/null
+++ b/blueprints/module-unification-app/files/config/environment.js
@@ -0,0 +1,56 @@
+'use strict';
+
+module.exports = function(environment) {
+  let ENV = {
+    'ember-resolver': {
+      features: {
+        EMBER_RESOLVER_MODULE_UNIFICATION: true
+      }
+    },
+    modulePrefix: '<%= modulePrefix %>',
+    environment,
+    rootURL: '/',
+    locationType: 'auto',
+    EmberENV: {
+      FEATURES: {
+        // Here you can enable experimental features on an ember canary build
+        // e.g. 'with-controller': true
+        'ember-module-unification': true
+      },
+      EXTEND_PROTOTYPES: {
+        // Prevent Ember Data from overriding Date.parse.
+        Date: false
+      }
+    },
+
+    APP: {
+      // Here you can pass flags/options to your application instance
+      // when it is created
+    }
+  };
+
+  if (environment === 'development') {
+    // ENV.APP.LOG_RESOLVER = true;
+    // ENV.APP.LOG_ACTIVE_GENERATION = true;
+    // ENV.APP.LOG_TRANSITIONS = true;
+    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
+    // ENV.APP.LOG_VIEW_LOOKUPS = true;
+  }
+
+  if (environment === 'test') {
+    // Testem prefers this...
+    ENV.locationType = 'none';
+
+    // keep test console output quieter
+    ENV.APP.LOG_ACTIVE_GENERATION = false;
+    ENV.APP.LOG_VIEW_LOOKUPS = false;
+
+    ENV.APP.rootElement = '#ember-testing';
+  }
+
+  if (environment === 'production') {
+
+  }
+
+  return ENV;
+};
diff --git a/blueprints/module-unification-app/files/config/targets.js b/blueprints/module-unification-app/files/config/targets.js
new file mode 100644
index 000000000..082e68b51
--- /dev/null
+++ b/blueprints/module-unification-app/files/config/targets.js
@@ -0,0 +1,8 @@
+module.exports = {
+  browsers: [
+    'ie 9',
+    'last 1 Chrome versions',
+    'last 1 Firefox versions',
+    'last 1 Safari versions'
+  ]
+};
diff --git a/blueprints/module-unification-app/files/ember-cli-build.js b/blueprints/module-unification-app/files/ember-cli-build.js
new file mode 100644
index 000000000..d690a2531
--- /dev/null
+++ b/blueprints/module-unification-app/files/ember-cli-build.js
@@ -0,0 +1,24 @@
+'use strict';
+
+const EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
+module.exports = function(defaults) {
+  let app = new EmberApp(defaults, {
+    // Add options here
+  });
+
+  // Use `app.import` to add additional libraries to the generated
+  // output files.
+  //
+  // If you need to use different assets in different
+  // environments, specify an object as the first parameter. That
+  // object's keys should be the environment name and the values
+  // should be the asset to use in that environment.
+  //
+  // If the library that you are including contains AMD or ES6
+  // modules that you would like to import into your application
+  // please specify an object with the list of modules as keys
+  // along with the exports of each module as its value.
+
+  return app.toTree();
+};
diff --git a/blueprints/module-unification-app/files/gitignore b/blueprints/module-unification-app/files/gitignore
new file mode 100644
index 000000000..8fa39a63c
--- /dev/null
+++ b/blueprints/module-unification-app/files/gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+
+# dependencies
+/node_modules
+/bower_components
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage/*
+/libpeerconnection.log
+npm-debug.log*
+yarn-error.log
+testem.log
+
+# ember-try
+.node_modules.ember-try/
+bower.json.ember-try
+package.json.ember-try
diff --git a/blueprints/module-unification-app/files/package.json b/blueprints/module-unification-app/files/package.json
new file mode 100644
index 000000000..54dbb2242
--- /dev/null
+++ b/blueprints/module-unification-app/files/package.json
@@ -0,0 +1,44 @@
+{
+  "name": "<%= name %>",
+  "version": "0.0.0",
+  "private": true,
+  "description": "Small description for <%= name %> goes here",
+  "license": "MIT",
+  "author": "",
+  "directories": {
+    "doc": "doc",
+    "test": "tests"
+  },
+  "repository": "",
+  "scripts": {
+    "build": "ember build",
+    "start": "ember server",
+    "test": "ember test"
+  },
+  "devDependencies": {
+    "broccoli-asset-rev": "^2.4.5",
+    "ember-ajax": "^3.0.0",
+    "ember-cli": "github:ember-cli/ember-cli",
+    "ember-cli-app-version": "^3.0.0",
+    "ember-cli-babel": "^6.6.0",
+    "ember-cli-dependency-checker": "^2.0.0",
+    "ember-cli-eslint": "^4.2.1",
+    "ember-cli-htmlbars": "^2.0.1",
+    "ember-cli-htmlbars-inline-precompile": "^1.0.0",
+    "ember-cli-inject-live-reload": "^1.4.1",
+    "ember-cli-qunit": "^4.1.1",
+    "ember-cli-shims": "^1.2.0",
+    "ember-cli-sri": "^2.1.0",
+    "ember-cli-uglify": "^2.0.0",
+    "ember-data": "~2.18.0-beta.1",
+    "ember-export-application-global": "^2.0.0",
+    "ember-load-initializers": "^1.0.0",
+    "ember-resolver": "^4.0.0<% if (welcome) { %>",
+    "ember-welcome-page": "^3.0.0<% } %>",
+    "eslint-plugin-ember": "^5.0.0",
+    "loader.js": "^4.2.3"
+  },
+  "engines": {
+    "node": "^4.5 || 6.* || >= 7.*"
+  }
+}
diff --git a/blueprints/module-unification-app/files/public/robots.txt b/blueprints/module-unification-app/files/public/robots.txt
new file mode 100644
index 000000000..f5916452e
--- /dev/null
+++ b/blueprints/module-unification-app/files/public/robots.txt
@@ -0,0 +1,3 @@
+# http://www.robotstxt.org
+User-agent: *
+Disallow:
diff --git a/blueprints/module-unification-app/files/src/main.js b/blueprints/module-unification-app/files/src/main.js
new file mode 100644
index 000000000..2a188be3b
--- /dev/null
+++ b/blueprints/module-unification-app/files/src/main.js
@@ -0,0 +1,14 @@
+import Application from "@ember/application";
+import Resolver from "./resolver";
+import loadInitializers from "ember-load-initializers";
+import config from "../config/environment";
+
+const App = Application.extend({
+  modulePrefix: config.modulePrefix,
+  podModulePrefix: config.podModulePrefix,
+  Resolver
+});
+
+loadInitializers(App, config.modulePrefix + "/src/init");
+
+export default App;
diff --git a/blueprints/module-unification-app/files/src/resolver.js b/blueprints/module-unification-app/files/src/resolver.js
new file mode 100644
index 000000000..f0b8a1cce
--- /dev/null
+++ b/blueprints/module-unification-app/files/src/resolver.js
@@ -0,0 +1,13 @@
+import Resolver from 'ember-resolver/resolvers/fallback';
+import buildResolverConfig from 'ember-resolver/ember-config';
+import config from '../config/environment';
+
+let moduleConfig = buildResolverConfig(config.modulePrefix);
+/*
+ * If your application has custom types and collections, modify moduleConfig here
+ * to add support for them.
+ */
+
+export default Resolver.extend({
+  config: moduleConfig
+});
diff --git a/blueprints/module-unification-app/files/src/router.js b/blueprints/module-unification-app/files/src/router.js
new file mode 100644
index 000000000..7f2298b0d
--- /dev/null
+++ b/blueprints/module-unification-app/files/src/router.js
@@ -0,0 +1,11 @@
+import EmberRouter from "@ember/routing/router";
+import config from "../config/environment";
+
+const Router = EmberRouter.extend({
+  location: config.locationType,
+  rootURL: config.rootURL
+});
+
+Router.map(function() {});
+
+export default Router;
diff --git a/blueprints/module-unification-app/files/src/ui/components/.gitkeep b/blueprints/module-unification-app/files/src/ui/components/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/blueprints/module-unification-app/files/src/ui/index.html b/blueprints/module-unification-app/files/src/ui/index.html
new file mode 100644
index 000000000..2567509b5
--- /dev/null
+++ b/blueprints/module-unification-app/files/src/ui/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <title><%= name %></title>
+    <meta name="description" content="">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    {{content-for "head"}}
+
+    <link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
+    <link rel="stylesheet" href="{{rootURL}}assets/<%= name %>.css">
+
+    {{content-for "head-footer"}}
+  </head>
+  <body>
+    {{content-for "body"}}
+
+    <script src="{{rootURL}}assets/vendor.js"></script>
+    <script src="{{rootURL}}assets/<%= name %>.js"></script>
+
+    {{content-for "body-footer"}}
+  </body>
+</html>
diff --git a/blueprints/module-unification-app/files/src/ui/routes/application/template.hbs b/blueprints/module-unification-app/files/src/ui/routes/application/template.hbs
new file mode 100644
index 000000000..8a64bd03d
--- /dev/null
+++ b/blueprints/module-unification-app/files/src/ui/routes/application/template.hbs
@@ -0,0 +1,6 @@
+<% if (welcome) { %>{{!-- The following component displays Ember's default welcome message. --}}
+{{welcome-page}}
+{{!-- Feel free to remove this! --}}
+<% } else { %><h2 id="title">Welcome to Ember</h2>
+<% } %>
+{{outlet}}
diff --git a/blueprints/module-unification-app/files/tests/unit/.gitkeep b/blueprints/module-unification-app/files/tests/unit/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/blueprints/module-unification-app/files/vendor/.gitkeep b/blueprints/module-unification-app/files/vendor/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/blueprints/module-unification-app/index.js b/blueprints/module-unification-app/index.js
new file mode 100644
index 000000000..278446398
--- /dev/null
+++ b/blueprints/module-unification-app/index.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const stringUtil = require('ember-cli-string-utils');
+
+module.exports = {
+  description: 'Generates an Ember application with a module unification layout.',
+
+  filesToRemove: [],
+
+  locals(options) {
+    let entity = options.entity;
+    let rawName = entity.name;
+    let name = stringUtil.dasherize(rawName);
+    let namespace = stringUtil.classify(rawName);
+
+    return {
+      name,
+      modulePrefix: name,
+      namespace,
+      emberCLIVersion: require('../../package').version,
+      yarn: options.yarn,
+      welcome: options.welcome,
+    };
+  },
+
+  fileMapTokens(options) {
+    return {
+      __component__() { return options.locals.component; },
+    };
+  },
+};
diff --git a/docs/node-support.md b/docs/node-support.md
index a9d42c6d6..c35187f55 100644
--- a/docs/node-support.md
+++ b/docs/node-support.md
@@ -1,5 +1,7 @@
 # Node Support
 
+* original proposal/discussion: https://github.com/ember-cli/rfcs/pull/47
+
 ## Supported Versions
 
 | Node LTS Version | Supported Ember CLI Versions |
@@ -13,33 +15,56 @@
 | 8.x              | 2.13.3 - Current             |
 | 9.x              | 2.16.2 - Current             |
 
-## Design
+# Design
 
 Commits to the `HEAD` of the master branch will provide support for any Active
-Node.js LTS and the current stable Node.js version(s).
+(not Maintenance Mode) Node.js LTS and the current stable Node.js version(s).
 This will be enforced via CI, preventing us from landing code which won't work
 with versions which we support. This means that our schedule for support is
 tied to the [LTS release schedule for
 Node.js](https://github.com/nodejs/LTS#lts_schedule).
 
-## Current support:
-
-* v4.2: Released as stable version then converted to LTS.
-  * Supported by ember-cli/ember-cli#master until: 2018-04-01
-* v5: Released as stable (not an LTS)
-  * Supported by ember-cli/ember-cli#master until: 2016-07-01.
-* v6: Released as stable version then converted to LTS. 
-  * Supported by ember-cli/ember-cli#master until: 2019-04-01.
-* v7: Released as stable (not an LTS)
-  * Supported by ember-cli/ember-cli#master until: 2017-07-01.
-* v8: Released as stable version then converted to LTS.
-  * Supported by ember-cli/ember-cli#master until: 2019-12-31
-* v9: Released as stable (not an LTS)
-  * Supported by ember-cli/ember-cli#master until: 2018-07-01.
-
-## Release Process and Support Policy
-
-Ember and Ember CLI have committed to supporting the [Node.js LTS schedule](https://github.com/nodejs/LTS#lts-schedule)
-for the `HEAD` of our `master` branch(es). This means that we will will drop support
-per the [Node.js Release Working Group](https://github.com/nodejs/Release)'s schedule without a major version
-bump/change of ember-cli itself.
+For our currently existing support of 0.10 and 0.12 we should drop support at
+the end of their Maintenance Mode–their official end-of-life from Node.js. This
+gives time for our community to migrate off of those environments which we
+presently support.
+
+## Legacy Support
+
+* v0.10: LTS Maintenance Mode ends on 2016-10-01. We drop all support on that date.
+* v0.12: LTS Maintenance Mode ends on 2017-04-01. We drop all support on that date.
+
+## Ongoing support per this RFC:
+
+These examples assume major version bumps. This is not required per Node.js
+policy in order to become an LTS.
+
+* v4.2: LTS Maintenance Mode begins on 2017-04-01. We drop support on that date
+  for new commits to `master`. _We must have an Ember CLI LTS strategy
+    specified by this date._
+* v5: Stable release, never LTS. Began support 2015-10-01. All support expires
+  on 2016-07-01.
+* v6: Released as Stable approximately 2016-04-01, converted to LTS Active
+  Support 2016-10-01. Begin support on 2016-04-01. Active support ends on
+  2018-04-01 at which point we drop support on that date for new commits to
+  `master`.
+* v7: Stable release, never LTS. Begin support upon release 2016-10-01. All
+  support expires on 2017-07-01.
+* v8: Released as stable on 2017-05-30, converted to LTS Active Support 2017-10-31.
+  Active support ends on 2019-12-31 at which point we drop support for new commits
+  to `master`.
+* v9: Stable release, never LTS. Begin support upon release 2017-11-01.
+
+# Release Process and Support Policy
+
+The Ember CLI release process and support policy is presently `undefined`. We
+are currently specifying a release process in RFC #46. We must specify a
+support policy no later than 2017-04-01. The support policy doesn't have to be
+complete before then as we will continue to 100% support all Node.js supported
+releases until that date under this RFC.
+
+This RFC is distinct from providing guarantees that some version of Ember CLI
+will run securely on each Node.js LTS release. Currently we provide no
+guarantees of support for any previously released version of Ember CLI–which is
+a gap we wish to correct and are working toward.
+
diff --git a/lib/commands/new.js b/lib/commands/new.js
index 3e4ed60b2..e80eb454c 100644
--- a/lib/commands/new.js
+++ b/lib/commands/new.js
@@ -37,13 +37,17 @@ module.exports = Command.extend({
   beforeRun: mergeBlueprintOptions,
 
   run(commandOptions, rawArgs) {
+    if (process.env.MODULE_UNIFICATION === 'true' && commandOptions.blueprint === 'app') {
+      commandOptions.blueprint = 'module-unification-app';
+    }
+
     let packageName = rawArgs[0],
         message;
 
     commandOptions.name = rawArgs.shift();
 
     if (!packageName) {
-      message = `The \`ember ${this.name}\` command requires a name to be specified. For more details, use \`ember help\`.`;
+      message = chalk.yellow(`The \`ember ${this.name}\` command requires a name to be specified. For more details, use \`ember help\`.`);
 
       return Promise.reject(new SilentError(message));
     }
diff --git a/lib/models/addon.js b/lib/models/addon.js
index 166d38f50..dc2eba819 100644
--- a/lib/models/addon.js
+++ b/lib/models/addon.js
@@ -33,7 +33,6 @@ const calculateCacheKeyForTree = require('calculate-cache-key-for-tree');
 const addonProcessTree = require('../utilities/addon-process-tree');
 const semver = require('semver');
 const processModulesOnly = require('../broccoli/babel-process-modules-only');
-const npa = require('npm-package-arg');
 
 const BUILD_BABEL_OPTIONS_FOR_PREPROCESSORS = Symbol('BUILD_BABEL_OPTIONS_FOR_PREPROCESSORS');
 
@@ -332,29 +331,7 @@ let addonProto = {
   */
   isDevelopingAddon() {
     if (process.env.EMBER_ADDON_ENV === 'development' && (this.parent instanceof Project)) {
-      const parentName = this.parent.name();
-      // If the name in package.json and index.js match, we're definitely developing
-      if (parentName === this.name) {
-        return true;
-      }
-
-      // If package.json and index.js names do not match, we should check for scoped vs non-scoped
-      const parsed = npa(parentName);
-      let parsedName = parsed.name;
-      if (parsed.scope) {
-        parsedName = parsedName.replace(parsed.scope, '').slice(1);
-      }
-      if (parsedName === this.name) {
-        if (!this._hasWarnedForMismatchedNames) {
-          this.ui.writeWarnLine(
-            'Your names in package.json and index.js should match. ' +
-            `You currently have ${parsed.name} in package.json and ${this.name} in index.js.`);
-
-          this._hasWarnedForMismatchedNames = true;
-        }
-
-        return true;
-      }
+      return this.parent.name() === this.name;
     }
     return false;
   },
diff --git a/lib/models/command.js b/lib/models/command.js
index eeb7e638e..1b45c832a 100644
--- a/lib/models/command.js
+++ b/lib/models/command.js
@@ -12,7 +12,7 @@ const _ = require('ember-cli-lodash-subset');
 const EOL = require('os').EOL;
 const CoreObject = require('core-object');
 const logger = require('heimdalljs-logger')('ember-cli:command');
-const WatchDetector = require('watch-detector');
+const WatchDetector = require('../models/watch-detector');
 const SilentError = require('silent-error');
 const execSync = require('child_process').execSync;
 const fs = require('fs');
diff --git a/lib/models/watch-detector.js b/lib/models/watch-detector.js
new file mode 100644
index 000000000..d5b3f8d00
--- /dev/null
+++ b/lib/models/watch-detector.js
@@ -0,0 +1,253 @@
+'use strict';
+
+const quickTemp = require('quick-temp');
+const Promise = require('rsvp').Promise;
+const WATCHMAN_INFO = 'Visit https://ember-cli.com/user-guide/#watchman for more info.';
+const semver = require('semver');
+const SilentError = require('silent-error');
+
+let POLLING = 'polling';
+let WATCHMAN = 'watchman';
+let NODE = 'node';
+let EVENTS = 'events';
+let POSSIBLE_WATCHERS = [
+  POLLING,
+  WATCHMAN,
+  NODE,
+  EVENTS,
+];
+
+let debug = require('heimdalljs-logger')('ember-cli:watcher');
+
+class WatchPreference {
+  constructor(watcher) {
+    this.watcher = watcher || null;
+    this._watchmanInfo = {
+      enabled: false,
+      version: null,
+      canNestRoots: false,
+    };
+  }
+
+  watchmanWorks(details) {
+    this._watchmanInfo.enabled = true;
+    this._watchmanInfo.version = details.version;
+    this._watchmanInfo.canNestRoots = semver.satisfies(details.version, '>= 3.7.0');
+  }
+
+  get watchmanInfo() {
+    return this._watchmanInfo;
+  }
+}
+
+/*
+ * @public
+ *
+ * A testable class that encapsulates which watch technique to use.
+ */
+class WatchDetector {
+  constructor(options) {
+    this.childProcess = options.childProcess;
+    this.watchmanSupportsPlatform = options.watchmanSupportsPlatform;
+    this.fs = options.fs;
+    this.root = options.root;
+
+    this.ui = options.ui;
+    this.tmp = undefined;
+
+    // This exists, because during tests repeadelty testing the same directories
+    this._doesNodeWorkCache = options.cache || {};
+  }
+
+  /*
+   * @private
+   *
+   * implements input options parsing and fallback.
+   *
+   * @method extractPreferenceFromOptions
+   * @returns {WatchDetector)
+  */
+  extractPreferenceFromOptions(options) {
+    if (options.watcher === POLLING) {
+      debug.info('skip detecting watchman, "polling" was selected.');
+      return new WatchPreference(POLLING);
+    } else if (options.watcher === NODE) {
+      debug.info('skip detecting watchman, "node" was selected.');
+      return new WatchPreference(NODE);
+    } else {
+      debug.info('no watcher preference set, lets attempt watchman');
+      return new WatchPreference(WATCHMAN);
+    }
+  }
+
+  /*
+   * @public
+   *
+   * Detect if `fs.watch` provided by node is capable of observing a directory
+   * within `process.cwd()`. Although `fs.watch` is provided as a part of the
+   * node stdlib, their exists platforms it ships to that do not implement the
+   * required sub-system. For example, there exists `posix` targets, without both
+   * inotify and FSEvents
+   *
+   * @method testIfNodeWatcherAppearsToWork
+   * @returns {Boolean) outcome
+  */
+  testIfNodeWatcherAppearsToWork() {
+    let root = this.root;
+    if (root in this._doesNodeWorkCache) {
+      return this._doesNodeWorkCache[root];
+    }
+
+    this._doesNodeWorkCache[root] = false;
+    try {
+      // builds a tmp directory at process.cwd() + '/tmp/' + 'something-unique';
+      let tmpDir = quickTemp.makeOrRemake(this, 'tmp');
+
+      let watcher = this.fs.watch(tmpDir, { persistent: false, recursive: false });
+      watcher.close();
+    } catch (e) {
+      debug.info('testing if node watcher failed with: %o', e);
+      return false;
+    } finally {
+      try {
+        quickTemp.remove(this, 'tmp');
+        // cleanup dir
+      } catch (e) {
+        // not much we can do, lets debug log.
+        debug.info('cleaning up dir failed with: %o', e);
+      }
+    }
+
+    // it seems we where able to at least watch and unwatch, this should catch
+    // systems that have a very broken NodeWatcher.
+    //
+    // NOTE: we could also add a more advance chance, that triggers a change
+    // and expects to be informed of the change. This can be added in a future
+    // iteration.
+    //
+
+    this._doesNodeWorkCache[root] = true;
+    return true;
+  }
+
+  /*
+   * @public
+   *
+   * Selecting the best watcher is complicated, this method (given a preference)
+   * will test and provide the best possible watcher available to the current
+   * system and project.
+   *
+   *
+   *  @method findBestWatcherOption
+   *  @input {Object} options
+   *  @returns {Object} watch preference
+  */
+  findBestWatcherOption(options) {
+    let preference = this.extractPreferenceFromOptions(options);
+    let original = options.watcher;
+
+    if (original && POSSIBLE_WATCHERS.indexOf(original) === -1) {
+      return Promise.reject(new SilentError(`Unknown Watcher: \`${original}\` supported watchers: [${POSSIBLE_WATCHERS.join(', ')}]`));
+    }
+
+    if (preference.watcher === WATCHMAN) {
+      preference = this.checkWatchman();
+    }
+
+    let bestOption = preference;
+    let actual;
+
+    if (bestOption.watcher === NODE) {
+      // although up to this point, we may believe Node is the best watcher
+      // this may not be true because:
+      // * not all platforms that run node, support node.watch
+      // * not all file systems support node watch
+      //
+      let appearsToWork = this.testIfNodeWatcherAppearsToWork();
+      actual = new WatchPreference(appearsToWork ? NODE : POLLING);
+    } else {
+      actual = bestOption;
+    }
+    debug.info('foundBestWatcherOption, preference was: %o, actual: %o', options, actual);
+    if (actual /* if no preference was initial set, don't bother informing the user */ &&
+      original /* if no original was set, the fallback is expected */ &&
+      actual.watcher !== original &&
+
+      // events represents either watcherman or node, but no preference
+      !(original === EVENTS && (actual.watcher === WATCHMAN || actual.watcher === NODE))) {
+      // if there was an initial preference, but we had to fall back inform the user.
+      this.ui.writeLine(`was unable to use: "${original}", fell back to: "${actual.watcher}"`);
+    }
+    return actual;
+  }
+
+  /*
+   * @public
+   *
+   * Although watchman may be selected, it may not work due to:
+   *
+   *  * invalid version
+   *  * it may be broken in detectable ways
+   *  * watchman executable may be something unexpected
+   *  * ???
+   *
+   *  @method checkWatchman
+   *  @returns {Object} watch preference + _watchmanInfo
+  */
+  checkWatchman() {
+    let result = new WatchPreference(null);
+
+    debug.info('execSync("watchman version")');
+    try {
+      let output = this.childProcess.execSync('watchman version', { stdio: [] });
+      debug.info('watchman version STDOUT: %s', output);
+      let version;
+      try {
+        version = JSON.parse(output).version;
+      } catch (e) {
+        this.ui.writeLine('Looks like you have a different program called watchman.');
+        this.ui.writeLine(WATCHMAN_INFO);
+
+        result.watcher = NODE;
+        return result;
+      }
+
+      debug.info('detected watchman: %s', version);
+
+      if (semver.satisfies(version, '>= 3.0.0')) {
+        debug.info('watchman %s does satisfy: %s', version, '>= 3.0.0');
+
+        result.watcher = WATCHMAN;
+
+        result.watchmanWorks({
+          version,
+          canNestRoots: semver.satisfies(version, '>= 3.7.0'),
+        });
+      } else {
+        debug.info('watchman %s does NOT satisfy: %s', version, '>= 3.0.0');
+
+        this.ui.writeLine(`Invalid watchman found, version: [${version}] did not satisfy [>= 3.0.0].`);
+        this.ui.writeLine(WATCHMAN_INFO);
+
+        result.watcher = NODE;
+      }
+
+      return result;
+    } catch (reason) {
+      debug.info('detecting watchman failed %o', reason);
+
+      if (this.watchmanSupportsPlatform) {
+        // don't bother telling windows users watchman detection failed, that is
+        // until watchman is legit on windows.
+      } else {
+        this.ui.writeLine('Could not start watchman');
+        this.ui.writeLine(WATCHMAN_INFO);
+      }
+
+      result.watcher = NODE;
+      return result;
+    }
+  }
+}
+
+module.exports = WatchDetector;
diff --git a/package.json b/package.json
index 0bdeeb553..5be6ee526 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "ember-cli",
-  "version": "2.18.0-beta.2",
+  "version": "2.18.0-beta.1",
   "description": "Command line tool for developing ambitious ember.js apps",
   "keywords": [
     "app",
@@ -119,7 +119,6 @@
     "uuid": "^3.0.0",
     "validate-npm-package-name": "^3.0.0",
     "walk-sync": "^0.3.0",
-    "watch-detector": "^0.1.0",
     "yam": "0.0.22"
   },
   "devDependencies": {


Test patch:
diff --git a/blueprints/app/files/tests/helpers/start-app.js b/blueprints/app/files/tests/helpers/start-app.js
index 99d35dcf4..681aa01a2 100644
--- a/blueprints/app/files/tests/helpers/start-app.js
+++ b/blueprints/app/files/tests/helpers/start-app.js
@@ -5,7 +5,6 @@ import { run } from '@ember/runloop';
 
 export default function startApp(attrs) {
   let attributes = merge({}, config.APP);
-  attributes.autoboot = true;
   attributes = merge(attributes, attrs); // use defaults, but you can override;
 
   return run(() => {
diff --git a/blueprints/app/files/tests/test-helper.js b/blueprints/app/files/tests/test-helper.js
index 0382a848d..edc8122d5 100644
--- a/blueprints/app/files/tests/test-helper.js
+++ b/blueprints/app/files/tests/test-helper.js
@@ -1,8 +1,7 @@
 import Application from '../app';
-import config from '../config/environment';
 import { setApplication } from '@ember/test-helpers';
 import { start } from 'ember-qunit';
 
-setApplication(Application.create(config.APP));
+setApplication(Application.create({ autoboot: false }));
 
 start();
diff --git a/blueprints/module-unification-app/files/src/ui/styles/app.css b/blueprints/module-unification-app/files/src/ui/styles/app.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/blueprints/module-unification-app/files/testem.js b/blueprints/module-unification-app/files/testem.js
new file mode 100644
index 000000000..09a686f94
--- /dev/null
+++ b/blueprints/module-unification-app/files/testem.js
@@ -0,0 +1,18 @@
+module.exports = {
+  test_page: 'tests/index.html?hidepassed',
+  disable_watching: true,
+  launch_in_ci: [
+    'Chrome'
+  ],
+  launch_in_dev: [
+    'Chrome'
+  ],
+  browser_args: {
+    Chrome: [
+      '--disable-gpu',
+      '--headless',
+      '--remote-debugging-port=9222',
+      '--window-size=1440,900'
+    ]
+  }
+};
diff --git a/blueprints/module-unification-app/files/tests/helpers/destroy-app.js b/blueprints/module-unification-app/files/tests/helpers/destroy-app.js
new file mode 100644
index 000000000..e7f983bd1
--- /dev/null
+++ b/blueprints/module-unification-app/files/tests/helpers/destroy-app.js
@@ -0,0 +1,5 @@
+import { run } from '@ember/runloop';
+
+export default function destroyApp(application) {
+  run(application, 'destroy');
+}
diff --git a/blueprints/module-unification-app/files/tests/helpers/module-for-acceptance.js b/blueprints/module-unification-app/files/tests/helpers/module-for-acceptance.js
new file mode 100644
index 000000000..90a93bac2
--- /dev/null
+++ b/blueprints/module-unification-app/files/tests/helpers/module-for-acceptance.js
@@ -0,0 +1,21 @@
+import { module } from 'qunit';
+import { resolve } from 'rsvp';
+import startApp from '../helpers/start-app';
+import destroyApp from '../helpers/destroy-app';
+
+export default function(name, options = {}) {
+  module(name, {
+    beforeEach() {
+      this.application = startApp();
+
+      if (options.beforeEach) {
+        return options.beforeEach.apply(this, arguments);
+      }
+    },
+
+    afterEach() {
+      let afterEach = options.afterEach && options.afterEach.apply(this, arguments);
+      return resolve(afterEach).then(() => destroyApp(this.application));
+    }
+  });
+}
diff --git a/blueprints/module-unification-app/files/tests/helpers/start-app.js b/blueprints/module-unification-app/files/tests/helpers/start-app.js
new file mode 100644
index 000000000..00f3e4f53
--- /dev/null
+++ b/blueprints/module-unification-app/files/tests/helpers/start-app.js
@@ -0,0 +1,16 @@
+import Application from '../../src/main';
+import config from '../../config/environment';
+import { run } from '@ember/runloop';
+import { merge } from '@ember/polyfills';
+
+export default function startApp(attrs) {
+  let attributes = merge({}, config.APP);
+  attributes = merge(attributes, attrs); // use defaults, but you can override;
+
+  return run(() => {
+    let application = Application.create(attributes);
+    application.setupForTesting();
+    application.injectTestHelpers();
+    return application;
+  });
+}
diff --git a/blueprints/module-unification-app/files/tests/index.html b/blueprints/module-unification-app/files/tests/index.html
new file mode 100644
index 000000000..140695b91
--- /dev/null
+++ b/blueprints/module-unification-app/files/tests/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <title><%= namespace %> Tests</title>
+    <meta name="description" content="">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    {{content-for "head"}}
+    {{content-for "test-head"}}
+
+    <link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
+    <link rel="stylesheet" href="{{rootURL}}assets/<%= name %>.css">
+    <link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
+
+    {{content-for "head-footer"}}
+    {{content-for "test-head-footer"}}
+  </head>
+  <body>
+    {{content-for "body"}}
+    {{content-for "test-body"}}
+
+    <script src="/testem.js" integrity=""></script>
+    <script src="{{rootURL}}assets/vendor.js"></script>
+    <script src="{{rootURL}}assets/test-support.js"></script>
+    <script src="{{rootURL}}assets/<%= name %>.js"></script>
+    <script src="{{rootURL}}assets/tests.js"></script>
+
+    {{content-for "body-footer"}}
+    {{content-for "test-body-footer"}}
+  </body>
+</html>
diff --git a/blueprints/module-unification-app/files/tests/integration/.gitkeep b/blueprints/module-unification-app/files/tests/integration/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/blueprints/module-unification-app/files/tests/test-helper.js b/blueprints/module-unification-app/files/tests/test-helper.js
new file mode 100644
index 000000000..f95229e14
--- /dev/null
+++ b/blueprints/module-unification-app/files/tests/test-helper.js
@@ -0,0 +1,7 @@
+import Application from '../src/main';
+import { setApplication } from '@ember/test-helpers';
+import { start } from 'ember-qunit';
+
+setApplication(Application.create({ autoboot: false }));
+
+start();
diff --git a/tests/acceptance/new-test.js b/tests/acceptance/new-test.js
index e2457a6dc..0450443e3 100644
--- a/tests/acceptance/new-test.js
+++ b/tests/acceptance/new-test.js
@@ -30,6 +30,7 @@ describe('Acceptance: ember new', function() {
   }));
 
   afterEach(function() {
+    process.env.MODULE_UNIFICATION = undefined;
     return tmp.teardown(tmpDir);
   });
 
@@ -65,6 +66,18 @@ describe('Acceptance: ember new', function() {
     confirmBlueprinted();
   }));
 
+  it('MODULE_UNIFICATION=true ember new foo works', co.wrap(function *() {
+    process.env.MODULE_UNIFICATION = 'true';
+    yield ember([
+      'new',
+      'foo',
+      '--skip-npm',
+      '--skip-bower',
+    ]);
+
+    confirmBlueprintedForDir('blueprints/module-unification-app');
+  }));
+
   it('ember new with empty app name fails with a warning', co.wrap(function *() {
     let err = yield expect(ember([
       'new',
diff --git a/tests/fixtures/addon/npm/README.md b/tests/fixtures/addon/npm/README.md
index 744417746..b8e8e3d9a 100644
--- a/tests/fixtures/addon/npm/README.md
+++ b/tests/fixtures/addon/npm/README.md
@@ -1,46 +1,26 @@
-foo
-==============================================================================
+# foo
 
-[Short description of the addon.]
+This README outlines the details of collaborating on this Ember addon.
 
-Installation
-------------------------------------------------------------------------------
+## Installation
 
-```
-ember install foo
-```
-
-
-Usage
-------------------------------------------------------------------------------
-
-[Longer description of how to use the addon in apps.]
-
-
-Contributing
-------------------------------------------------------------------------------
-
-### Installation
-
-* `git clone <repository-url>`
+* `git clone <repository-url>` this repository
 * `cd foo`
 * `npm install`
 
-### Running tests
-
-* `ember test` – Runs the test suite on the current Ember version
-* `ember test --server` – Runs the test suite in "watch mode"
-* `npm test` – Runs `ember try:each` to test your addon against multiple Ember versions
-
-### Running the dummy application
+## Running
 
 * `ember serve`
-* Visit the dummy application at [http://localhost:4200](http://localhost:4200).
+* Visit your app at [http://localhost:4200](http://localhost:4200).
 
-For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
+## Running Tests
+
+* `npm test` (Runs `ember try:each` to test your addon against multiple Ember versions)
+* `ember test`
+* `ember test --server`
 
+## Building
 
-License
-------------------------------------------------------------------------------
+* `ember build`
 
-This project is licensed under the [MIT License](LICENSE.md).
+For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
diff --git a/tests/fixtures/addon/yarn/README.md b/tests/fixtures/addon/yarn/README.md
index df6379b26..3dc559c2f 100644
--- a/tests/fixtures/addon/yarn/README.md
+++ b/tests/fixtures/addon/yarn/README.md
@@ -1,46 +1,26 @@
-foo
-==============================================================================
+# foo
 
-[Short description of the addon.]
+This README outlines the details of collaborating on this Ember addon.
 
-Installation
-------------------------------------------------------------------------------
+## Installation
 
-```
-ember install foo
-```
-
-
-Usage
-------------------------------------------------------------------------------
-
-[Longer description of how to use the addon in apps.]
-
-
-Contributing
-------------------------------------------------------------------------------
-
-### Installation
-
-* `git clone <repository-url>`
+* `git clone <repository-url>` this repository
 * `cd foo`
 * `yarn install`
 
-### Running tests
-
-* `ember test` – Runs the test suite on the current Ember version
-* `ember test --server` – Runs the test suite in "watch mode"
-* `yarn test` – Runs `ember try:each` to test your addon against multiple Ember versions
-
-### Running the dummy application
+## Running
 
 * `ember serve`
-* Visit the dummy application at [http://localhost:4200](http://localhost:4200).
+* Visit your app at [http://localhost:4200](http://localhost:4200).
 
-For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
+## Running Tests
+
+* `yarn test` (Runs `ember try:each` to test your addon against multiple Ember versions)
+* `ember test`
+* `ember test --server`
 
+## Building
 
-License
-------------------------------------------------------------------------------
+* `ember build`
 
-This project is licensed under the [MIT License](LICENSE.md).
+For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
diff --git a/tests/fixtures/brocfile-tests/custom-ember-env/config/environment.js b/tests/fixtures/brocfile-tests/custom-ember-env/config/environment.js
index e93f8a135..43cb47cb9 100644
--- a/tests/fixtures/brocfile-tests/custom-ember-env/config/environment.js
+++ b/tests/fixtures/brocfile-tests/custom-ember-env/config/environment.js
@@ -3,9 +3,6 @@ module.exports = function() {
     modulePrefix: 'some-cool-app',
     EmberENV: {
       asdflkmawejf: ';jlnu3yr23'
-    },
-    APP: {
-      autoboot: false
     }
   };
 };
diff --git a/tests/fixtures/brocfile-tests/custom-environment-config/config/environment.js b/tests/fixtures/brocfile-tests/custom-environment-config/config/environment.js
index 8b9b53f05..ad0acb32e 100644
--- a/tests/fixtures/brocfile-tests/custom-environment-config/config/environment.js
+++ b/tests/fixtures/brocfile-tests/custom-environment-config/config/environment.js
@@ -3,6 +3,6 @@ module.exports = function() {
     modulePrefix: 'some-cool-app',
     fileUsed: 'config/environment.js',
     baseURL: '/',
-    locationType: 'auto',
+    locationType: 'auto'
   };
 };
diff --git a/tests/fixtures/brocfile-tests/custom-environment-config/config/something-else.js b/tests/fixtures/brocfile-tests/custom-environment-config/config/something-else.js
index 2494f7900..044268f9f 100644
--- a/tests/fixtures/brocfile-tests/custom-environment-config/config/something-else.js
+++ b/tests/fixtures/brocfile-tests/custom-environment-config/config/something-else.js
@@ -4,8 +4,6 @@ module.exports = function() {
     fileUsed: 'config/something-else.js',
     baseURL: '/',
     locationType: 'auto',
-    APP: {
-      autoboot: false
-    }
+    APP: { }
   };
 };
diff --git a/tests/fixtures/help/generate-with-addon.txt b/tests/fixtures/help/generate-with-addon.txt
index c24a21c9a..4afd4a88d 100644
--- a/tests/fixtures/help/generate-with-addon.txt
+++ b/tests/fixtures/help/generate-with-addon.txt
@@ -44,6 +44,8 @@ ember generate \u001b[33m<blueprint>\u001b[39m \u001b[36m<options...>\u001b[39m
         \u001b[90mThe blueprint for addon in repo ember-cli addons.\u001b[39m
       lib \u001b[33m<name>\u001b[39m
         \u001b[90mGenerates a lib directory for in-repo addons.\u001b[39m
+      module-unification-app \u001b[33m<name>\u001b[39m
+        \u001b[90mGenerates an Ember application with a module unification layout.\u001b[39m
       server \u001b[33m<name>\u001b[39m
         \u001b[90mGenerates a server directory for mocks and proxies.\u001b[39m
       vendor-shim \u001b[33m<name>\u001b[39m
diff --git a/tests/fixtures/help/generate.txt b/tests/fixtures/help/generate.txt
index b35330d6e..0d458f974 100644
--- a/tests/fixtures/help/generate.txt
+++ b/tests/fixtures/help/generate.txt
@@ -17,8 +17,9 @@
         \u001b[90mThe blueprint for addon in repo ember-cli addons.\u001b[39m
       lib \u001b[33m<name>\u001b[39m
         \u001b[90mGenerates a lib directory for in-repo addons.\u001b[39m
+      module-unification-app \u001b[33m<name>\u001b[39m
+        \u001b[90mGenerates an Ember application with a module unification layout.\u001b[39m
       server \u001b[33m<name>\u001b[39m
         \u001b[90mGenerates a server directory for mocks and proxies.\u001b[39m
       vendor-shim \u001b[33m<name>\u001b[39m
         \u001b[90mGenerates an ES6 module shim for global libraries.\u001b[39m
-
diff --git a/tests/fixtures/help/help.js b/tests/fixtures/help/help.js
index 9481740bd..d7973a1a9 100644
--- a/tests/fixtures/help/help.js
+++ b/tests/fixtures/help/help.js
@@ -304,6 +304,13 @@ module.exports = {
               anonymousOptions: ['name'],
               overridden: false
             },
+            {
+              name: 'module-unification-app',
+              description: 'Generates an Ember application with a module unification layout.',
+              availableOptions: [],
+              anonymousOptions: ['name'],
+              overridden: false
+            },
             {
               name: 'server',
               description: 'Generates a server directory for mocks and proxies.',
diff --git a/tests/fixtures/help/with-addon-blueprints.js b/tests/fixtures/help/with-addon-blueprints.js
index 6443a4d97..2595c9f00 100644
--- a/tests/fixtures/help/with-addon-blueprints.js
+++ b/tests/fixtures/help/with-addon-blueprints.js
@@ -336,6 +336,13 @@ module.exports = {
               anonymousOptions: ['name'],
               overridden: false
             },
+            {
+              name: 'module-unification-app',
+              description: 'Generates an Ember application with a module unification layout.',
+              availableOptions: [],
+              anonymousOptions: ['name'],
+              overridden: false
+            },
             {
               name: 'server',
               description: 'Generates a server directory for mocks and proxies.',
diff --git a/tests/fixtures/help/with-addon-commands.js b/tests/fixtures/help/with-addon-commands.js
index a811188ff..415558d35 100644
--- a/tests/fixtures/help/with-addon-commands.js
+++ b/tests/fixtures/help/with-addon-commands.js
@@ -304,6 +304,13 @@ module.exports = {
               anonymousOptions: ['name'],
               overridden: false
             },
+            {
+              name: 'module-unification-app',
+              description: 'Generates an Ember application with a module unification layout.',
+              availableOptions: [],
+              anonymousOptions: ['name'],
+              overridden: false
+            },
             {
               name: 'server',
               description: 'Generates a server directory for mocks and proxies.',
diff --git a/tests/unit/models/addon-test.js b/tests/unit/models/addon-test.js
index d6e187c0e..3ae4bd829 100644
--- a/tests/unit/models/addon-test.js
+++ b/tests/unit/models/addon-test.js
@@ -364,7 +364,7 @@ describe('models/addon.js', function() {
 
         project = new Project(projectPath, packageContents, cli.ui, cli);
 
-        addon = new MyAddon(project, project);
+        addon = new MyAddon(project);
 
         originalEnvValue = process.env.EMBER_ADDON_ENV;
       });
@@ -383,14 +383,6 @@ describe('models/addon.js', function() {
         expect(addon.isDevelopingAddon(), 'addon is being developed').to.eql(true);
       });
 
-      it('returns true when the addon name is prefixed in package.json and not in index.js', function() {
-        process.env.EMBER_ADDON_ENV = 'development';
-
-        project.name = () => ('@foo/my-addon');
-        addon.name = 'my-addon';
-        expect(addon.isDevelopingAddon(), 'addon is being developed').to.eql(true);
-      });
-
       it('returns false when `EMBER_ADDON_ENV` is not set', function() {
         delete process.env.EMBER_ADDON_ENV;
 
diff --git a/tests/unit/models/watch-detector-test.js b/tests/unit/models/watch-detector-test.js
new file mode 100644
index 000000000..0f4caf30d
--- /dev/null
+++ b/tests/unit/models/watch-detector-test.js
@@ -0,0 +1,238 @@
+'use strict';
+
+const expect = require('chai').expect;
+
+const MockUI = require('console-ui/mock');
+const WatchDetector = require('../../../lib/models/watch-detector');
+const EOL = require('os').EOL;
+
+describe('WatchDetector', function() {
+  let ui;
+  let subject;
+  let fs, childProcess;
+
+  beforeEach(function() {
+    ui = new MockUI();
+
+    fs = {};
+    childProcess = {};
+
+    subject = new WatchDetector({
+      ui,
+      fs,
+      childProcess,
+      watchmanSupportsPlatform: false,
+      root: process.cwd(),
+    });
+  });
+
+  it('#extractPreferenceFromOptions works', function() {
+    expect(subject.extractPreferenceFromOptions({})).to.have.property('watcher', 'watchman');
+    expect(subject.extractPreferenceFromOptions({ watcher: 'polling' })).to.have.property('watcher', 'polling');
+    expect(subject.extractPreferenceFromOptions({ watcher: 'node' })).to.have.property('watcher', 'node');
+    expect(subject.extractPreferenceFromOptions({ watcher: 'nodsdfe' })).to.have.property('watcher', 'watchman');
+  });
+
+  describe('#testIfNodeWatcherAppearsToWork', function() {
+    it('reports NO if fs.watch throws', function() {
+      fs.watch = function() {
+        throw new Error('Something went wrong');
+      };
+
+      expect(subject.testIfNodeWatcherAppearsToWork()).to.be.false;
+    });
+
+    // we could extend this to test also if change events are triggered or not..
+    it('reports YES if nothing throws', function() {
+      fs.watch = function() { return { close() { } }; };
+
+      expect(subject.testIfNodeWatcherAppearsToWork()).to.be.true;
+    });
+  });
+
+
+  describe('#findBestWatcherOption', function() {
+    describe('input preference.watcher === watchman', function() {
+      beforeEach(function() {
+        childProcess.execSync = function() {
+          return '{"version":"3.0.0"}';
+        };
+      });
+
+      it('chooses watchman', function() {
+        let option = subject.findBestWatcherOption({ watcher: 'watchman' });
+        expect(option).to.have.property('watcher', 'watchman');
+        expect(option.watchmanInfo).to.have.property('version');
+        expect(option.watchmanInfo).to.have.property('canNestRoots');
+        expect(option.watchmanInfo).to.have.property('enabled', true);
+        expect(ui.output).not.to.match(/Could not start watchman/);
+        expect(ui.output).not.to.match(/fell back to: "node"/);
+        expect(ui.output).not.to.match(/Visit https:\/\/ember-cli.com\/user-guide\/#watchman/);
+      });
+
+      describe('watchman does not work', function() {
+        beforeEach(function() {
+          childProcess.execSync = function() {
+            throw new Error('');
+          };
+        });
+
+        it('false back to node if it can', function() {
+          fs.watch = function() { return { close() { } }; };
+
+          let option = subject.findBestWatcherOption({ watcher: 'watchman' });
+          expect(option.watchmanInfo).to.have.property('enabled', false);
+          expect(option).to.have.property('watcher', 'node');
+          expect(option.watchmanInfo).to.have.property('version');
+          expect(option.watchmanInfo).to.have.property('canNestRoots');
+          expect(ui.output).to.match(/Could not start watchman/);
+          expect(ui.output).to.match(/fell back to: "node"/);
+          expect(ui.output).to.match(/Visit https:\/\/ember-cli.com\/user-guide\/#watchman/);
+        });
+
+        it('false back to polling if node does not work', function() {
+          fs.watch = function() {
+            throw new Error('something went wrong');
+          };
+
+          let option = subject.findBestWatcherOption({ watcher: 'watchman' });
+          expect(option).to.have.property('watcher', 'polling');
+          expect(option.watchmanInfo).to.have.property('enabled', false);
+          expect(option.watchmanInfo).to.have.property('version');
+          expect(option.watchmanInfo).to.have.property('canNestRoots');
+          expect(ui.output).to.match(/Could not start watchman/);
+          expect(ui.output).to.match(/fell back to: "polling"/);
+          expect(ui.output).to.match(/Visit https:\/\/ember-cli.com\/user-guide\/#watchman/);
+        });
+      });
+    });
+
+    describe('input preference.watcher === polling', function() {
+      it('simply works', function() {
+        // we assuming polling can never not work, if it doesn't sorry..
+        let option = subject.findBestWatcherOption({ watcher: 'polling' });
+        expect(option.watchmanInfo).to.have.property('enabled', false);
+        expect(option).to.have.property('watcher', 'polling');
+        expect(ui.output).to.eql('');
+      });
+    });
+
+    describe('input preference.watcher === node', function() {
+      it('chooses node, if everything  seems ok', function() {
+        fs.watch = function() { return { close() { } }; };
+
+        // we assuming polling can never not work, if it doesn't sorry..
+        let option = subject.findBestWatcherOption({ watcher: 'node' });
+        expect(option).to.have.property('watcher', 'node');
+        expect(ui.output).to.eql('');
+      });
+
+      it('false back to polling if watch fails', function() {
+        fs.watch = function() {
+          throw new Error('OMG');
+        };
+        // we assuming polling can never not work, if it doesn't sorry..
+        let option = subject.findBestWatcherOption({ watcher: 'node' });
+        expect(option).to.have.property('watcher', 'polling');
+        expect(ui.output).to.eql(`was unable to use: "node", fell back to: "polling"${EOL}`);
+      });
+
+      it('falls back to polling if unwatch fails', function() {
+        fs.watch = function() {
+          // works
+        };
+        fs.unwatch = function() {
+          throw new Error('OMG');
+        };
+        // we assuming polling can never not work, if it doesn't sorry..
+        let option = subject.findBestWatcherOption({ watcher: 'node' });
+        expect(option).to.have.property('watcher', 'polling');
+        expect(ui.output).to.eql(`was unable to use: "node", fell back to: "polling"${EOL}`);
+      });
+    });
+
+  });
+
+  describe('#checkWatchman', function() {
+    describe('watchmanSupportsPlatform', function() {
+      it('true: hides the "watchman not found, falling back to XYZ message"', function() {
+        subject.watchmanSupportsPlatform = true;
+
+        childProcess.execSync = function() {
+          throw new Error();
+        };
+        fs.watch = function() { return { close() { } }; };
+
+        let result = subject.checkWatchman();
+        expect(result).to.have.property('watcher', 'node');
+        expect(ui.output).to.eql('');
+      });
+
+      it('false: shows the "watchman not found, falling back to XYZ message"', function() {
+        subject.watchmanSupportsPlatform = false;
+        fs.watch = function() { return { close() { } }; };
+
+        childProcess.execSync = function() {
+          throw new Error();
+        };
+
+        let result = subject.checkWatchman();
+        expect(result).to.have.property('watcher', 'node');
+        expect(ui.output).to.match(/Could not start watchman/);
+        expect(ui.output).to.match(/Visit https:\/\/ember-cli.com\/user-guide\/#watchman/);
+      });
+    });
+    it('prefers watchman if everything appears to be good', function() {
+      childProcess.execSync = function() {
+        return '{"version":"3.0.0"}';
+      };
+
+      let preference = subject.checkWatchman();
+      expect(preference).to.have.property('watcher', 'watchman');
+      expect(ui.output).to.not.match(/Could not start watchman/);
+      expect(ui.output).to.not.match(/falling back to NodeWatcher/);
+      expect(ui.output).to.not.match(/ember-cli\.com\/user-guide\/#watchman/);
+      expect(ui.output).to.not.match(/Looks like you have a different program called watchman/);
+      expect(ui.output).to.not.match(/Invalid watchman found/);
+    });
+
+    describe('fallse back to NODE', function() {
+      let iff = it;
+
+      iff('the exec rejects', function() {
+        childProcess.execSync = function() {
+          throw new Error();
+        };
+
+        let preference = subject.checkWatchman();
+        expect(preference).to.have.property('watcher', 'node');
+        expect(ui.output).to.match(/Could not start watchman/);
+        expect(ui.output).to.match(/ember-cli\.com\/user-guide\/#watchman/);
+      });
+
+      iff('the `watchman version` doesn\'t parse', function() {
+        childProcess.execSync = function() {
+          return 'not json';
+        };
+
+        let preference = subject.checkWatchman();
+        expect(preference).to.have.property('watcher', 'node');
+        expect(ui.output).to.match(/Looks like you have a different program called watchman/);
+        expect(ui.output).to.match(/ember-cli\.com\/user-guide\/#watchman/);
+      });
+
+
+      iff('the `watchman version` doesn\'t satisfy => 3.0.0', function() {
+        childProcess.execSync = function() {
+          return '{"version":"2.9.9"}';
+        };
+
+        let preference = subject.checkWatchman();
+        expect(preference).to.have.property('watcher', 'node');
+        expect(ui.output).to.match(/Invalid watchman found/);
+        expect(ui.output).to.match(/version: \[2\.9\.9\] did not satisfy/);
+        expect(ui.output).to.match(/ember-cli\.com\/user-guide\/#watchman/);
+      });
+    });
+  });
+});