eslint 1.9.0 → 1.10.3

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 (51) hide show
  1. package/README.md +1 -1
  2. package/bin/eslint.js +1 -2
  3. package/lib/ast-utils.js +22 -1
  4. package/lib/cli-engine.js +15 -7
  5. package/lib/cli.js +2 -1
  6. package/lib/config/config-file.js +440 -0
  7. package/lib/config/config-initializer.js +241 -0
  8. package/lib/config/config-ops.js +186 -0
  9. package/lib/{config-validator.js → config/config-validator.js} +10 -2
  10. package/lib/config.js +39 -183
  11. package/lib/eslint.js +49 -41
  12. package/lib/file-finder.js +34 -15
  13. package/lib/ignored-paths.js +1 -1
  14. package/lib/options.js +7 -1
  15. package/lib/rules/block-spacing.js +7 -2
  16. package/lib/rules/brace-style.js +3 -1
  17. package/lib/rules/comma-spacing.js +25 -66
  18. package/lib/rules/consistent-this.js +25 -29
  19. package/lib/rules/curly.js +37 -7
  20. package/lib/rules/eqeqeq.js +17 -2
  21. package/lib/rules/id-length.js +2 -2
  22. package/lib/rules/indent.js +7 -10
  23. package/lib/rules/lines-around-comment.js +32 -12
  24. package/lib/rules/new-cap.js +11 -1
  25. package/lib/rules/no-alert.js +2 -3
  26. package/lib/rules/no-catch-shadow.js +7 -13
  27. package/lib/rules/no-extend-native.js +1 -2
  28. package/lib/rules/no-fallthrough.js +1 -2
  29. package/lib/rules/no-implicit-coercion.js +19 -0
  30. package/lib/rules/no-label-var.js +9 -23
  31. package/lib/rules/no-multiple-empty-lines.js +1 -1
  32. package/lib/rules/no-sequences.js +2 -1
  33. package/lib/rules/no-shadow.js +31 -58
  34. package/lib/rules/no-spaced-func.js +16 -12
  35. package/lib/rules/no-undef-init.js +5 -3
  36. package/lib/rules/no-undef.js +10 -13
  37. package/lib/rules/no-use-before-define.js +7 -21
  38. package/lib/rules/operator-linebreak.js +3 -2
  39. package/lib/rules/quotes.js +34 -15
  40. package/lib/rules/require-jsdoc.js +61 -2
  41. package/lib/rules/space-after-keywords.js +2 -0
  42. package/lib/rules/space-before-function-paren.js +5 -26
  43. package/lib/rules/space-before-keywords.js +5 -2
  44. package/lib/rules/spaced-comment.js +1 -3
  45. package/lib/rules/valid-jsdoc.js +8 -4
  46. package/lib/rules/vars-on-top.js +2 -2
  47. package/lib/testers/rule-tester.js +48 -7
  48. package/lib/util/source-code.js +4 -0
  49. package/lib/util.js +0 -92
  50. package/package.json +4 -6
  51. package/lib/config-initializer.js +0 -146
package/README.md CHANGED
@@ -122,5 +122,5 @@ Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](ht
122
122
  [travis-url]: https://travis-ci.org/eslint/eslint
123
123
  [coveralls-image]: https://img.shields.io/coveralls/eslint/eslint/master.svg?style=flat-square
124
124
  [coveralls-url]: https://coveralls.io/r/eslint/eslint?branch=master
125
- [downloads-image]: http://img.shields.io/npm/dm/eslint.svg?style=flat-square
125
+ [downloads-image]: https://img.shields.io/npm/dm/eslint.svg?style=flat-square
126
126
  [downloads-url]: https://www.npmjs.com/package/eslint
package/bin/eslint.js CHANGED
@@ -46,14 +46,13 @@ if (useStdIn) {
46
46
  }
47
47
  }));
