eslint 4.7.2 → 4.11.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 (139) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +34 -19
  3. package/conf/default-cli-options.js +7 -4
  4. package/conf/eslint-recommended.js +2 -0
  5. package/lib/ast-utils.js +83 -42
  6. package/lib/cli-engine.js +53 -17
  7. package/lib/cli.js +17 -9
  8. package/lib/code-path-analysis/code-path-analyzer.js +8 -4
  9. package/lib/code-path-analysis/code-path-segment.js +43 -41
  10. package/lib/code-path-analysis/code-path-state.js +7 -2
  11. package/lib/config/autoconfig.js +14 -12
  12. package/lib/config/config-file.js +8 -51
  13. package/lib/config/config-initializer.js +10 -6
  14. package/lib/config/config-ops.js +21 -21
  15. package/lib/config/config-rule.js +24 -24
  16. package/lib/config/config-validator.js +38 -36
  17. package/lib/config/plugins.js +8 -35
  18. package/lib/config.js +12 -8
  19. package/lib/formatters/html-template-message.html +1 -1
  20. package/lib/formatters/html-template-page.html +3 -1
  21. package/lib/formatters/html.js +2 -1
  22. package/lib/formatters/junit.js +21 -15
  23. package/lib/formatters/tap.js +5 -3
  24. package/lib/ignored-paths.js +5 -3
  25. package/lib/linter.js +42 -42
  26. package/lib/logging.js +2 -2
  27. package/lib/options.js +12 -0
  28. package/lib/rules/.eslintrc.yml +2 -2
  29. package/lib/rules/array-bracket-newline.js +39 -25
  30. package/lib/rules/array-bracket-spacing.js +28 -28
  31. package/lib/rules/array-callback-return.js +13 -9
  32. package/lib/rules/array-element-newline.js +8 -8
  33. package/lib/rules/arrow-body-style.js +12 -6
  34. package/lib/rules/arrow-parens.js +4 -2
  35. package/lib/rules/block-spacing.js +1 -1
  36. package/lib/rules/brace-style.js +14 -14
  37. package/lib/rules/callback-return.js +2 -1
  38. package/lib/rules/capitalized-comments.js +2 -1
  39. package/lib/rules/comma-style.js +3 -1
  40. package/lib/rules/computed-property-spacing.js +22 -22
  41. package/lib/rules/consistent-return.js +4 -4
  42. package/lib/rules/consistent-this.js +4 -2
  43. package/lib/rules/curly.js +13 -9
  44. package/lib/rules/dot-notation.js +56 -35
  45. package/lib/rules/func-call-spacing.js +4 -2
  46. package/lib/rules/generator-star-spacing.js +3 -3
  47. package/lib/rules/getter-return.js +2 -1
  48. package/lib/rules/indent-legacy.js +25 -14
  49. package/lib/rules/indent.js +101 -91
  50. package/lib/rules/key-spacing.js +5 -3
  51. package/lib/rules/lines-around-comment.js +33 -4
  52. package/lib/rules/lines-around-directive.js +16 -12
  53. package/lib/rules/lines-between-class-members.js +91 -0
  54. package/lib/rules/max-len.js +2 -3
  55. package/lib/rules/max-statements-per-line.js +5 -3
  56. package/lib/rules/multiline-comment-style.js +294 -0
  57. package/lib/rules/new-cap.js +2 -1
  58. package/lib/rules/newline-after-var.js +8 -6
  59. package/lib/rules/newline-before-return.js +13 -9
  60. package/lib/rules/no-alert.js +7 -15
  61. package/lib/rules/no-await-in-loop.js +17 -9
  62. package/lib/rules/no-bitwise.js +5 -3
  63. package/lib/rules/no-catch-shadow.js +4 -2
  64. package/lib/rules/no-console.js +2 -1
  65. package/lib/rules/no-constant-condition.js +2 -2
  66. package/lib/rules/no-control-regex.js +2 -1
  67. package/lib/rules/no-else-return.js +60 -19
  68. package/lib/rules/no-empty-character-class.js +11 -11
  69. package/lib/rules/no-extra-parens.js +22 -11
  70. package/lib/rules/no-extra-semi.js +5 -3
  71. package/lib/rules/no-global-assign.js +4 -2
  72. package/lib/rules/no-implicit-coercion.js +6 -6
  73. package/lib/rules/no-implied-eval.js +2 -1
  74. package/lib/rules/no-label-var.js +4 -2
  75. package/lib/rules/no-lone-blocks.js +3 -3
  76. package/lib/rules/no-lonely-if.js +2 -1
  77. package/lib/rules/no-loop-func.js +10 -7
  78. package/lib/rules/no-mixed-requires.js +8 -4
  79. package/lib/rules/no-native-reassign.js +4 -2
  80. package/lib/rules/no-param-reassign.js +4 -2
  81. package/lib/rules/no-regex-spaces.js +1 -1
  82. package/lib/rules/no-restricted-imports.js +86 -17
  83. package/lib/rules/no-restricted-modules.js +84 -15
  84. package/lib/rules/no-restricted-properties.js +10 -10
  85. package/lib/rules/no-return-await.js +6 -6
  86. package/lib/rules/no-self-assign.js +4 -2
  87. package/lib/rules/no-sequences.js +6 -4
  88. package/lib/rules/no-trailing-spaces.js +14 -8
  89. package/lib/rules/no-unneeded-ternary.js +3 -1
  90. package/lib/rules/no-unreachable.js +4 -2
  91. package/lib/rules/no-unused-labels.js +2 -1
  92. package/lib/rules/no-use-before-define.js +13 -11
  93. package/lib/rules/no-useless-call.js +1 -25
  94. package/lib/rules/no-useless-computed-key.js +2 -1
  95. package/lib/rules/no-useless-escape.js +31 -23
  96. package/lib/rules/no-useless-return.js +14 -8
  97. package/lib/rules/no-var.js +11 -0
  98. package/lib/rules/no-whitespace-before-property.js +4 -2
  99. package/lib/rules/object-curly-newline.js +9 -2
  100. package/lib/rules/object-curly-spacing.js +20 -20
  101. package/lib/rules/object-shorthand.js +47 -35
  102. package/lib/rules/operator-assignment.js +9 -9
  103. package/lib/rules/operator-linebreak.js +15 -11
  104. package/lib/rules/padding-line-between-statements.js +6 -4
  105. package/lib/rules/prefer-arrow-callback.js +12 -10
  106. package/lib/rules/prefer-const.js +18 -10
  107. package/lib/rules/prefer-destructuring.js +4 -2
  108. package/lib/rules/prefer-numeric-literals.js +4 -2
  109. package/lib/rules/prefer-promise-reject-errors.js +16 -16
  110. package/lib/rules/prefer-rest-params.js +4 -2
  111. package/lib/rules/prefer-spread.js +1 -25
  112. package/lib/rules/prefer-template.js +33 -29
  113. package/lib/rules/quote-props.js +8 -8
  114. package/lib/rules/require-jsdoc.js +11 -18
  115. package/lib/rules/semi-style.js +44 -19
  116. package/lib/rules/semi.js +5 -3
  117. package/lib/rules/sort-imports.js +11 -6
  118. package/lib/rules/space-unary-ops.js +67 -69
  119. package/lib/rules/strict.js +8 -8
  120. package/lib/rules/valid-jsdoc.js +39 -33
  121. package/lib/rules/valid-typeof.js +4 -4
  122. package/lib/rules/wrap-iife.js +4 -4
  123. package/lib/rules/yoda.js +9 -7
  124. package/lib/testers/rule-tester.js +63 -40
  125. package/lib/token-store/backward-token-cursor.js +5 -3
  126. package/lib/token-store/forward-token-cursor.js +5 -3
  127. package/lib/token-store/utils.js +8 -4
  128. package/lib/util/apply-disable-directives.js +56 -27
  129. package/lib/util/glob.js +1 -1
  130. package/lib/util/naming.js +112 -0
  131. package/lib/util/node-event-generator.js +13 -27
  132. package/lib/util/safe-emitter.js +54 -0
  133. package/lib/util/source-code-fixer.js +4 -2
  134. package/lib/util/source-code.js +70 -65
  135. package/messages/no-config-found.txt +1 -1
  136. package/package.json +8 -8
  137. package/lib/internal-rules/.eslintrc.yml +0 -3
  138. package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
  139. package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
