Repository: ember-cli/ember-cli
Pull request #7451 (2017-11-21 04:33:30)

Add `eslint-plugin-node` for addons.
Add `eslint-plugin-node` and its default rules to node-land files. This plugin allows us to fix the following common issues:

* Accidentially requiring files that are not included in `package.json`'s `files` array (or are specifically excluded via either `.npmignore` or `.gitignore`).
* Enforces all node land code to conform to the project `package.json`'s `engines` config. For example, when `engines` allows Node 4 linting would fail if someone used `let` or `const` without `'use strict';`.

These two gotchas are _super_ common for addons to hit, and this will save quite a few "quick-fix patch release" scenarios for folks going forward.

See [eslint-plugin-node's README](https://github.com/mysticatea/eslint-plugin-node#readme) for complete rule details.

rwjblue: The latest round of changes are:

* Remove templating from `.eslintrc.js` and `package.json`. These issues definitely plague app developers also and this sort of linting is still useful.  We can bring templating back in a future PR if we want to do more customization around app vs addon `.eslintrc.js`. 
* Move unrelated test file changes into https://github.com/ember-cli/ember-cli/pull/7457 (to avoid confusion)
Turbo87: @rwjblue I just realized that even if we added this plugin we wouldn't actually run ESLint on these files by default yet, so maybe we should get that sorted out first before adding a plugin that is only relevant for as of now unlinted files :thin
rwjblue: Ya, sounds good 

Patch:
diff --git a/blueprints/addon/index.js b/blueprints/addon/index.js
index 911f3ed15..4c3704e95 100644
--- a/blueprints/addon/index.js
+++ b/blueprints/addon/index.js
@@ -62,6 +62,9 @@ module.exports = {
     // add `ember-disable-prototype-extensions` to addons by default
     contents.devDependencies['ember-disable-prototype-extensions'] = '^1.1.2';
 
+    // add `eslint-plugin-node` to addons by default
+    contents.devDependencies['eslint-plugin-node'] = '^5.2.1';
+
     // use `ember-try` as test script in addons by default
     contents.scripts.test = 'ember try:each';
 
@@ -124,6 +127,7 @@ module.exports = {
       year: date.getFullYear(),
       yarn: options.yarn,
       welcome: options.welcome,
+      blueprint: 'addon',
     };
   },
 
diff --git a/blueprints/app/files/.eslintrc.js b/blueprints/app/files/.eslintrc.js
index 9e95299e0..e6c3f9c35 100644
--- a/blueprints/app/files/.eslintrc.js
+++ b/blueprints/app/files/.eslintrc.js
@@ -8,7 +8,7 @@ module.exports = {
     'ember'
   ],
   extends: [
-    'eslint:recommended', 
+    'eslint:recommended',
     'plugin:ember/recommended'
   ],
   env: {
@@ -33,7 +33,11 @@ module.exports = {
       env: {
         browser: false,
         node: true
-      }
+      }<% if (blueprint !== 'app') { %>,
+      plugins: ['node'],
+      rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
+        // add your custom rules and overrides for node files here
+      })<% } %>
     },
 
     // test files
diff --git a/blueprints/app/index.js b/blueprints/app/index.js
index 9918676a9..9e8060e64 100644
--- a/blueprints/app/index.js
+++ b/blueprints/app/index.js
@@ -27,6 +27,7 @@ module.exports = {
       emberCLIVersion: require('../../package').version,
       yarn: options.yarn,
       welcome: options.welcome,
+      blueprint: 'app',
     };
   },
 };