48
48
  } else if (init) {
49
- var configInit = require("../lib/config-initializer");
49
+ var configInit = require("../lib/config/config-initializer");
50
50
  configInit.initializeConfig(function(err) {
51
51
  if (err) {
52
52
  exitCode = 1;
53
53
  console.error(err.message);
54
54
  console.error(err.stack);
55
55
  } else {
56
- console.log("Successfully created .eslintrc file in " + process.cwd());
57
56
  exitCode = 0;
58
57
  }
59
58
  });
package/lib/ast-utils.js CHANGED
@@ -129,5 +129,26 @@ module.exports = {
129
129
  * @param {ASTNode} A node to get.
130
130
  * @returns {ASTNode|null} The trailing statement's node.
131
131
  */
132
- getTrailingStatement: esutils.ast.trailingStatement
132
+ getTrailingStatement: esutils.ast.trailingStatement,
133
+
134
+ /**
135
+ * Finds the variable by a given name in a given scope and its upper scopes.
136
+ *
137
+ * @param {escope.Scope} initScope - A scope to start find.
138
+ * @param {string} name - A variable name to find.
139
+ * @returns {escope.Variable|null} A found variable or `null`.
140
+ */
141
+ getVariableByName: function(initScope, name) {
142
+ var scope = initScope;
143
+ while (scope) {
144
+ var variable = scope.set.get(name);
145
+ if (variable) {
146
+ return variable;
147
+ }
148
+
149
+ scope = scope.upper;
150
+ }
151
+
152
+ return null;
153
+ }
133
154
  };
package/lib/cli-engine.js CHANGED
@@ -32,7 +32,7 @@ var fs = require("fs"),
32
32
  fileEntryCache = require("file-entry-cache"),
33
33
  globUtil = require("./util/glob-util"),
34
34
  SourceCodeFixer = require("./util/source-code-fixer"),
35
- validator = require("./config-validator"),
35
+ validator = require("./config/config-validator"),
36
36
  stringify = require("json-stable-stringify"),
37
37
 
38
38
  crypto = require( "crypto" ),
@@ -95,7 +95,8 @@ var defaultOptions = {
95
95
  // it will always be used
96
96
  cacheLocation: "",
97
97
  cacheFile: ".eslintcache",
98
- fix: false
98
+ fix: false,
99
+ allowInlineConfig: true
99
100
  },
100
101
  loadedPlugins = Object.create(null);
101
102
 
@@ -176,10 +177,11 @@ function calculateStatsPerRun(results) {
176
177
  * @param {Object} configHelper The configuration options for ESLint.
177
178
  * @param {string} filename An optional string representing the texts filename.
178
179
  * @param {boolean} fix Indicates if fixes should be processed.
180
+ * @param {boolean} allowInlineConfig Allow/ignore comments that change config.
179
181
  * @returns {Result} The results for linting on this text.
180
182
  * @private
181
183
  */
182
- function processText(text, configHelper, filename, fix) {
184
+ function processText(text, configHelper, filename, fix, allowInlineConfig) {
183
185
 
184
186
  // clear all existing settings for a new file
185
187
  eslint.reset();
@@ -213,7 +215,10 @@ function processText(text, configHelper, filename, fix) {
213
215
  var parsedBlocks = processor.preprocess(text, filename);
214
216
  var unprocessedMessages = [];
215
217
  parsedBlocks.forEach(function(block) {
216
- unprocessedMessages.push(eslint.verify(block, config, filename));
218
+ unprocessedMessages.push(eslint.verify(block, config, {
219
+ filename: filename,
220
+ allowInlineConfig: allowInlineConfig
221
+ }));
217
222
  });
218
223
 
219
224
  // TODO(nzakas): Figure out how fixes might work for processors
@@ -222,7 +227,10 @@ function processText(text, configHelper, filename, fix) {
222
227
 
223
228
  } else {
224
229
 
225
- messages = eslint.verify(text, config, filename);
230
+ messages = eslint.verify(text, config, {
231
+ filename: filename,
232
+ allowInlineConfig: allowInlineConfig
233
+ });
226
234
 
227
235
  if (fix) {
228
236
  debug("Generating fixed text for " + filename);
@@ -259,7 +267,7 @@ function processText(text, configHelper, filename, fix) {
259
267
  function processFile(filename, configHelper, options) {
260
268
 
261
269
  var text = fs.readFileSync(path.resolve(filename), "utf8"),
262
- result = processText(text, configHelper, filename, options.fix);
270
+ result = processText(text, configHelper, filename, options.fix, options.allowInlineConfig);
263
271
 
264
272
  return result;
265
273
 
@@ -675,7 +683,7 @@ CLIEngine.prototype = {
675
683
  if (filename && options.ignore && exclude(filename)) {
676
684
  results.push(createIgnoreResult(filename));
677
685
  } else {
678
- results.push(processText(text, configHelper, filename, options.fix));
686
+ results.push(processText(text, configHelper, filename, options.fix, options.allowInlineConfig));
679
687
  }
680
688
 
681
689
  stats = calculateStatsPerRun(results);
package/lib/cli.js CHANGED
@@ -54,7 +54,8 @@ function translateOptions(cliOptions) {
54
54
  cache: cliOptions.cache,
55
55
  cacheFile: cliOptions.cacheFile,
56
56
  cacheLocation: cliOptions.cacheLocation,
57
- fix: cliOptions.fix
57
+ fix: cliOptions.fix,
58
+ allowInlineConfig: cliOptions.inlineConfig
58
59
  };
59
60
  }
60
61
 
@@ -0,0 +1,440 @@
1
+ /**
2
+ * @fileoverview Helper to locate and load configuration files.
3
+ * @author Nicholas C. Zakas
4
+ * @copyright 2015 Nicholas C. Zakas. All rights reserved.
5
+ * See LICENSE file in root directory for full license.
6
+ */
7
+ /* eslint no-use-before-define: 0 */
8
+ "use strict";
9
+
10
+ //------------------------------------------------------------------------------
11
+ // Requirements
12
+ //------------------------------------------------------------------------------
13
+
14
+ var debug = require("debug"),
15
+ fs = require("fs"),
16
+ path = require("path"),
17
+ ConfigOps = require("./config-ops"),
18
+ validator = require("./config-validator"),
19
+ stripComments = require("strip-json-comments"),
20
+ isAbsolutePath = require("path-is-absolute");
21
+
22
+ //------------------------------------------------------------------------------
23
+ // Private
24
+ //------------------------------------------------------------------------------
25
+
26
+ var CONFIG_FILES = [
27
+ ".eslintrc.js",
28
+ ".eslintrc.yaml",
29
+ ".eslintrc.yml",
30
+ ".eslintrc.json",
31
+ ".eslintrc"
32
+ ];
33
+
34
+ debug = debug("eslint:config-file");
35
+
36
+ /**
37
+ * Convenience wrapper for synchronously reading file contents.
38
+ * @param {string} filePath The filename to read.
39
+ * @returns {string} The file contents.
40
+ * @private
41
+ */
42
+ function readFile(filePath) {
43
+ return fs.readFileSync(filePath, "utf8");
44
+ }
45
+
46
+ /**
47
+ * Determines if a given string represents a filepath or not using the same
48
+ * conventions as require(), meaning that the first character must be nonalphanumeric
49
+ * and not the @ sign which is used for scoped packages to be considered a file path.
50
+ * @param {string} filePath The string to check.
51
+ * @returns {boolean} True if it's a filepath, false if not.
52
+ * @private
53
+ */
54
+ function isFilePath(filePath) {
55
+ return isAbsolutePath(filePath) || !/\w|@/.test(filePath.charAt(0));
56
+ }
57
+
58
+ /**
59
+ * Loads a YAML configuration from a file.
60
+ * @param {string} filePath The filename to load.
61
+ * @returns {Object} The configuration object from the file.
62
+ * @throws {Error} If the file cannot be read.
63
+ * @private
64
+ */
65
+ function loadYAMLConfigFile(filePath) {
66
+ debug("Loading YAML config file: " + filePath);
67
+
68
+ // lazy load YAML to improve performance when not used
69
+ var yaml = require("js-yaml");
70
+
71
+ try {
72
+ // empty YAML file can be null, so always use
73
+ return yaml.safeLoad(readFile(filePath)) || {};
74
+ } catch (e) {
75
+ debug("Error reading YAML file: " + filePath);
76
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
77
+ throw e;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Loads a JSON configuration from a file.
83
+ * @param {string} filePath The filename to load.
84
+ * @returns {Object} The configuration object from the file.
85
+ * @throws {Error} If the file cannot be read.
86
+ * @private
87
+ */
88
+ function loadJSONConfigFile(filePath) {
89
+ debug("Loading JSON config file: " + filePath);
90
+
91
+ try {
92
+ return JSON.parse(stripComments(readFile(filePath)));
93
+ } catch (e) {
94
+ debug("Error reading JSON file: " + filePath);
95
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
96
+ throw e;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Loads a legacy (.eslintrc) configuration from a file.
102
+ * @param {string} filePath The filename to load.
103
+ * @returns {Object} The configuration object from the file.
104
+ * @throws {Error} If the file cannot be read.
105
+ * @private
106
+ */
107
+ function loadLegacyConfigFile(filePath) {
108
+ debug("Loading config file: " + filePath);
109
+
110
+ // lazy load YAML to improve performance when not used
111
+ var yaml = require("js-yaml");
112
+
113
+ try {
114
+ return yaml.safeLoad(stripComments(readFile(filePath))) || {};
115
+ } catch (e) {
116
+ debug("Error reading YAML file: " + filePath);
117
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
118
+ throw e;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Loads a JavaScript configuration from a file.
124
+ * @param {string} filePath The filename to load.
125
+ * @returns {Object} The configuration object from the file.
126
+ * @throws {Error} If the file cannot be read.
127
+ * @private
128
+ */
129
+ function loadJSConfigFile(filePath) {
130
+ debug("Loading JS config file: " + filePath);
131
+ try {
132
+ return require(filePath);
133
+ } catch (e) {
134
+ debug("Error reading JavaScript file: " + filePath);
135
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
136
+ throw e;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Loads a configuration from a package.json file.
142
+ * @param {string} filePath The filename to load.
143
+ * @returns {Object} The configuration object from the file.
144
+ * @throws {Error} If the file cannot be read.
145
+ * @private
146
+ */
147
+ function loadPackageJSONConfigFile(filePath) {
148
+ debug("Loading package.json config file: " + filePath);
149
+ try {
150
+ return require(filePath).eslintConfig || null;
151
+ } catch (e) {
152
+ debug("Error reading package.json file: " + filePath);
153
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
154
+ throw e;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Loads a JavaScript configuration from a package.
160
+ * @param {string} filePath The package name to load.
161
+ * @returns {Object} The configuration object from the package.
162
+ * @throws {Error} If the package cannot be read.
163
+ * @private
164
+ */
165
+ function loadPackage(filePath) {
166
+ debug("Loading config package: " + filePath);
167
+ try {
168
+ return require(filePath);
169
+ } catch (e) {
170
+ debug("Error reading package: " + filePath);
171
+ e.message = "Cannot read config package: " + filePath + "\nError: " + e.message;
172
+ throw e;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Loads a configuration file regardless of the source. Inspects the file path
178
+ * to determine the correctly way to load the config file.
179
+ * @param {string} filePath The path to the configuration.
180
+ * @returns {Object} The configuration information.
181
+ * @private
182
+ */
183
+ function loadConfigFile(filePath) {
184
+ var config;
185
+
186
+ if (isFilePath(filePath)) {
187
+ switch (path.extname(filePath)) {
188
+ case ".js":
189
+ config = loadJSConfigFile(filePath);
190
+ break;
191
+
192
+ case ".json":
193
+ if (path.basename(filePath) === "package.json") {
194
+ config = loadPackageJSONConfigFile(filePath);
195
+ if (config === null) {
196
+ return null;
197
+ }
198
+ } else {
199
+ config = loadJSONConfigFile(filePath);
200
+ }
201
+ break;
202
+
203
+ case ".yaml":
204
+ case ".yml":
205
+ config = loadYAMLConfigFile(filePath);
206
+ break;
207
+
208
+ default:
209
+ config = loadLegacyConfigFile(filePath);
210
+ }
211
+ } else {
212
+ config = loadPackage(filePath);
213
+ }
214
+
215
+ return ConfigOps.merge(ConfigOps.createEmptyConfig(), config);
216
+ }
217
+
218
+ /**
219
+ * Writes a configuration file in JSON format.
220
+ * @param {Object} config The configuration object to write.
221
+ * @param {string} filePath The filename to write to.
222
+ * @returns {void}
223
+ * @private
224
+ */
225
+ function writeJSONConfigFile(config, filePath) {
226
+ debug("Writing JSON config file: " + filePath);
227
+
228
+ var content = JSON.stringify(config, null, 4);
229
+ fs.writeFileSync(filePath, content, "utf8");
230
+ }
231
+
232
+ /**
233
+ * Writes a configuration file in YAML format.
234
+ * @param {Object} config The configuration object to write.
235
+ * @param {string} filePath The filename to write to.
236
+ * @returns {void}
237
+ * @private
238
+ */
239
+ function writeYAMLConfigFile(config, filePath) {
240
+ debug("Writing YAML config file: " + filePath);
241
+
242
+ // lazy load YAML to improve performance when not used
243
+ var yaml = require("js-yaml");
244
+
245
+ var content = yaml.safeDump(config);
246
+ fs.writeFileSync(filePath, content, "utf8");
247
+ }
248
+
249
+ /**
250
+ * Writes a configuration file in JavaScript format.
251
+ * @param {Object} config The configuration object to write.
252
+ * @param {string} filePath The filename to write to.
253
+ * @returns {void}
254
+ * @private
255
+ */
256
+ function writeJSConfigFile(config, filePath) {
257
+ debug("Writing JS config file: " + filePath);
258
+
259
+ var content = "module.exports = " + JSON.stringify(config, null, 4) + ";";
260
+ fs.writeFileSync(filePath, content, "utf8");
261
+ }
262
+
263
+ /**
264
+ * Writes a configuration file.
265
+ * @param {Object} config The configuration object to write.
266
+ * @param {string} filePath The filename to write to.
267
+ * @returns {void}
268
+ * @throws {Error} When an unknown file type is specified.
269
+ * @private
270
+ */
271
+ function write(config, filePath) {
272
+ switch (path.extname(filePath)) {
273
+ case ".js":
274
+ writeJSConfigFile(config, filePath);
275
+ break;
276
+
277
+ case ".json":
278
+ writeJSONConfigFile(config, filePath);
279
+ break;
280
+
281
+ case ".yaml":
282
+ case ".yml":
283
+ writeYAMLConfigFile(config, filePath);
284
+ break;
285
+
286
+ default:
287
+ throw new Error("Can't write to unknown file type.");
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Applies values from the "extends" field in a configuration file.
293
+ * @param {Object} config The configuration information.
294
+ * @param {string} filePath The file path from which the configuration information
295
+ * was loaded.
296
+ * @returns {Object} A new configuration object with all of the "extends" fields
297
+ * loaded and merged.
298
+ * @private
299
+ */
300
+ function applyExtends(config, filePath) {
301
+ var configExtends = config.extends;
302
+
303
+ // normalize into an array for easier handling
304
+ if (!Array.isArray(config.extends)) {
305
+ configExtends = [config.extends];
306
+ }
307
+
308
+ // Make the last element in an array take the highest precedence
309
+ config = configExtends.reduceRight(function(previousValue, parentPath) {
310
+
311
+ if (parentPath === "eslint:recommended") {
312
+ // Add an explicit substitution for eslint:recommended to conf/eslint.json
313
+ // this lets us use the eslint.json file as the recommended rules
314
+ parentPath = path.resolve(__dirname, "../../conf/eslint.json");
315
+ } else if (isFilePath(parentPath)) {
316
+ // If the `extends` path is relative, use the directory of the current configuration
317
+ // file as the reference point. Otherwise, use as-is.
318
+ parentPath = (!isAbsolutePath(parentPath) ?
319
+ path.join(path.dirname(filePath), parentPath) :
320
+ parentPath
321
+ );
322
+ }
323
+
324
+ try {
325
+ debug("Loading " + parentPath);
326
+ return ConfigOps.merge(load(parentPath), previousValue);
327
+ } catch (e) {
328
+ // If the file referenced by `extends` failed to load, add the path to the
329
+ // configuration file that referenced it to the error message so the user is
330
+ // able to see where it was referenced from, then re-throw
331
+ e.message += "\nReferenced from: " + filePath;
332
+ throw e;
333
+ }
334
+
335
+ }, config);
336
+
337
+ return config;
338
+ }
339
+
340
+ /**
341
+ * Resolves a configuration file path into the fully-formed path, whether filename
342
+ * or package name.
343
+ * @param {string} filePath The filepath to resolve.
344
+ * @returns {string} A path that can be used directly to load the configuration.
345
+ * @private
346
+ */
347
+ function resolve(filePath) {
348
+
349
+ if (isFilePath(filePath)) {
350
+ return path.resolve(filePath);
351
+ } else {
352
+
353
+ // it's a package
354
+
355
+ if (filePath.charAt(0) === "@") {
356
+ // it's a scoped package
357
+
358
+ // package name is "eslint-config", or just a username
359
+ var scopedPackageShortcutRegex = /^(@[^\/]+)(?:\/(?:eslint-config)?)?$/;
360
+ if (scopedPackageShortcutRegex.test(filePath)) {
361
+ filePath = filePath.replace(scopedPackageShortcutRegex, "$1/eslint-config");
362
+ } else if (filePath.split("/")[1].indexOf("eslint-config-") !== 0) {
363
+ // for scoped packages, insert the eslint-config after the first /
364
+ filePath = filePath.replace(/^@([^\/]+)\/(.*)$/, "@$1/eslint-config-$2");
365
+ }
366
+ } else if (filePath.indexOf("eslint-config-") !== 0) {
367
+ filePath = "eslint-config-" + filePath;
368
+ }
369
+
370
+ return filePath;
371
+ }
372
+
373
+ }
374
+
375
+ /**
376
+ * Loads a configuration file from the given file path.
377
+ * @param {string} filePath The filename or package name to load the configuration
378
+ * information from.
379
+ * @returns {Object} The configuration information.
380
+ * @private
381
+ */
382
+ function load(filePath) {
383
+
384
+ var resolvedPath = resolve(filePath),
385
+ config = loadConfigFile(resolvedPath);
386
+
387
+ if (config) {
388
+
389
+ // validate the configuration before continuing
390
+ validator.validate(config, filePath);
391
+
392
+ // If an `extends` property is defined, it represents a configuration file to use as
393
+ // a "parent". Load the referenced file and merge the configuration recursively.
394
+ if (config.extends) {
395
+ config = applyExtends(config, filePath);
396
+ }
397
+
398
+ if (config.env) {
399
+ // Merge in environment-specific globals and ecmaFeatures.
400
+ config = ConfigOps.applyEnvironments(config);
401
+ }
402
+
403
+ }
404
+
405
+ return config;
406
+ }
407
+
408
+ //------------------------------------------------------------------------------
409
+ // Public Interface
410
+ //------------------------------------------------------------------------------
411
+
412
+ module.exports = {
413
+
414
+ load: load,
415
+ resolve: resolve,
416
+ write: write,
417
+ applyExtends: applyExtends,
418
+ CONFIG_FILES: CONFIG_FILES,
419
+
420
+ /**
421
+ * Retrieves the configuration filename for a given directory. It loops over all
422
+ * of the valid configuration filenames in order to find the first one that exists.
423
+ * @param {string} directory The directory to check for a config file.
424
+ * @returns {?string} The filename of the configuration file for the directory
425
+ * or null if there is no configuration file in the directory.
426
+ */
427
+ getFilenameForDirectory: function(directory) {
428
+
429
+ var filename;
430
+
431
+ for (var i = 0, len = CONFIG_FILES.length; i < len; i++) {
432
+ filename = path.join(directory, CONFIG_FILES[i]);
433
+ if (fs.existsSync(filename)) {
434
+ return filename;
435
+ }
436
+ }
437
+
438
+ return null;
439
+ }
440
+ };