eslint 4.2.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/bin/eslint.js +5 -4
  3. package/conf/category-list.json +2 -2
  4. package/conf/config-schema.js +3 -1
  5. package/conf/eslint-recommended.js +11 -14
  6. package/lib/config/config-file.js +5 -5
  7. package/lib/config/config-initializer.js +123 -14
  8. package/lib/config/config-validator.js +25 -1
  9. package/lib/config/plugins.js +13 -1
  10. package/lib/formatters/junit.js +2 -8
  11. package/lib/formatters/stylish.js +2 -1
  12. package/lib/linter.js +21 -13
  13. package/lib/rule-context.js +53 -41
  14. package/lib/rules/arrow-parens.js +1 -1
  15. package/lib/rules/curly.js +1 -1
  16. package/lib/rules/getter-return.js +34 -9
  17. package/lib/rules/id-blacklist.js +7 -3
  18. package/lib/rules/id-match.js +8 -4
  19. package/lib/rules/indent-legacy.js +2 -2
  20. package/lib/rules/indent.js +451 -380
  21. package/lib/rules/key-spacing.js +2 -2
  22. package/lib/rules/no-cond-assign.js +7 -3
  23. package/lib/rules/no-constant-condition.js +62 -6
  24. package/lib/rules/no-else-return.js +1 -1
  25. package/lib/rules/no-extra-parens.js +3 -1
  26. package/lib/rules/no-inner-declarations.js +8 -4
  27. package/lib/rules/no-multi-spaces.js +53 -115
  28. package/lib/rules/no-regex-spaces.js +2 -2
  29. package/lib/rules/no-restricted-globals.js +50 -9
  30. package/lib/rules/no-restricted-properties.js +19 -11
  31. package/lib/rules/no-tabs.js +8 -4
  32. package/lib/rules/no-underscore-dangle.js +28 -1
  33. package/lib/rules/object-curly-newline.js +18 -0
  34. package/lib/rules/object-curly-spacing.js +1 -1
  35. package/lib/rules/padded-blocks.js +2 -2
  36. package/lib/rules/padding-line-between-statements.js +1 -1
  37. package/lib/rules/prefer-destructuring.js +70 -32
  38. package/lib/rules/prefer-numeric-literals.js +36 -7
  39. package/lib/rules/prefer-reflect.js +8 -4
  40. package/lib/rules/prefer-template.js +2 -2
  41. package/lib/rules/space-infix-ops.js +1 -1
  42. package/lib/rules/spaced-comment.js +2 -2
  43. package/lib/rules/valid-jsdoc.js +15 -7
  44. package/lib/testers/rule-tester.js +13 -21
  45. package/lib/testers/test-parser.js +48 -0
  46. package/lib/util/npm-util.js +9 -8
  47. package/package.json +11 -6