Test patch:
diff --git a/tests/acceptance/new-test.js b/tests/acceptance/new-test.js
index a5e100b87..52a073f52 100644
--- a/tests/acceptance/new-test.js
+++ b/tests/acceptance/new-test.js
@@ -406,6 +406,7 @@ describe('Acceptance: ember new', function() {
         'src/ui/routes/application/template.hbs',
         '.travis.yml',
         'README.md',
+        '.eslintrc.js',
       ].forEach(filePath => {
         expect(file(filePath))
           .to.equal(file(path.join(__dirname, '../fixtures', fixturePath, filePath)));
@@ -431,6 +432,7 @@ describe('Acceptance: ember new', function() {
         'src/ui/routes/application/template.hbs',
         '.travis.yml',
         'README.md',
+        '.eslintrc.js',
       ].forEach(filePath => {
         expect(file(filePath))
           .to.equal(file(path.join(__dirname, '../fixtures', fixturePath, filePath)));
@@ -454,6 +456,7 @@ describe('Acceptance: ember new', function() {
         'app/templates/application.hbs',
         '.travis.yml',
         'README.md',
+        '.eslintrc.js',
       ].forEach(filePath => {
         expect(file(filePath))
           .to.equal(file(path.join(__dirname, '../fixtures', fixturePath, filePath)));
@@ -477,6 +480,7 @@ describe('Acceptance: ember new', function() {
         'app/templates/application.hbs',
         '.travis.yml',
         'README.md',
+        '.eslintrc.js',
       ].forEach(filePath => {
         expect(file(filePath))
           .to.equal(file(path.join(__dirname, '../fixtures', fixturePath, filePath)));
@@ -500,6 +504,7 @@ describe('Acceptance: ember new', function() {
         'tests/dummy/app/templates/application.hbs',
         '.travis.yml',
         'README.md',
+        '.eslintrc.js',
       ].forEach(filePath => {
         expect(file(filePath))
           .to.equal(file(path.join(__dirname, '../fixtures', fixturePath, filePath)));
@@ -525,6 +530,7 @@ describe('Acceptance: ember new', function() {
         'tests/dummy/app/templates/application.hbs',
         '.travis.yml',
         'README.md',
+        '.eslintrc.js',
       ].forEach(filePath => {
         expect(file(filePath))
           .to.equal(file(path.join(__dirname, '../fixtures', fixturePath, filePath)));
diff --git a/tests/fixtures/addon/npm/.eslintrc.js b/tests/fixtures/addon/npm/.eslintrc.js
new file mode 100644
index 000000000..6ed6f7478
--- /dev/null
+++ b/tests/fixtures/addon/npm/.eslintrc.js
@@ -0,0 +1,52 @@
+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
+      },
+      plugins: ['node'],
+      rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
+        // add your custom rules and overrides for node files here
+      })
+    },
+
+    // test files
+    {
+      files: ['tests/**/*.js'],
+      excludedFiles: ['tests/dummy/**/*.js'],
+      env: {
+        embertest: true
+      }
+    }
+  ]
+};
diff --git a/tests/fixtures/addon/npm/package.json b/tests/fixtures/addon/npm/package.json
index 03459db49..44b4beef8 100644
--- a/tests/fixtures/addon/npm/package.json
+++ b/tests/fixtures/addon/npm/package.json
@@ -40,6 +40,7 @@
     "ember-resolver": "^4.0.0",
     "ember-source": "~2.18.0-beta.3",
     "eslint-plugin-ember": "^5.0.0",
+    "eslint-plugin-node": "^5.2.1",
     "loader.js": "^4.2.3"
   },
   "engines": {
diff --git a/tests/fixtures/addon/yarn/.eslintrc.js b/tests/fixtures/addon/yarn/.eslintrc.js
new file mode 100644
index 000000000..6ed6f7478
--- /dev/null
+++ b/tests/fixtures/addon/yarn/.eslintrc.js
@@ -0,0 +1,52 @@
+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
+      },
+      plugins: ['node'],
+      rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
+        // add your custom rules and overrides for node files here
+      })
+    },
+
+    // test files
+    {
+      files: ['tests/**/*.js'],
+      excludedFiles: ['tests/dummy/**/*.js'],
+      env: {
+        embertest: true
+      }
+    }
+  ]
+};
diff --git a/tests/fixtures/addon/yarn/package.json b/tests/fixtures/addon/yarn/package.json
index ba93ae687..b5f916d5d 100644
--- a/tests/fixtures/addon/yarn/package.json
+++ b/tests/fixtures/addon/yarn/package.json
@@ -41,6 +41,7 @@
     "ember-source": "~2.18.0-beta.3",
     "ember-welcome-page": "^3.0.0",
     "eslint-plugin-ember": "^5.0.0",
+    "eslint-plugin-node": "^5.2.1",
     "loader.js": "^4.2.3"
   },
   "engines": {
diff --git a/tests/fixtures/app/npm/.eslintrc.js b/tests/fixtures/app/npm/.eslintrc.js
new file mode 100644
index 000000000..7d328870e
--- /dev/null
+++ b/tests/fixtures/app/npm/.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/tests/fixtures/app/yarn/.eslintrc.js b/tests/fixtures/app/yarn/.eslintrc.js
new file mode 100644
index 000000000..7d328870e
--- /dev/null
+++ b/tests/fixtures/app/yarn/.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/tests/fixtures/module-unification-app/npm/.eslintrc.js b/tests/fixtures/module-unification-app/npm/.eslintrc.js
new file mode 100644
index 000000000..7d328870e
--- /dev/null
+++ b/tests/fixtures/module-unification-app/npm/.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/tests/fixtures/module-unification-app/yarn/.eslintrc.js b/tests/fixtures/module-unification-app/yarn/.eslintrc.js
new file mode 100644
index 000000000..7d328870e
--- /dev/null
+++ b/tests/fixtures/module-unification-app/yarn/.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/tests/unit/blueprints/addon-test.js b/tests/unit/blueprints/addon-test.js
index 5c765a644..771e0adae 100644
--- a/tests/unit/blueprints/addon-test.js
+++ b/tests/unit/blueprints/addon-test.js
@@ -75,7 +75,8 @@ describe('blueprint - addon', function() {
   },\n\
   "dependencies": {},\n\
   "devDependencies": {\n\
-    "ember-disable-prototype-extensions": "^1.1.2"\n\
+    "ember-disable-prototype-extensions": "^1.1.2",\n\
+    "eslint-plugin-node": "^5.2.1"\n\
   },\n\
   "ember-addon": {\n\
     "configPath": "tests/dummy/config"\n\
@@ -170,6 +171,7 @@ describe('blueprint - addon', function() {
 
         let json = JSON.parse(output);
         delete json.devDependencies['ember-disable-prototype-extensions'];
+        delete json.devDependencies['eslint-plugin-node'];
         expect(json.dependencies).to.deep.equal({ a: "1", b: "1" });
         expect(json.devDependencies).to.deep.equal({ a: "1", b: "1" });
       });