Repository: ember-cli/ember-cli
Pull request #6307 (2016-09-30 04:45:00)

Node.js LTS updates.
This updates the versions of Node.js we support, improves our messaging to users, and makes the platform checker alerts dynamic (and correct, for the first time ever).


Patch:
diff --git a/.npmignore b/.npmignore
index 0a86e7f3c..59e4a8af8 100644
--- a/.npmignore
+++ b/.npmignore
@@ -5,3 +5,5 @@
 /tests/!(helpers)
 /tmp
 /*.*
+!.travis.yml
+!appveyor.yml
diff --git a/.travis.yml b/.travis.yml
index 6a3a7d9ad..6cac904c1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,11 +2,9 @@ sudo: false
 
 language: node_js
 node_js:
-  - "0.10"
   - "0.12"
   - "4"
-  - "5"
-  - node
+  - "6"
 
 branches:
   except:
diff --git a/appveyor.yml b/appveyor.yml
index 30236a8c9..ada93ead9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -4,8 +4,8 @@
 environment:
   MOCHA_REPORTER: "mocha-appveyor-reporter"
   matrix:
-    - nodejs_version: "4.2"
-    - nodejs_version: "5.0"
+    - nodejs_version: "4"
+    - nodejs_version: "6"
 
 branches:
   except:
diff --git a/lib/cli/cli.js b/lib/cli/cli.js
index a3b3ded08..57f5a807e 100644
--- a/lib/cli/cli.js
+++ b/lib/cli/cli.js
@@ -98,15 +98,17 @@ CLI.prototype.run = function(environment) {
 
     var PlatformChecker = require('../utilities/platform-checker');
     var platform = new PlatformChecker(process.version);
-    if (!platform.isValid && !this.testing) {
+    var recommendation = ' We recommend that you use the most-recent "Active LTS" version of Node.js.';
+
+    if (!this.testing) {
       if (platform.isDeprecated) {
         this.ui.writeDeprecateLine('Node ' + process.version +
-                                   ' is no longer supported by Ember CLI. Please update to a more recent version of Node');
+                                   ' is no longer supported by Ember CLI.' + recommendation);
       }
 
-      if (platform.isUntested) {
+      if (!platform.isTested) {
         this.ui.writeWarnLine('Node ' + process.version +
-                              ' has currently not been tested against Ember CLI and may result in unexpected behaviour.');
+                              ' is not tested against Ember CLI on your platform.' + recommendation);
       }
     }
 
diff --git a/lib/utilities/load-config.js b/lib/utilities/load-config.js
new file mode 100644
index 000000000..2b6ce6b45
--- /dev/null
+++ b/lib/utilities/load-config.js
@@ -0,0 +1,32 @@
+'use strict';
+
+var findup = require('find-up');
+var fs = require('fs');
+var path = require('path');
+var yaml = require('js-yaml');
+
+var parsers = {
+  '.yml': yaml.safeLoad,
+  '.yaml': yaml.safeLoad,
+  '.json': JSON.parse
+};
+
+function loadConfig(file, basedir) {
+  basedir = basedir || __dirname;
+  var ext = path.extname(file);
+  var configPath = findup.sync(file, { cwd: basedir });
+
+  var content;
+  try {
+    content = fs.readFileSync(configPath, 'utf8');
+    if (parsers[ext]) {
+      content = parsers[ext](content);
+    }
+  } catch (e) {
+    // ESLint doesn't like this.
+  }
+
+  return content;
+}
+
+module.exports = loadConfig;
diff --git a/lib/utilities/platform-checker.js b/lib/utilities/platform-checker.js
index f116f6fa5..52abec145 100644
--- a/lib/utilities/platform-checker.js
+++ b/lib/utilities/platform-checker.js
@@ -2,33 +2,50 @@
 
 var semver = require('semver');
 var logger = require('heimdalljs-logger')('ember-cli:platform-checker:');
+var loadConfig = require('./load-config');
 
-var LOWER_RANGE = '0.12.0';
-var UPPER_RANGE = '7.0.0';
+var testedEngines;
+if (process.platform === 'win32') {
+  testedEngines = loadConfig('appveyor.yml')
+    .environment.matrix
+    .map(function(element) {
+      return element.nodejs_version;
+    })
+    .join(' || ');
+} else {
+  testedEngines = loadConfig('.travis.yml')
+    .node_js
+    .join(' || ');
+}
+
+var supportedEngines = loadConfig('package.json').engines.node;
 
 module.exports = PlatformChecker;
 function PlatformChecker(version) {
   this.version = version;
   this.isValid = this.checkIsValid();
-  this.isUntested = this.checkIsUntested();
+  this.isTested = this.checkIsTested();
   this.isDeprecated = this.checkIsDeprecated();
 
   logger.info('%o', {
     version: this.version,
     isValid: this.isValid,
-    isUntested: this.isUntested,
+    isTested: this.isTested,
     isDeprecated: this.isDeprecated
   });
 }
 
-PlatformChecker.prototype.checkIsValid = function() {
-  return semver.satisfies(this.version, '>=' + LOWER_RANGE + ' <' + UPPER_RANGE);
+PlatformChecker.prototype.checkIsValid = function(range) {
+  range = range || supportedEngines;
+  return semver.satisfies(this.version, range) || semver.gtr(this.version, supportedEngines);
 };
 
-PlatformChecker.prototype.checkIsDeprecated = function() {
-  return semver.satisfies(this.version, '<' + LOWER_RANGE);
+PlatformChecker.prototype.checkIsDeprecated = function(range) {
+  range = range || supportedEngines;
+  return !this.checkIsValid(range);
 };
 
-PlatformChecker.prototype.checkIsUntested = function() {
-  return semver.satisfies(this.version, '>=' + UPPER_RANGE);
+PlatformChecker.prototype.checkIsTested = function(range) {
+  range = range || testedEngines;
+  return semver.satisfies(this.version, range);
 };
diff --git a/package.json b/package.json
index 524dafe88..62f3d4577 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
     "inquirer": "^1.0.2",
     "is-git-url": "^0.2.0",
     "isbinaryfile": "^3.0.0",
+    "js-yaml": "^3.6.1",
     "leek": "0.0.23",
     "lodash.template": "^4.2.5",
     "markdown-it": "8.0.0",
@@ -138,7 +139,7 @@
     "yuidocjs": "0.10.2"
   },
   "engines": {
-    "node": ">= 0.10.0"
+    "node": "0.12.* || ^4.5 || 6.*"
   },
   "trackingCode": "UA-49225444-1"
 }


Test patch:
diff --git a/blueprints/app/files/tests/helpers/start-app.js b/blueprints/app/files/tests/helpers/start-app.js
index 54c33c16c..e098f1d5b 100644
--- a/blueprints/app/files/tests/helpers/start-app.js
+++ b/blueprints/app/files/tests/helpers/start-app.js
@@ -5,8 +5,8 @@ import config from '../../config/environment';
 export default function startApp(attrs) {
   let application;
 
-  // use defaults, but you can override
-  let attributes = Ember.assign({}, config.APP, attrs);
+  let attributes = Ember.merge({}, config.APP);
+  attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
 
   Ember.run(() => {
     application = Application.create(attributes);
diff --git a/tests/fixtures/load-config/.something b/tests/fixtures/load-config/.something
new file mode 100644
index 000000000..bb56b0551
--- /dev/null
+++ b/tests/fixtures/load-config/.something
@@ -0,0 +1,1 @@
+hello: world
diff --git a/tests/fixtures/load-config/.travis.yaml b/tests/fixtures/load-config/.travis.yaml
new file mode 100644
index 000000000..bb56b0551
--- /dev/null
+++ b/tests/fixtures/load-config/.travis.yaml
@@ -0,0 +1,1 @@
+hello: world
diff --git a/tests/fixtures/load-config/.travis.yml b/tests/fixtures/load-config/.travis.yml
new file mode 100644
index 000000000..bb56b0551
--- /dev/null
+++ b/tests/fixtures/load-config/.travis.yml
@@ -0,0 +1,1 @@
+hello: world
diff --git a/tests/fixtures/load-config/child/test.json b/tests/fixtures/load-config/child/test.json
new file mode 100644
index 000000000..f2a886f39
--- /dev/null
+++ b/tests/fixtures/load-config/child/test.json
@@ -0,0 +1,3 @@
+{
+  "hello": "world"
+}
diff --git a/tests/fixtures/load-config/package.json b/tests/fixtures/load-config/package.json
new file mode 100644
index 000000000..f2a886f39
--- /dev/null
+++ b/tests/fixtures/load-config/package.json
@@ -0,0 +1,3 @@
+{
+  "hello": "world"
+}
diff --git a/tests/unit/utilities/load-config-test.js b/tests/unit/utilities/load-config-test.js
new file mode 100644
index 000000000..c4033f238
--- /dev/null
+++ b/tests/unit/utilities/load-config-test.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var expect = require('chai').expect;
+var path = require('path');
+var loadConfig = require('../../../lib/utilities/load-config');
+
+describe('load-config', function() {
+  var fixtureDirectory = path.resolve(__dirname, '../../fixtures/load-config');
+
+  it('loads and parses a yml file', function() {
+    expect(loadConfig('.travis.yml', fixtureDirectory).hello).to.equal('world');
+  });
+  it('loads and parses a yaml file', function() {
+    expect(loadConfig('.travis.yml', fixtureDirectory).hello).to.equal('world');
+  });
+  it('loads and parses a json file', function() {
+    expect(loadConfig('package.json', fixtureDirectory).hello).to.equal('world');
+  });
+  it('loads a vanilla file', function() {
+    expect(loadConfig('.something', fixtureDirectory)).to.equal('hello: world\n');
+  });
+  it('works across directories', function() {
+    expect(loadConfig('.something', path.join(fixtureDirectory, 'child'))).to.equal('hello: world\n');
+    expect(loadConfig('test.json', path.join(fixtureDirectory, 'child')).hello).to.equal('world');
+  });
+});
+
+describe('publishes the appropriate config files', function() {
+  var EmberCLIDir = path.resolve(__dirname, '../../../');
+  var npmignore = loadConfig('.npmignore', EmberCLIDir);
+
+  it('has the config files', function() {
+    expect(npmignore).to.include('!.travis.yml');
+    expect(npmignore).to.include('!appveyor.yml');
+  });
+});
diff --git a/tests/unit/utilities/platform-checker-test.js b/tests/unit/utilities/platform-checker-test.js
index 85247a1fe..4702aaeb0 100644
--- a/tests/unit/utilities/platform-checker-test.js
+++ b/tests/unit/utilities/platform-checker-test.js
@@ -4,59 +4,23 @@ var expect = require('chai').expect;
 var PlatformChecker = require('../../../lib/utilities/platform-checker');
 
 describe('platform-checker', function() {
-  it('should return isDeprecated for Node v0.12', function() {
-    expect(new PlatformChecker('v0.10.1').isDeprecated).to.be.equal(true);
-    expect(new PlatformChecker('v0.10.15').isDeprecated).to.be.equal(true);
-    expect(new PlatformChecker('v0.10.30').isDeprecated).to.be.equal(true);
-    expect(new PlatformChecker('v0.12.0').isDeprecated).to.be.equal(false);
+  it('checkIsDeprecated', function() {
+    expect(new PlatformChecker('v0.10.1').checkIsDeprecated('4 || 6')).to.be.equal(true, 'versions below the range are deprecated');
+    expect(new PlatformChecker('v4.5.0').checkIsDeprecated('4 || 6')).to.be.equal(false, 'versions in the range are not deprecated');
+    expect(new PlatformChecker('4.5.0').checkIsDeprecated('4 || 6')).to.be.equal(false, 'versions without a v prefix recognize correctly');
+    expect(new PlatformChecker('v9.0.0').checkIsDeprecated('4 || 6')).to.be.equal(false, 'versions above the range are not deprecated');
   });
 
-  it('should return isUntested for Node v6', function() {
-    expect(new PlatformChecker('v7.0.0').isUntested).to.be.equal(true);
-    expect(new PlatformChecker('v6.0.0').isUntested).to.be.equal(false);
-    expect(new PlatformChecker('v0.12.0').isUntested).to.be.equal(false);
+  it('checkIsValid', function() {
+    expect(new PlatformChecker('v0.10.1').checkIsValid('4 || 6')).to.be.equal(false, 'versions below the range are not valid');
+    expect(new PlatformChecker('v4.5.0').checkIsValid('4 || 6')).to.be.equal(true, 'versions in the range are valid');
+    expect(new PlatformChecker('v4.6.0').checkIsValid('^4.5')).to.be.equal(true, 'LTS "minor" pattern works.');
+    expect(new PlatformChecker('v9.0.0').checkIsValid('4 || 6')).to.be.equal(true, 'versions above the range are valid');
   });
 
-  it('should return isValid for iojs', function() {
-    expect(new PlatformChecker('v1.0.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v1.0.1').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v1.0.2').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v1.0.3').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v1.0.4').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v1.1.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v1.2.0').isValid).to.be.equal(true);
-  });
-
-  it('should return isValid for Node v0.12', function() {
-    expect(new PlatformChecker('v0.12.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v0.12.15').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v0.12.30').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v0.10.0').isValid).to.be.equal(false);
-  });
-
-  it('should return isValid for Node v0.13', function() {
-    expect(new PlatformChecker('v0.13.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v0.13.15').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v0.13.30').isValid).to.be.equal(true);
-  });
-
-  it('should return isValid for Node v4', function() {
-    expect(new PlatformChecker('v4.0.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v4.0.15').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v4.0.30').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v4.1.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v4.2.0').isValid).to.be.equal(true);
-  });
-
-  it('should return isValid for Node v5', function() {
-    expect(new PlatformChecker('v5.0.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v5.1.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v5.99.0').isValid).to.be.equal(true);
-  });
-
-  it('should return isValid for Node v6', function() {
-    expect(new PlatformChecker('v6.0.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v6.1.0').isValid).to.be.equal(true);
-    expect(new PlatformChecker('v6.99.0').isValid).to.be.equal(true);
+  it('checkIsTested', function() {
+    expect(new PlatformChecker('v0.10.1').checkIsTested('4 || 6')).to.be.equal(false, 'versions not in range are untested');
+    expect(new PlatformChecker('v9.0.0').checkIsTested('4 || 6')).to.be.equal(false, 'versions not in range are untested');
+    expect(new PlatformChecker('v4.5.0').checkIsTested('4 || 6')).to.be.equal(true, 'versions in the range are valid');
   });
 });