package/CHANGELOG.md CHANGED
@@ -1,3 +1,83 @@
1
+ v4.5.0 - August 18, 2017
2
+
3
+ * decdd2c Update: allow arbitrary nodes to be ignored in `indent` (fixes #8594) (#9105) (Teddy Katz)
4
+ * 79062f3 Update: fix indentation of multiline `new.target` expressions (#9116) (Teddy Katz)
5
+ * d00e24f Upgrade: `chalk` to 2.x release (#9115) (Stephen Edgar)
6
+ * 6ef734a Docs: add missing word in processor documentation (#9106) (Teddy Katz)
7
+ * a4f53ba Fix: Include files with no messages in junit results (#9093) (#9094) (Sean DuBois)
8
+ * 1d6a9c0 Chore: enable eslint-plugin/test-case-shorthand-strings (#9067) (薛定谔的猫)
9
+ * f8add8f Fix: don't autofix with linter.verifyAndFix when `fix: false` is used (#9098) (Teddy Katz)
10
+ * 77bcee4 Docs: update instructions for adding TSC members (#9086) (Teddy Katz)
11
+ * bd09cd5 Update: avoid requiring NaN spaces of indentation (fixes #9083) (#9085) (Teddy Katz)
12
+ * c93a853 Chore: Remove extra space in blogpost template (#9088) (Kai Cataldo)
13
+
14
+ v4.4.1 - August 7, 2017
15
+
16
+ * ec93614 Fix: no-multi-spaces to avoid reporting consecutive tabs (fixes #9079) (#9087) (Teddy Katz)
17
+
18
+ v4.4.0 - August 5, 2017
19
+
20
+ * 89196fd Upgrade: Espree to 3.5.0 (#9074) (Gyandeep Singh)
21
+ * b3e4598 Fix: clarify AST and don't use `node.start`/`node.end` (fixes #8956) (#8984) (Toru Nagashima)
22
+ * 62911e4 Update: Add ImportDeclaration option to indent rule (#8955) (David Irvine)
23
+ * de75f9b Chore: enable object-curly-newline & object-property-newline.(fixes #9042) (#9068) (薛定谔的猫)
24
+ * 5ae8458 Docs: fix typo in object-shorthand.md (#9066) (Jon Berry)
25
+ * c3d5b39 Docs: clarify options descriptions (fixes #8875) (#9060) (Brandon Mailhiot)
26
+ * 37158c5 Docs: clarified behavior of globalReturn option (fixes #8953) (#9058) (Brandon Mailhiot)
27
+ * c2f3553 Docs: Update example for MemberExpression option of indent (fixes #9056) (#9057) (Jeff)
28
+ * 78a85e0 Fix: no-extra-parens incorrectly reports async function expressions (#9035) (薛定谔的猫)
29
+ * c794f86 Fix: getter-return reporting method named 'get' (fixes #8919) (#9004) (薛定谔的猫)
30
+ * d0f78ec Docs: update rule deprecation policy (fixes #8635) (#9033) (Teddy Katz)
31
+ * 5ab282f Fix: Print error message in bin/eslint.js (fixes #9011) (#9041) (Victor Hom)
32
+ * 50e3cf3 Docs: Update sort-keys doc to define natural ordering (fixes #9043) (#9045) (Karan Sharma)
33
+ * 7ecfe6a Chore: enable eslint-plugin/test-case-property-ordering (#9040) (薛定谔的猫)
34
+ * ad32697 Upgrade: js-yaml to 3.9.1 (refs #9011) (#9044) (Teddy Katz)
35
+ * 66c1d43 Docs: Create SUPPORT.md (#9031) (Teddy Katz)
36
+ * 7247b6c Update: handle indentation of custom destructuring syntax (fixes #8990) (#9027) (Teddy Katz)
37
+ * cdb82f2 Fix: padding-line-between-statements crash on semicolons after blocks (#8748) (Alexander Madyankin)
38
+ * 3141872 Chore: remove unnecessary eslint-disable comments in codebase (#9032) (Teddy Katz)
39
+ * 0f97279 Fix: refactor no-multi-spaces to avoid regex backtracking (fixes #9001) (#9008) (Teddy Katz)
40
+ * b74514d Fix: refactor RuleContext to not modify report locations (fixes #8980) (#8997) (Teddy Katz)
41
+ * 31d7fd2 Fix: inconsistent `indent` behavior on computed properties (fixes #8989) (#8999) (Teddy Katz)
42
+ * 3393894 Fix: avoid reporting the entire AST for missing rules (#8998) (Teddy Katz)
43
+ * b3b95b8 Chore: enable additional rules on ESLint codebase (#9013) (Teddy Katz)
44
+ * 9b6c552 Upgrade: eslint-plugin-eslint-plugin@0.8.0 (#9012) (薛定谔的猫)
45
+ * acbe86a Chore: disallow .substr and .substring in favor of .slice (#9010) (Teddy Katz)
46
+ * d0536d6 Chore: Optimizes adding Linter methods (fixes #9000) (#9007) (Sean C Denison)
47
+ * 0a0401f Chore: fix spelling error. (#9003) (薛定谔的猫)
48
+ * 3d020b9 Update: emit a warning for ecmaFeatures rather than throwing an error (#8974) (Teddy Katz)
49
+ * d2f8f9f Fix: include name of invalid config in validation messages (fixes #8963) (#8973) (Teddy Katz)
50
+ * c3ee46b Chore: fix misleading comment in RuleTester (#8995) (Teddy Katz)
51
+
52
+ v4.3.0 - July 21, 2017
53
+
54
+ * 91dccdf Update: support more options in prefer-destructuring (#8796) (Victor Hom)
55
+ * 3bebcfd Update: Support generator yields in no constant condition (#8762) (Victor Hom)
56
+ * 96df8c9 Fix: Handle fixing objects containing comments (fixes #8484) (#8944) (Brian Schemp)
57
+ * e39d41d Docs: Make `peerDependencies` package.json snippet valid JSON (#8971) (Sam Adams)
58
+ * a5fd101 Fix: duplicated error message if a crash occurs (fixes #8964) (#8965) (Teddy Katz)
59
+ * f8d122c Docs: trailing commas not allowed in json (#8969) (Scott Fletcher)
60
+ * d09288a Chore: Use `output: null` to assert that a test case is not autofixed. (#8960) (薛定谔的猫)
61
+ * e639358 Update: add question to confirm downgrade (fixes #8870) (#8911) (Toru Nagashima)
62
+ * 601039d Docs: fix badge in eslint-config-eslint readme (#8954) (Teddy Katz)
63
+ * 3c231fa Update: add enforceInMethodNames to no-underscore-dangle (fixes #7065) (#7234) (Gabriele Petronella)
64
+ * 128591f Update: prefer-numeric-literals warns Number.parseInt (fixes #8913) (#8929) (Kevin Partington)
65
+ * 846f8b1 Docs: Clarified that core PRs require issue in maintainer guide (#8927) (Kevin Partington)
66
+ * 55bc35d Fix: Avoid shell mangling during eslint --init (#8936) (Anders Kaseorg)
67
+ * 10c3d78 Chore: fix misleading `indent` test (#8925) (Teddy Katz)
68
+ * fb8005d Update: no-restricted-globals custom error messages (fixes #8315) (#8932) (Kevin Partington)
69
+ * a747b6f Chore: make minor improvements to `indent` internals (#8947) (Teddy Katz)
70
+ * 1ea3723 Update: fix indentation of parenthesized MemberExpressions (fixes #8924) (#8928) (Teddy Katz)
71
+ * 9abc6f7 Update: fix BinaryExpression indentation edge case (fixes #8914) (#8930) (Teddy Katz)
72
+ * 0e90453 Docs: Fixing broken cyclomatic complexity link (fixes #8396) (#8937) (Chris Bargren)
73
+ * a8a8350 Chore: improve performance of `indent` rule (#8905) (Teddy Katz)
74
+ * 764b2a9 Chore: update header info in `indent` (#8926) (Teddy Katz)
75
+ * 597c217 Fix: confusing error if plugins from config is not an array (#8888) (Calvin Freitas)
76
+ * 3c1dd6d Docs: add description of no-sync `allowAtRootLevel` option (fixes #8902) (#8906) (Teddy Katz)
77
+ * 933a9cf Chore: add a fuzzer to detect bugs in core rules (#8422) (Teddy Katz)
78
+ * 45f8cd9 Docs: fix verifyAndFix result property name (#8903) (Tino Vyatkin)
79
+ * 1a89e1c Docs: Fix always-multiline example in multiline-ternary docs (#8904) (Nathan Woltman)
80
+
1
81
  v4.2.0 - July 8, 2017
2
82
 
3
83
  * e0f0101 Update: fix indentation of nested function parameters (fixes #8892) (#8900) (Teddy Katz)
package/bin/eslint.js CHANGED
@@ -44,11 +44,12 @@ process.once("uncaughtException", err => {
44
44
  if (typeof err.messageTemplate === "string" && err.messageTemplate.length > 0) {
45
45
  const template = lodash.template(fs.readFileSync(path.resolve(__dirname, `../messages/${err.messageTemplate}.txt`), "utf-8"));
46
46
 
47
- console.log("\nOops! Something went wrong! :(");
48
- console.log(`\n${template(err.messageData || {})}`);
47
+ console.error("\nOops! Something went wrong! :(");
48
+ console.error(`\n${template(err.messageData || {})}`);
49
49
  } else {
50
- console.log(err.message);
51
- console.log(err.stack);
50
+
51
+ console.error(err.message);
52
+ console.error(err.stack);
52
53
  }
53
54
 
54
55
  process.exitCode = 1;
@@ -10,12 +10,12 @@
10
10
  ],
11
11
  "deprecated": {
12
12
  "name": "Deprecated",
13
- "description": "These rules have been deprecated and replaced by newer rules:",
13
+ "description": "These rules have been deprecated in accordance with the [deprecation policy](/docs/user-guide/rule-deprecation), and replaced by newer rules:",
14
14
  "rules": []
15
15
  },
16
16
  "removed": {
17
17
  "name": "Removed",
18
- "description": "These rules from older versions of ESLint have been replaced by newer rules:",
18
+ "description": "These rules from older versions of ESLint (before the [deprecation policy](/docs/user-guide/rule-deprecation) existed) have been replaced by newer rules:",
19
19
  "rules": [
20
20
  { "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
21
21
  { "removed": "global-strict", "replacedBy": ["strict"] },
@@ -12,7 +12,9 @@ const baseConfigProperties = {
12
12
  parserOptions: { type: "object" },
13
13
  plugins: { type: "array" },
14
14
  rules: { type: "object" },
15
- settings: { type: "object" }
15
+ settings: { type: "object" },
16
+
17
+ ecmaFeatures: { type: "object" } // deprecated; logs a warning when used
16
18
  };
17
19
 
18
20
  const overrideProperties = Object.assign(
@@ -6,13 +6,10 @@
6
6
 
7
7
  "use strict";
8
8
 
9
- /* eslint sort-keys: ["error", "asc"], quote-props: ["error", "consistent"] */
10
- /* eslint-disable sort-keys */
9
+ /* eslint sort-keys: ["error", "asc"] */
11
10
 
12
11
  module.exports = {
13
12
  rules: {
14
-
15
- /* eslint-enable sort-keys */
16
13
  "accessor-pairs": "off",
17
14
  "array-bracket-newline": "off",
18
15
  "array-bracket-spacing": "off",
@@ -25,23 +22,23 @@ module.exports = {
25
22
  "block-spacing": "off",
26
23
  "brace-style": "off",
27
24
  "callback-return": "off",
28
- "camelcase": "off",
25
+ camelcase: "off",
29
26
  "capitalized-comments": "off",
30
27
  "class-methods-use-this": "off",
31
28
  "comma-dangle": "off",
32
29
  "comma-spacing": "off",
33
30
  "comma-style": "off",
34
- "complexity": "off",
31
+ complexity: "off",
35
32
  "computed-property-spacing": "off",
36
33
  "consistent-return": "off",
37
34
  "consistent-this": "off",
38
35
  "constructor-super": "error",
39
- "curly": "off",
36
+ curly: "off",
40
37
  "default-case": "off",
41
38
  "dot-location": "off",
42
39
  "dot-notation": "off",
43
40
  "eol-last": "off",
44
- "eqeqeq": "off",
41
+ eqeqeq: "off",
45
42
  "for-direction": "off",
46
43
  "func-call-spacing": "off",
47
44
  "func-name-matching": "off",
@@ -55,7 +52,7 @@ module.exports = {
55
52
  "id-blacklist": "off",
56
53
  "id-length": "off",
57
54
  "id-match": "off",
58
- "indent": "off",
55
+ indent: "off",
59
56
  "indent-legacy": "off",
60
57
  "init-declarations": "off",
61
58
  "jsx-quotes": "off",
@@ -234,13 +231,13 @@ module.exports = {
234
231
  "prefer-spread": "off",
235
232
  "prefer-template": "off",
236
233
  "quote-props": "off",
237
- "quotes": "off",
238
- "radix": "off",
234
+ quotes: "off",
235
+ radix: "off",
239
236
  "require-await": "off",
240
237
  "require-jsdoc": "off",
241
238
  "require-yield": "error",
242
239
  "rest-spread-spacing": "off",
243
- "semi": "off",
240
+ semi: "off",
244
241
  "semi-spacing": "off",
245
242
  "semi-style": "off",
246
243
  "sort-imports": "off",
@@ -252,7 +249,7 @@ module.exports = {
252
249
  "space-infix-ops": "off",
253
250
  "space-unary-ops": "off",
254
251
  "spaced-comment": "off",
255
- "strict": "off",
252
+ strict: "off",
256
253
  "switch-colon-spacing": "off",
257
254
  "symbol-description": "off",
258
255
  "template-curly-spacing": "off",
@@ -265,6 +262,6 @@ module.exports = {
265
262
  "wrap-iife": "off",
266
263
  "wrap-regex": "off",
267
264
  "yield-star-spacing": "off",
268
- "yoda": "off"
265
+ yoda: "off"
269
266
  }
270
267
  };
@@ -3,8 +3,6 @@
3
3
  * @author Nicholas C. Zakas
4
4
  */
5
5
 
6
- /* eslint no-use-before-define: 0 */
7
-
8
6
  "use strict";
9
7
 
10
8
  //------------------------------------------------------------------------------
@@ -418,6 +416,8 @@ function applyExtends(config, configContext, filePath, relativeTo) {
418
416
  );
419
417
  }
420
418
  debug(`Loading ${parentPath}`);
419
+
420
+ // eslint-disable-next-line no-use-before-define
421
421
  return ConfigOps.merge(load(parentPath, configContext, relativeTo), previousValue);
422
422
  } catch (e) {
423
423
 
@@ -502,8 +502,8 @@ function resolve(filePath, relativeTo) {
502
502
 
503
503
  if (filePath.startsWith("plugin:")) {
504
504
  const configFullName = filePath;
505
- const pluginName = filePath.substr(7, filePath.lastIndexOf("/") - 7);
506
- const configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1);
505
+ const pluginName = filePath.slice(7, filePath.lastIndexOf("/"));
506
+ const configName = filePath.slice(filePath.lastIndexOf("/") + 1);
507
507
 
508
508
  normalizedPackageName = normalizePackageName(pluginName, "eslint-plugin");
509
509
  debug(`Attempting to resolve ${normalizedPackageName}`);
@@ -546,7 +546,7 @@ function loadFromDisk(resolvedPath, configContext) {
546
546
  }
547
547
 
548
548
  // validate the configuration before continuing
549
- validator.validate(config, resolvedPath, configContext.linterContext.rules, configContext.linterContext.environments);
549
+ validator.validate(config, resolvedPath.configFullName, configContext.linterContext.rules, configContext.linterContext.environments);
550
550
 
551
551
  /*
552
552
  * If an `extends` property is defined, it represents a configuration file to use as
@@ -12,10 +12,12 @@
12
12
  const util = require("util"),
13
13
  inquirer = require("inquirer"),
14
14
  ProgressBar = require("progress"),
15
+ semver = require("semver"),
15
16
  autoconfig = require("./autoconfig.js"),
16
17
  ConfigFile = require("./config-file"),
17
18
  ConfigOps = require("./config-ops"),
18
19
  getSourceCodeOfFiles = require("../util/source-code-util").getSourceCodeOfFiles,
20
+ ModuleResolver = require("../util/module-resolver"),
19
21
  npmUtil = require("../util/npm-util"),
20
22
  recConfig = require("../../conf/eslint-recommended"),
21
23
  log = require("../logging");
@@ -56,12 +58,35 @@ function writeFile(config, format) {
56
58
  }
57
59
  }
58
60
 
61
+ /**
62
+ * Get the peer dependencies of the given module.
63
+ * This adds the gotten value to cache at the first time, then reuses it.
64
+ * In a process, this function is called twice, but `npmUtil.fetchPeerDependencies` needs to access network which is relatively slow.
65
+ * @param {string} moduleName The module name to get.
66
+ * @returns {Object} The peer dependencies of the given module.
67
+ * This object is the object of `peerDependencies` field of `package.json`.
68
+ */
69
+ function getPeerDependencies(moduleName) {
70
+ let result = getPeerDependencies.cache.get(moduleName);
71
+
72
+ if (!result) {
73
+ log.info(`Checking peerDependencies of ${moduleName}`);
74
+
75
+ result = npmUtil.fetchPeerDependencies(moduleName);
76
+ getPeerDependencies.cache.set(moduleName, result);
77
+ }
78
+
79
+ return result;
80
+ }
81
+ getPeerDependencies.cache = new Map();
82
+
59
83
  /**
60
84
  * Synchronously install necessary plugins, configs, parsers, etc. based on the config
61
85
  * @param {Object} config config object
86
+ * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
62
87
  * @returns {void}
63
88
  */
64
- function installModules(config) {
89
+ function installModules(config, installESLint) {
65
90
  const modules = {};
66
91
 
67
92
  // Create a list of modules which should be installed based on config
@@ -73,11 +98,10 @@ function installModules(config) {
73
98
  if (config.extends && config.extends.indexOf("eslint:") === -1) {
74
99
  const moduleName = `eslint-config-${config.extends}`;
75
100
 
76
- log.info(`Checking peerDependencies of ${moduleName}`);
77
101
  modules[moduleName] = "latest";
78
102
  Object.assign(
79
103
  modules,
80
- npmUtil.fetchPeerDependencies(`${moduleName}@latest`)
104
+ getPeerDependencies(`${moduleName}@latest`)
81
105
  );
82
106
  }
83
107
 
@@ -86,15 +110,17 @@ function installModules(config) {
86
110
  return;
87
111
  }
88
112
 
89
- // Add eslint to list in case user does not have it installed locally
90
- modules.eslint = modules.eslint || "latest";
91
-
92
- // Mark to show messages if it's new installation of eslint.
93
- const installStatus = npmUtil.checkDevDeps(["eslint"]);
113
+ if (installESLint === false) {
114
+ delete modules.eslint;
115
+ } else {
116
+ const installStatus = npmUtil.checkDevDeps(["eslint"]);
94
117
 
95
- if (installStatus.eslint === false) {
96
- log.info("Local ESLint installation not found.");
97
- config.installedESLint = true;
118
+ // Mark to show messages if it's new installation of eslint.
119
+ if (installStatus.eslint === false) {
120
+ log.info("Local ESLint installation not found.");
121
+ modules.eslint = modules.eslint || "latest";
122
+ config.installedESLint = true;
123
+ }
98
124
  }
99
125
 
100
126
  // Install packages
@@ -265,9 +291,10 @@ function processAnswers(answers) {
265
291
  /**
266
292
  * process user's style guide of choice and return an appropriate config object.
267
293
  * @param {string} guide name of the chosen style guide
294
+ * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
268
295
  * @returns {Object} config object
269
296
  */
270
- function getConfigForStyleGuide(guide) {
297
+ function getConfigForStyleGuide(guide, installESLint) {
271
298
  const guides = {
272
299
  google: { extends: "google" },
273
300
  airbnb: { extends: "airbnb" },
@@ -279,11 +306,74 @@ function getConfigForStyleGuide(guide) {
279
306
  throw new Error("You referenced an unsupported guide.");
280
307
  }
281
308
 
282
- installModules(guides[guide]);
309
+ installModules(guides[guide], installESLint);
283
310
 
284
311
  return guides[guide];
285
312
  }
286
313
 
314
+ /**
315
+ * Get the version of the local ESLint.
316
+ * @returns {string|null} The version. If the local ESLint was not found, returns null.
317
+ */
318
+ function getLocalESLintVersion() {
319
+ try {
320
+ const resolver = new ModuleResolver();
321
+ const eslintPath = resolver.resolve("eslint", process.cwd());
322
+ const eslint = require(eslintPath);
323
+
324
+ return eslint.linter.version || null;
325
+ } catch (_err) {
326
+ return null;
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Get the shareable config name of the chosen style guide.
332
+ * @param {Object} answers The answers object.
333
+ * @returns {string} The shareable config name.
334
+ */
335
+ function getStyleGuideName(answers) {
336
+ if (answers.styleguide === "airbnb" && !answers.airbnbReact) {
337
+ return "airbnb-base";
338
+ }
339
+ return answers.styleguide;
340
+ }
341
+
342
+ /**
343
+ * Check whether the local ESLint version conflicts with the required version of the chosen shareable config.
344
+ * @param {Object} answers The answers object.
345
+ * @returns {boolean} `true` if the local ESLint is found then it conflicts with the required version of the chosen shareable config.
346
+ */
347
+ function hasESLintVersionConflict(answers) {
348
+
349
+ // Get the local ESLint version.
350
+ const localESLintVersion = getLocalESLintVersion();
351
+
352
+ if (!localESLintVersion) {
353
+ return false;
354
+ }
355
+
356
+ // Get the required range of ESLint version.
357
+ const configName = getStyleGuideName(answers);
358
+ const moduleName = `eslint-config-${configName}@latest`;
359
+ const requiredESLintVersionRange = getPeerDependencies(moduleName).eslint;
360
+
361
+ if (!requiredESLintVersionRange) {
362
+ return false;
363
+ }
364
+
365
+ answers.localESLintVersion = localESLintVersion;
366
+ answers.requiredESLintVersionRange = requiredESLintVersionRange;
367
+
368
+ // Check the version.
369
+ if (semver.satisfies(localESLintVersion, requiredESLintVersionRange)) {
370
+ answers.installESLint = false;
371
+ return false;
372
+ }
373
+
374
+ return true;
375
+ }
376
+
287
377
  /* istanbul ignore next: no need to test inquirer*/
288
378
  /**
289
379
  * Ask use a few questions on command prompt
@@ -346,6 +436,21 @@ function promptUser() {
346
436
  when(answers) {
347
437
  return ((answers.source === "guide" && answers.packageJsonExists) || answers.source === "auto");
348
438
  }
439
+ },
440
+ {
441
+ type: "confirm",
442
+ name: "installESLint",
443
+ message(answers) {
444
+ const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange)
445
+ ? "upgrade"
446
+ : "downgrade";
447
+
448
+ return `The style guide "${answers.styleguide}" requires eslint@${answers.requiredESLintVersionRange}. You are currently using eslint@${answers.localESLintVersion}.\n Do you want to ${verb}?`;
449
+ },
450
+ default: true,
451
+ when(answers) {
452
+ return answers.source === "guide" && answers.packageJsonExists && hasESLintVersionConflict(answers);
453
+ }
349
454
  }
350
455
  ]).then(earlyAnswers => {
351
456
 
@@ -355,11 +460,14 @@ function promptUser() {
355
460
  log.info("A package.json is necessary to install plugins such as style guides. Run `npm init` to create a package.json file and try again.");
356
461
  return void 0;
357
462
  }
463
+ if (earlyAnswers.installESLint === false && !semver.satisfies(earlyAnswers.localESLintVersion, earlyAnswers.requiredESLintVersionRange)) {
464
+ log.info(`Note: it might not work since ESLint's version is mismatched with the ${earlyAnswers.styleguide} config.`);
465
+ }
358
466
  if (earlyAnswers.styleguide === "airbnb" && !earlyAnswers.airbnbReact) {
359
467
  earlyAnswers.styleguide = "airbnb-base";
360
468
  }
361
469
 
362
- config = getConfigForStyleGuide(earlyAnswers.styleguide);
470
+ config = getConfigForStyleGuide(earlyAnswers.styleguide, earlyAnswers.installESLint);
363
471
  writeFile(config, earlyAnswers.format);
364
472
 
365
473
  return void 0;
@@ -479,6 +587,7 @@ function promptUser() {
479
587
 
480
588
  const init = {
481
589
  getConfigForStyleGuide,
590
+ hasESLintVersionConflict,
482
591
  processAnswers,
483
592
  /* istanbul ignore next */initializeConfig() {
484
593
  return promptUser();
@@ -10,6 +10,7 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const ajv = require("../util/ajv"),
13
+ lodash = require("lodash"),
13
14
  configSchema = require("../../conf/config-schema.js"),
14
15
  util = require("util");
15
16
 
@@ -179,6 +180,25 @@ function formatErrors(errors) {
179
180
  }).map(message => `\t- ${message}.\n`).join("");
180
181
  }
181
182
 
183
+ /**
184
+ * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
185
+ * for each unique file path, but repeated invocations with the same file path have no effect.
186
+ * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active.
187
+ * @param {string} source The name of the configuration source to report the warning for.
188
+ * @returns {void}
189
+ */
190
+ const emitEcmaFeaturesWarning = lodash.memoize(source => {
191
+
192
+ /*
193
+ * util.deprecate seems to be the only way to emit a warning in Node 4.x while respecting the --no-warnings flag.
194
+ * (In Node 6+, process.emitWarning could be used instead.)
195
+ */
196
+ util.deprecate(
197
+ () => {},
198
+ `[eslint] The 'ecmaFeatures' config file property is deprecated, and has no effect. (found in ${source})`
199
+ )();
200
+ });
201
+
182
202
  /**
183
203
  * Validates the top level properties of the config object.
184
204
  * @param {Object} config The config object to validate.
@@ -189,7 +209,11 @@ function validateConfigSchema(config, source) {
189
209
  validateSchema = validateSchema || ajv.compile(configSchema);
190
210
 
191
211
  if (!validateSchema(config)) {
192
- throw new Error(`${source}:\n\tESLint configuration is invalid:\n${formatErrors(validateSchema.errors)}`);
212
+ throw new Error(`ESLint configuration in ${source} is invalid:\n${formatErrors(validateSchema.errors)}`);
213
+ }
214
+
215
+ if (Object.prototype.hasOwnProperty.call(config, "ecmaFeatures")) {
216
+ emitEcmaFeaturesWarning(source);
193
217
  }
194
218
  }
195
219
 
@@ -43,7 +43,7 @@ class Plugins {
43
43
  * @returns {string} The name of the plugin without prefix.
44
44
  */
45
45
  static removePrefix(pluginName) {
46
- return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.substring(PLUGIN_NAME_PREFIX.length) : pluginName;
46
+ return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.slice(PLUGIN_NAME_PREFIX.length) : pluginName;
47
47
  }
48
48
 
49
49
  /**
@@ -156,8 +156,20 @@ class Plugins {
156
156
  * @param {string[]} pluginNames An array of plugins names.
157
157
  * @returns {void}
158
158
  * @throws {Error} If a plugin cannot be loaded.
159
+ * @throws {Error} If "plugins" in config is not an array
159
160
  */
160
161
  loadAll(pluginNames) {
162
+
163
+ // if "plugins" in config is not an array, throw an error so user can fix their config.
164
+ if (!Array.isArray(pluginNames)) {
165
+ const pluginNotArrayMessage = "ESLint configuration error: \"plugins\" value must be an array";
166
+
167
+ debug(`${pluginNotArrayMessage}: ${JSON.stringify(pluginNames)}`);
168
+
169
+ throw new Error(pluginNotArrayMessage);
170
+ }
171
+
172
+ // load each plugin by name
161
173
  pluginNames.forEach(this.load, this);
162
174
  }
163
175
  }
@@ -39,10 +39,7 @@ module.exports = function(results) {
39
39
 
40
40
  const messages = result.messages;
41
41
 
42
- if (messages.length) {
43
- output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
44
- }
45
-
42
+ output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
46
43
  messages.forEach(message => {
47
44
  const type = message.fatal ? "error" : "failure";
48
45
 
@@ -57,10 +54,7 @@ module.exports = function(results) {
57
54
  output += `</${type}>`;
58
55
  output += "</testcase>\n";
59
56
  });
60
-
61
- if (messages.length) {
62
- output += "</testsuite>\n";
63
- }
57
+ output += "</testsuite>\n";
64
58
 
65
59
  });
66
60
 
@@ -5,6 +5,7 @@
5
5
  "use strict";
6
6
 
7
7
  const chalk = require("chalk"),
8
+ stripAnsi = require("strip-ansi"),
8
9
  table = require("text-table");
9
10
 
10
11
  //------------------------------------------------------------------------------
@@ -71,7 +72,7 @@ module.exports = function(results) {
71
72
  {
72
73
  align: ["", "r", "l"],
73
74
  stringLength(str) {
74
- return chalk.stripColor(str).length;
75
+ return stripAnsi(str).length;
75
76
  }
76
77
  }
77
78
  ).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
package/lib/linter.js CHANGED
@@ -70,8 +70,8 @@ function parseBooleanConfig(string, comment) {
70
70
  let value;
71
71
 
72
72
  if (pos !== -1) {
73
- value = name.substring(pos + 1, name.length);
74
- name = name.substring(0, pos);
73
+ value = name.slice(pos + 1);
74
+ name = name.slice(0, pos);
75
75
  }
76
76
 
77
77
  items[name] = {
@@ -338,7 +338,7 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
338
338
  const match = /^(eslint(-\w+){0,3}|exported|globals?)(\s|$)/.exec(value);
339
339
 
340
340
  if (match) {
341
- value = value.substring(match.index + match[1].length);
341
+ value = value.slice(match.index + match[1].length);
342
342
 
343
343
  if (comment.type === "Block") {
344
344
  switch (match[1]) {
@@ -519,8 +519,11 @@ function createStubRule(message) {
519
519
  */
520
520
  function createRuleModule(context) {
521
521
  return {
522
- Program(node) {
523
- context.report(node, message);
522
+ Program() {
523
+ context.report({
524
+ loc: { line: 1, column: 0 },
525
+ message
526
+ });
524
527
  }
525
528
  };
526
529
  }
@@ -1207,7 +1210,7 @@ class Linter extends EventEmitter {
1207
1210
  fixed = false,
1208
1211
  passNumber = 0;
1209
1212
  const debugTextDescription = options && options.filename || `${text.slice(0, 10)}...`;
1210
- const shouldFix = options && options.fix || true;
1213
+ const shouldFix = options && typeof options.fix !== "undefined" ? options.fix : true;
1211
1214
 
1212
1215
  /**
1213
1216
  * This loop continues until one of the following is true:
@@ -1288,13 +1291,18 @@ const externalMethods = {
1288
1291
  Object.keys(externalMethods).forEach(methodName => {
1289
1292
  const exMethodName = externalMethods[methodName];
1290
1293
 
1291
- // All functions expected to have less arguments than 5.
1292
- Linter.prototype[methodName] = function(a, b, c, d, e) {
1293
- if (this.sourceCode) {
1294
- return this.sourceCode[exMethodName](a, b, c, d, e);
1295
- }
1296
- return null;
1297
- };
1294
+ // Applies the SourceCode methods to the Linter prototype
1295
+ Object.defineProperty(Linter.prototype, methodName, {
1296
+ value() {
1297
+ if (this.sourceCode) {
1298
+ return this.sourceCode[exMethodName].apply(this.sourceCode, arguments);
1299
+ }
1300
+ return null;
1301
+ },
1302
+ configurable: true,
1303
+ writable: true,
1304
+ enumerable: false
1305
+ });
1298
1306
  });
1299
1307
 
1300
1308
  module.exports = Linter;