@@ -14,9 +14,7 @@ const ajv = require("../util/ajv"),
14
14
  configSchema = require("../../conf/config-schema.js"),
15
15
  util = require("util");
16
16
 
17
- const validators = {
18
- rules: Object.create(null)
19
- };
17
+ const ruleValidators = new WeakMap();
20
18
 
21
19
  //------------------------------------------------------------------------------
22
20
  // Private
@@ -25,13 +23,11 @@ let validateSchema;
25
23
 
26
24
  /**
27
25
  * Gets a complete options schema for a rule.
28
- * @param {string} id The rule's unique name.
29
- * @param {Rules} rulesContext Rule context
26
+ * @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
30
27
  * @returns {Object} JSON Schema for the rule's options.
31
28
  */
32
- function getRuleOptionsSchema(id, rulesContext) {
33
- const rule = rulesContext.get(id),
34
- schema = rule && rule.schema || rule && rule.meta && rule.meta.schema;
29
+ function getRuleOptionsSchema(rule) {
30
+ const schema = rule.schema || rule.meta && rule.meta.schema;
35
31
 
36
32
  // Given a tuple of schemas, insert warning level at the beginning
37
33
  if (Array.isArray(schema)) {
@@ -56,10 +52,10 @@ function getRuleOptionsSchema(id, rulesContext) {
56
52
  }
57
53
 
58
54
  /**
59
- * Validates a rule's severity and returns the severity value. Throws an error if the severity is invalid.
60
- * @param {options} options The given options for the rule.
61
- * @returns {number|string} The rule's severity value
62
- */
55
+ * Validates a rule's severity and returns the severity value. Throws an error if the severity is invalid.
56
+ * @param {options} options The given options for the rule.
57
+ * @returns {number|string} The rule's severity value
58
+ */
63
59
  function validateRuleSeverity(options) {
64
60
  const severity = Array.isArray(options) ? options[0] : options;
65
61
 
@@ -71,46 +67,52 @@ function validateRuleSeverity(options) {
71
67
  }
72
68
 
73
69
  /**
74
- * Validates the non-severity options passed to a rule, based on its schema.
75
- * @param {string} id The rule's unique name
76
- * @param {array} localOptions The options for the rule, excluding severity
77
- * @param {Rules} rulesContext Rule context
78
- * @returns {void}
79
- */
80
- function validateRuleSchema(id, localOptions, rulesContext) {
81
- const schema = getRuleOptionsSchema(id, rulesContext);
82
-
83
- if (!validators.rules[id] && schema) {
84
- validators.rules[id] = ajv.compile(schema);
70
+ * Validates the non-severity options passed to a rule, based on its schema.
71
+ * @param {{create: Function}} rule The rule to validate
72
+ * @param {array} localOptions The options for the rule, excluding severity
73
+ * @returns {void}
74
+ */
75
+ function validateRuleSchema(rule, localOptions) {
76
+ if (!ruleValidators.has(rule)) {
77
+ const schema = getRuleOptionsSchema(rule);
78
+
79
+ if (schema) {
80
+ ruleValidators.set(rule, ajv.compile(schema));
81
+ }
85
82
  }
86
83
 
87
- const validateRule = validators.rules[id];
84
+ const validateRule = ruleValidators.get(rule);
88
85
 
89
86
  if (validateRule) {
90
87
  validateRule(localOptions);
91
88
  if (validateRule.errors) {
92
- throw new Error(validateRule.errors.map(error => `\tValue "${error.data}" ${error.message}.\n`).join(""));
89
+ throw new Error(validateRule.errors.map(
90
+ error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
91
+ ).join(""));
93
92
  }
94
93
  }
95
94
  }
96
95
 
97
96
  /**
98
97
  * Validates a rule's options against its schema.
99
- * @param {string} id The rule's unique name.
98
+ * @param {{create: Function}|null} rule The rule that the config is being validated for
99
+ * @param {string} ruleId The rule's unique name.
100
100
  * @param {array|number} options The given options for the rule.
101
101
  * @param {string} source The name of the configuration source to report in any errors.
102
- * @param {Rules} rulesContext Rule context
103
102
  * @returns {void}
104
103
  */
105
- function validateRuleOptions(id, options, source, rulesContext) {
104
+ function validateRuleOptions(rule, ruleId, options, source) {
105
+ if (!rule) {
106
+ return;
107
+ }
106
108
  try {
107
109
  const severity = validateRuleSeverity(options);
108
110
 
109
111
  if (severity !== 0 && !(typeof severity === "string" && severity.toLowerCase() === "off")) {
110
- validateRuleSchema(id, Array.isArray(options) ? options.slice(1) : [], rulesContext);
112
+ validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
111
113
  }
112
114
  } catch (err) {
113
- throw new Error(`${source}:\n\tConfiguration for rule "${id}" is invalid:\n${err.message}`);
115
+ throw new Error(`${source}:\n\tConfiguration for rule "${ruleId}" is invalid:\n${err.message}`);
114
116
  }
115
117
  }
116
118
 
@@ -141,16 +143,16 @@ function validateEnvironment(environment, source, envContext) {
141
143
  * Validates a rules config object
142
144
  * @param {Object} rulesConfig The rules config object to validate.
143
145
  * @param {string} source The name of the configuration source to report in any errors.
144
- * @param {Rules} rulesContext Rule context
146
+ * @param {function(string): {create: Function}} ruleMapper A mapper function from strings to loaded rules
145
147
  * @returns {void}
146
148
  */
147
- function validateRules(rulesConfig, source, rulesContext) {
149
+ function validateRules(rulesConfig, source, ruleMapper) {
148
150
  if (!rulesConfig) {
149
151
  return;
150
152
  }
151
153
 
152
154
  Object.keys(rulesConfig).forEach(id => {
153
- validateRuleOptions(id, rulesConfig[id], source, rulesContext);
155
+ validateRuleOptions(ruleMapper(id), id, rulesConfig[id], source);
154
156
  });
155
157
  }
156
158
 
@@ -221,13 +223,13 @@ function validateConfigSchema(config, source) {
221
223
  * Validates an entire config object.
222
224
  * @param {Object} config The config object to validate.
223
225
  * @param {string} source The name of the configuration source to report in any errors.
224
- * @param {Rules} rulesContext The rules context
226
+ * @param {function(string): {create: Function}} ruleMapper A mapper function from rule IDs to defined rules
225
227
  * @param {Environments} envContext The env context
226
228
  * @returns {void}
227
229
  */
228
- function validate(config, source, rulesContext, envContext) {
230
+ function validate(config, source, ruleMapper, envContext) {
229
231
  validateConfigSchema(config, source);
230
- validateRules(config.rules, source, rulesContext);
232
+ validateRules(config.rules, source, ruleMapper);
231
233
  validateEnvironment(config.env, source, envContext);
232
234
  }
233
235
 
@@ -9,13 +9,13 @@
9
9
  //------------------------------------------------------------------------------
10
10
 
11
11
  const debug = require("debug")("eslint:plugins");
12
+ const naming = require("../util/naming");
12
13
 
13
14
  //------------------------------------------------------------------------------
14
15
  // Private
15
16
  //------------------------------------------------------------------------------
16
17
 
17
- const PLUGIN_NAME_PREFIX = "eslint-plugin-",
18
- NAMESPACE_REGEX = /^@.*\//i;
18
+ const PLUGIN_NAME_PREFIX = "eslint-plugin-";
19
19
 
20
20
  //------------------------------------------------------------------------------
21
21
  // Public Interface
@@ -37,33 +37,6 @@ class Plugins {
37
37
  this._rules = rulesContext;
38
38
  }
39
39
 
40
- /**
41
- * Removes the prefix `eslint-plugin-` from a plugin name.
42
- * @param {string} pluginName The name of the plugin which may have the prefix.
43
- * @returns {string} The name of the plugin without prefix.
44
- */
45
- static removePrefix(pluginName) {
46
- return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.slice(PLUGIN_NAME_PREFIX.length) : pluginName;
47
- }
48
-
49
- /**
50
- * Gets the scope (namespace) of a plugin.
51
- * @param {string} pluginName The name of the plugin which may have the prefix.
52
- * @returns {string} The name of the plugins namepace if it has one.
53
- */
54
- static getNamespace(pluginName) {
55
- return pluginName.match(NAMESPACE_REGEX) ? pluginName.match(NAMESPACE_REGEX)[0] : "";
56
- }
57
-
58
- /**
59
- * Removes the namespace from a plugin name.
60
- * @param {string} pluginName The name of the plugin which may have the prefix.
61
- * @returns {string} The name of the plugin without the namespace.
62
- */
63
- static removeNamespace(pluginName) {
64
- return pluginName.replace(NAMESPACE_REGEX, "");
65
- }
66
-
67
40
  /**
68
41
  * Defines a plugin with a given name rather than loading from disk.
69
42
  * @param {string} pluginName The name of the plugin to load.
@@ -71,9 +44,9 @@ class Plugins {
71
44
  * @returns {void}
72
45
  */
73
46
  define(pluginName, plugin) {
74
- const pluginNamespace = Plugins.getNamespace(pluginName),
75
- pluginNameWithoutNamespace = Plugins.removeNamespace(pluginName),
76
- pluginNameWithoutPrefix = Plugins.removePrefix(pluginNameWithoutNamespace),
47
+ const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
48
+ pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
49
+ pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
77
50
  shortName = pluginNamespace + pluginNameWithoutPrefix;
78
51
 
79
52
  // load up environments and rules
@@ -106,9 +79,9 @@ class Plugins {
106
79
  * @throws {Error} If the plugin cannot be loaded.
107
80
  */
108
81
  load(pluginName) {
109
- const pluginNamespace = Plugins.getNamespace(pluginName),
110
- pluginNameWithoutNamespace = Plugins.removeNamespace(pluginName),
111
- pluginNameWithoutPrefix = Plugins.removePrefix(pluginNameWithoutNamespace),
82
+ const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
83
+ pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
84
+ pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
112
85
  shortName = pluginNamespace + pluginNameWithoutPrefix,
113
86
  longName = pluginNamespace + PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix;
114
87
  let plugin = null;
package/lib/config.js CHANGED
@@ -120,10 +120,10 @@ class Config {
120
120
  }
121
121
 
122
122
  /**
123
- * Loads the config options from a config specified on the command line.
124
- * @param {string} [config] A shareable named config or path to a config file.
125
- * @returns {void}
126
- */
123
+ * Loads the config options from a config specified on the command line.
124
+ * @param {string} [config] A shareable named config or path to a config file.
125
+ * @returns {void}
126
+ */
127
127
  loadSpecificConfig(config) {
128
128
  if (config) {
129
129
  debug(`Using command line config ${config}`);
@@ -216,8 +216,10 @@ class Config {
216
216
  return localConfigHierarchy;
217
217
  }
218
218
 
219
- // Don't consider the personal config file in the home directory,
220
- // except if the home directory is the same as the current working directory
219
+ /*
220
+ * Don't consider the personal config file in the home directory,
221
+ * except if the home directory is the same as the current working directory
222
+ */
221
223
  if (localConfigDirectory === PERSONAL_CONFIG_DIR && localConfigFile !== projectConfigPath) {
222
224
  continue;
223
225
  }
@@ -343,8 +345,10 @@ class Config {
343
345
  this.plugins.loadAll(this.cliConfig.plugins);
344
346
  }
345
347
 
346
- // Step 3: Override parser only if it is passed explicitly through the command line
347
- // or if it's not defined yet (because the final object will at least have the parser key)
348
+ /*
349
+ * Step 3: Override parser only if it is passed explicitly through the command line
350
+ * or if it's not defined yet (because the final object will at least have the parser key)
351
+ */
348
352
  if (this.parser || !config.parser) {
349
353
  config = ConfigOps.merge(config, { parser: this.parser });
350
354
  }
@@ -3,6 +3,6 @@
3
3
  <td class="clr-<%= severityNumber %>"><%= severityName %></td>
4
4
  <td><%- message %></td>
5
5
  <td>
6
- <a href="http://eslint.org/docs/rules/<%= ruleId %>" target="_blank"><%= ruleId %></a>
6
+ <a href="https://eslint.org/docs/rules/<%= ruleId %>" target="_blank"><%= ruleId %></a>
7
7
  </td>
8
8
  </tr>
@@ -1,5 +1,7 @@
1
1
  <!DOCTYPE html>
2
+ <html>
2
3
  <head>
4
+ <meta charset="UTF-8">
3
5
  <title>ESLint Report</title>
4
6
  <style>
5
7
  body {
@@ -29,7 +31,7 @@
29
31
  }
30
32
  th {
31
33
  font-weight:400;
32
- font-size:normal;
34
+ font-size:medium;
33
35
  text-align:left;
34
36
  cursor:pointer
35
37
  }
@@ -51,7 +51,8 @@ function renderSummary(totalErrors, totalWarnings) {
51
51
  function renderColor(totalErrors, totalWarnings) {
52
52
  if (totalErrors !== 0) {
53
53
  return 2;
54
- } else if (totalWarnings !== 0) {
54
+ }
55
+ if (totalWarnings !== 0) {
55
56
  return 1;
56
57
  }
57
58
  return 0;
@@ -39,22 +39,28 @@ module.exports = function(results) {
39
39
 
40
40
  const messages = result.messages;
41
41
 
42
- output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
43
- messages.forEach(message => {
44
- const type = message.fatal ? "error" : "failure";
42
+ if (messages.length > 0) {
43
+ output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
44
+ messages.forEach(message => {
45
+ const type = message.fatal ? "error" : "failure";
45
46
 
46
- output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}">`;
47
- output += `<${type} message="${xmlEscape(message.message || "")}">`;
48
- output += "<![CDATA[";
49
- output += `line ${message.line || 0}, col `;
50
- output += `${message.column || 0}, ${getMessageType(message)}`;
51
- output += ` - ${xmlEscape(message.message || "")}`;
52
- output += (message.ruleId ? ` (${message.ruleId})` : "");
53
- output += "]]>";
54
- output += `</${type}>`;
55
- output += "</testcase>\n";
56
- });
57
- output += "</testsuite>\n";
47
+ output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}">`;
48
+ output += `<${type} message="${xmlEscape(message.message || "")}">`;
49
+ output += "<![CDATA[";
50
+ output += `line ${message.line || 0}, col `;
51
+ output += `${message.column || 0}, ${getMessageType(message)}`;
52
+ output += ` - ${xmlEscape(message.message || "")}`;
53
+ output += (message.ruleId ? ` (${message.ruleId})` : "");
54
+ output += "]]>";
55
+ output += `</${type}>`;
56
+ output += "</testcase>\n";
57
+ });
58
+ output += "</testsuite>\n";
59
+ } else {
60
+ output += `<testsuite package="org.eslint" time="0" tests="1" errors="0" name="${result.filePath}">\n`;
61
+ output += `<testcase time="0" name="${result.filePath}" />\n`;
62
+ output += "</testsuite>\n";
63
+ }
58
64
 
59
65
  });
60
66
 
@@ -63,9 +63,11 @@ module.exports = function(results) {
63
63
  }
64
64
  };
65
65
 
66
- // If we have multiple messages place them under a messages key
67
- // The first error will be logged as message key
68
- // This is to adhere to TAP 13 loosely defined specification of having a message key
66
+ /*
67
+ * If we have multiple messages place them under a messages key
68
+ * The first error will be logged as message key
69
+ * This is to adhere to TAP 13 loosely defined specification of having a message key
70
+ */
69
71
  if ("message" in diagnostics) {
70
72
  if (typeof diagnostics.messages === "undefined") {
71
73
  diagnostics.messages = [];
@@ -114,8 +114,10 @@ class IgnoredPaths {
114
114
  default: ignore()
115
115
  };
116
116
 
117
- // Add a way to keep track of ignored files. This was present in node-ignore
118
- // 2.x, but dropped for now as of 3.0.10.
117
+ /*
118
+ * Add a way to keep track of ignored files. This was present in node-ignore
119
+ * 2.x, but dropped for now as of 3.0.10.
120
+ */
119
121
  this.ig.custom.ignoreFiles = [];
120
122
  this.ig.default.ignoreFiles = [];
121
123
 
@@ -184,7 +186,7 @@ class IgnoredPaths {
184
186
  addPattern(this.ig.default, pattern);
185
187
  });
186
188
  } else {
187
- throw new Error("Package.json eslintIgnore property requires an array of paths");
189
+ throw new TypeError("Package.json eslintIgnore property requires an array of paths");
188
190
  }
189
191
  }
190
192
  }
package/lib/linter.js CHANGED
@@ -9,8 +9,7 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const EventEmitter = require("events").EventEmitter,
13
- eslintScope = require("eslint-scope"),
12
+ const eslintScope = require("eslint-scope"),
14
13
  levn = require("levn"),
15
14
  lodash = require("lodash"),
16
15
  blankScriptAST = require("../conf/blank-script.json"),
@@ -20,6 +19,7 @@ const EventEmitter = require("events").EventEmitter,
20
19
  validator = require("./config/config-validator"),
21
20
  Environments = require("./config/environments"),
22
21
  applyDisableDirectives = require("./util/apply-disable-directives"),
22
+ createEmitter = require("./util/safe-emitter"),
23
23
  NodeEventGenerator = require("./util/node-event-generator"),
24
24
  SourceCode = require("./util/source-code"),
25
25
  Traverser = require("./util/traverser"),
@@ -111,8 +111,10 @@ function parseJsonConfig(string, location) {
111
111
  // ignore to parse the string by a fallback.
112
112
  }
113
113
 
114
- // Optionator cannot parse commaless notations.
115
- // But we are supporting that. So this is a fallback for that.
114
+ /*
115
+ * Optionator cannot parse commaless notations.
116
+ * But we are supporting that. So this is a fallback for that.
117
+ */
116
118
  items = {};
117
119
  string = string.replace(/([a-zA-Z0-9\-/]+):/g, "\"$1\":").replace(/(]|[0-9])\s+(?=")/, "$1,");
118
120
  try {
@@ -164,13 +166,12 @@ function parseListConfig(string) {
164
166
  * Ensures that variables representing built-in properties of the Global Object,
165
167
  * and any globals declared by special block comments, are present in the global
166
168
  * scope.
167
- * @param {ASTNode} program The top node of the AST.
168
169
  * @param {Scope} globalScope The global scope.
169
170
  * @param {Object} config The existing configuration data.
170
171
  * @param {Environments} envContext Env context
171
172
  * @returns {void}
172
173
  */
173
- function addDeclaredGlobals(program, globalScope, config, envContext) {
174
+ function addDeclaredGlobals(globalScope, config, envContext) {
174
175
  const declaredGlobals = {},
175
176
  exportedGlobals = {},
176
177
  explicitGlobals = {},
@@ -277,7 +278,7 @@ function createDisableDirectives(type, loc, value) {
277
278
  * @param {string} filename The file being checked.
278
279
  * @param {ASTNode} ast The top node of the AST.
279
280
  * @param {Object} config The existing configuration data.
280
- * @param {Linter} linterContext Linter context object
281
+ * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
281
282
  * @returns {{
282
283
  * config: Object,
283
284
  * problems: Problem[],
@@ -290,9 +291,9 @@ function createDisableDirectives(type, loc, value) {
290
291
  * }} Modified config object, along with any problems encountered
291
292
  * while parsing config comments
292
293
  */
293
- function modifyConfigsFromComments(filename, ast, config, linterContext) {
294
+ function modifyConfigsFromComments(filename, ast, config, ruleMapper) {
294
295
 
295
- let commentConfig = {
296
+ const commentConfig = {
296
297
  exported: {},
297
298
  astGlobals: {},
298
299
  rules: {},
@@ -321,10 +322,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
321
322
  Object.assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
322
323
  break;
323
324
 
324
- case "eslint-env":
325
- Object.assign(commentConfig.env, parseListConfig(value));
326
- break;
327
-
328
325
  case "eslint-disable":
329
326
  [].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
330
327
  break;
@@ -340,7 +337,7 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
340
337
  Object.keys(parseResult.config).forEach(name => {
341
338
  const ruleValue = parseResult.config[name];
342
339
 
343
- validator.validateRuleOptions(name, ruleValue, `${filename} line ${comment.loc.start.line}`, linterContext.rules);
340
+ validator.validateRuleOptions(ruleMapper(name), name, ruleValue, `${filename} line ${comment.loc.start.line}`);
344
341
  commentRules[name] = ruleValue;
345
342
  });
346
343
  } else {
@@ -362,14 +359,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
362
359
  }
363
360
  });
364
361
 
365
- // apply environment configs
366
- Object.keys(commentConfig.env).forEach(name => {
367
- const env = linterContext.environments.get(name);
368
-
369
- if (env) {
370
- commentConfig = ConfigOps.merge(commentConfig, env);
371
- }
372
- });
373
362
  Object.assign(commentConfig.rules, commentRules);
374
363
 
375
364
  return {
@@ -392,8 +381,10 @@ function normalizeEcmaVersion(ecmaVersion, isModule) {
392
381
  ecmaVersion = 6;
393
382
  }
394
383
 
395
- // Calculate ECMAScript edition number from official year version starting with
396
- // ES2015, which corresponds with ES6 (or a difference of 2009).
384
+ /*
385
+ * Calculate ECMAScript edition number from official year version starting with
386
+ * ES2015, which corresponds with ES6 (or a difference of 2009).
387
+ */
397
388
  if (ecmaVersion >= 2015) {
398
389
  ecmaVersion -= 2009;
399
390
  }
@@ -698,6 +689,8 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
698
689
  )
699
690
  );
700
691
 
692
+ const lastSourceCodes = new WeakMap();
693
+
701
694
  //------------------------------------------------------------------------------
702
695
  // Public Interface
703
696
  //------------------------------------------------------------------------------
@@ -709,7 +702,7 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
709
702
  module.exports = class Linter {
710
703
 
711
704
  constructor() {
712
- this.sourceCode = null;
705
+ lastSourceCodes.set(this, null);
713
706
  this.version = pkg.version;
714
707
 
715
708
  this.rules = new Rules();
@@ -734,20 +727,24 @@ module.exports = class Linter {
734
727
  * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
735
728
  * If this is not set, the filename will default to '<input>' in the rule context. If
736
729
  * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
737
- * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
730
+ * @param {boolean} [filenameOrOptions.allowInlineConfig=true] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
738
731
  * Useful if you want to validate JS without comments overriding rules.
732
+ * @param {boolean} [filenameOrOptions.reportUnusedDisableDirectives=false] Adds reported errors for unused
733
+ * eslint-disable directives
739
734
  * @returns {Object[]} The results as an array of messages or null if no messages.
740
735
  */
741
736
  _verifyWithoutProcessors(textOrSourceCode, config, filenameOrOptions) {
742
737
  let text,
743
738
  parserServices,
744
739
  allowInlineConfig,
745
- providedFilename;
740
+ providedFilename,
741
+ reportUnusedDisableDirectives;
746
742
 
747
743
  // evaluate arguments
748
744
  if (typeof filenameOrOptions === "object") {
749
745
  providedFilename = filenameOrOptions.filename;
750
746
  allowInlineConfig = filenameOrOptions.allowInlineConfig;
747
+ reportUnusedDisableDirectives = filenameOrOptions.reportUnusedDisableDirectives;
751
748
  } else {
752
749
  providedFilename = filenameOrOptions;
753
750
  }
@@ -755,11 +752,11 @@ module.exports = class Linter {
755
752
  const filename = typeof providedFilename === "string" ? providedFilename : "<input>";
756
753
 
757
754
  if (typeof textOrSourceCode === "string") {
758
- this.sourceCode = null;
755
+ lastSourceCodes.set(this, null);
759
756
  text = textOrSourceCode;
760
757
  } else {
761
- this.sourceCode = textOrSourceCode;
762
- text = this.sourceCode.text;
758
+ lastSourceCodes.set(this, textOrSourceCode);
759
+ text = textOrSourceCode.text;
763
760
  }
764
761
 
765
762
  // search and apply "eslint-env *".
@@ -778,13 +775,13 @@ module.exports = class Linter {
778
775
  // process initial config to make it safe to extend
779
776
  config = prepareConfig(config, this.environments);
780
777
 
781
- if (this.sourceCode) {
778
+ if (lastSourceCodes.get(this)) {
782
779
  parserServices = {};
783
780
  } else {
784
781
 
785
782
  // there's no input, just exit here
786
783
  if (text.trim().length === 0) {
787
- this.sourceCode = new SourceCode(text, blankScriptAST);
784
+ lastSourceCodes.set(this, new SourceCode(text, blankScriptAST));
788
785
  return [];
789
786
  }
790
787
 
@@ -800,16 +797,16 @@ module.exports = class Linter {
800
797
  }
801
798
 
802
799
  parserServices = parseResult.services;
803
- this.sourceCode = new SourceCode(text, parseResult.ast);
800
+ lastSourceCodes.set(this, new SourceCode(text, parseResult.ast));
804
801
  }
805
802
 
806
803
  const problems = [];
807
- const sourceCode = this.sourceCode;
804
+ const sourceCode = lastSourceCodes.get(this);
808
805
  let disableDirectives;
809
806
 
810
807
  // parse global comments and modify config
811
808
  if (allowInlineConfig !== false) {
812
- const modifyConfigResult = modifyConfigsFromComments(filename, sourceCode.ast, config, this);
809
+ const modifyConfigResult = modifyConfigsFromComments(filename, sourceCode.ast, config, ruleId => this.rules.get(ruleId));
813
810
 
814
811
  config = modifyConfigResult.config;
815
812
  modifyConfigResult.problems.forEach(problem => problems.push(problem));
@@ -818,7 +815,7 @@ module.exports = class Linter {
818
815
  disableDirectives = [];
819
816
  }
820
817
 
821
- const emitter = new EventEmitter().setMaxListeners(Infinity);
818
+ const emitter = createEmitter();
822
819
  const traverser = new Traverser();
823
820
  const ecmaFeatures = config.parserOptions.ecmaFeatures || {};
824
821
  const ecmaVersion = config.parserOptions.ecmaVersion || 5;
@@ -861,7 +858,7 @@ module.exports = class Linter {
861
858
  */
862
859
  _linter: {
863
860
  report() {},
864
- on: emitter.on.bind(emitter)
861
+ on: emitter.on
865
862
  }
866
863
  }
867
864
  )
@@ -946,7 +943,7 @@ module.exports = class Linter {
946
943
  });
947
944
 
948
945
  // augment global scope with declared global variables
949
- addDeclaredGlobals(sourceCode.ast, scopeManager.scopes[0], config, this.environments);
946
+ addDeclaredGlobals(scopeManager.scopes[0], config, this.environments);
950
947
 
951
948
  const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter));
952
949
 
@@ -968,7 +965,8 @@ module.exports = class Linter {
968
965
 
969
966
  return applyDisableDirectives({
970
967
  directives: disableDirectives,
971
- problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column)
968
+ problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
969
+ reportUnusedDisableDirectives
972
970
  });
973
971
  }
974
972
 
@@ -1006,7 +1004,7 @@ module.exports = class Linter {
1006
1004
  * @returns {SourceCode} The SourceCode object.
1007
1005
  */
1008
1006
  getSourceCode() {
1009
- return this.sourceCode;
1007
+ return lastSourceCodes.get(this);
1010
1008
  }
1011
1009
 
1012
1010
  /**
@@ -1082,8 +1080,10 @@ module.exports = class Linter {
1082
1080
  debug(`Generating fixed text for ${debugTextDescription} (pass ${passNumber})`);
1083
1081
  fixedResult = SourceCodeFixer.applyFixes(text, messages, shouldFix);
1084
1082
 
1085
- // stop if there are any syntax errors.
1086
- // 'fixedResult.output' is a empty string.
1083
+ /*
1084
+ * stop if there are any syntax errors.
1085
+ * 'fixedResult.output' is a empty string.
1086
+ */
1087
1087
  if (messages.length === 1 && messages[0].fatal) {
1088
1088
  break;
1089
1089
  }