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/lib/config.js CHANGED
@@ -2,8 +2,9 @@
2
2
  * @fileoverview Responsible for loading config files
3
3
  * @author Seth McLaughlin
4
4
  * @copyright 2014 Nicholas C. Zakas. All rights reserved.
5
- * @copyright 2013 Seth McLaughlin. All rights reserved.
6
5
  * @copyright 2014 Michael McLaughlin. All rights reserved.
6
+ * @copyright 2013 Seth McLaughlin. All rights reserved.
7
+ * See LICENSE in root directory for full license.
7
8
  */
8
9
  "use strict";
9
10
 
@@ -11,29 +12,22 @@
11
12
  // Requirements
12
13
  //------------------------------------------------------------------------------
13
14
 
14
- var fs = require("fs"),
15
- path = require("path"),
16
- environments = require("../conf/environments"),
15
+ var path = require("path"),
16
+ ConfigOps = require("./config/config-ops"),
17
+ ConfigFile = require("./config/config-file"),
17
18
  util = require("./util"),
18
19
  FileFinder = require("./file-finder"),
19
- stripComments = require("strip-json-comments"),
20
- assign = require("object-assign"),
21
20
  debug = require("debug"),
22
- yaml = require("js-yaml"),
23
21
  userHome = require("user-home"),
24
- isAbsolutePath = require("path-is-absolute"),
25
22
  isResolvable = require("is-resolvable"),
26
- validator = require("./config-validator"),
27
23
  pathIsInside = require("path-is-inside");
28
24
 
29
25
  //------------------------------------------------------------------------------
30
26
  // Constants
31
27
  //------------------------------------------------------------------------------
32
28
 
33
- var LOCAL_CONFIG_FILENAME = ".eslintrc",
34
- PACKAGE_CONFIG_FILENAME = "package.json",
35
- PACKAGE_CONFIG_FIELD_NAME = "eslintConfig",
36
- PERSONAL_CONFIG_PATH = userHome ? path.join(userHome, LOCAL_CONFIG_FILENAME) : null;
29
+ var PACKAGE_CONFIG_FILENAME = "package.json",
30
+ PERSONAL_CONFIG_DIR = userHome || null;
37
31
 
38
32
  //------------------------------------------------------------------------------
39
33
  // Private
@@ -47,18 +41,6 @@ var loadedPlugins = Object.create(null);
47
41
 
48
42
  debug = debug("eslint:config");
49
43
 
50
- /**
51
- * Determines if a given string represents a filepath or not using the same
52
- * conventions as require(), meaning that the first character must be nonalphanumeric
53
- * and not the @ sign which is used for scoped packages to be considered a file path.
54
- * @param {string} filePath The string to check.
55
- * @returns {boolean} True if it's a filepath, false if not.
56
- * @private
57
- */
58
- function isFilePath(filePath) {
59
- return isAbsolutePath(filePath) || !/\w|@/.test(filePath.charAt(0));
60
- }
61
-
62
44
  /**
63
45
  * Check if item is an javascript object
64
46
  * @param {*} item object to check for
@@ -69,94 +51,6 @@ function isObject(item) {
69
51
  return typeof item === "object" && !Array.isArray(item) && item !== null;
70
52
  }
71
53
 
72
- /**
73
- * Creates an environment config based on the specified environments.
74
- * @param {Object<string,boolean>} envs The environment settings.
75
- * @returns {Object} A configuration object with the appropriate rules and globals
76
- * set.
77
- * @private
78
- */
79
- function createEnvironmentConfig(envs) {
80
-
81
- var envConfig = {
82
- globals: {},
83
- env: envs || {},
84
- rules: {},
85
- ecmaFeatures: {}
86
- };
87
-
88
- if (envs) {
89
- Object.keys(envs).filter(function(name) {
90
- return envs[name];
91
- }).forEach(function(name) {
92
- var environment = environments[name];
93
-
94
- if (environment) {
95
-
96
- if (environment.globals) {
97
- assign(envConfig.globals, environment.globals);
98
- }
99
-
100
- if (environment.ecmaFeatures) {
101
- assign(envConfig.ecmaFeatures, environment.ecmaFeatures);
102
- }
103
- }
104
- });
105
- }
106
-
107
- return envConfig;
108
- }
109
-
110
- /**
111
- * Read the config from the config JSON file
112
- * @param {string} filePath the path to the JSON config file
113
- * @returns {Object} config object
114
- * @private
115
- */
116
- function readConfigFromFile(filePath) {
117
- var config = {};
118
-
119
- if (isFilePath(filePath)) {
120
- if (path.extname(filePath) === ".js") { // using js files for config
121
- config = require(filePath);
122
- } else {
123
- try {
124
- config = yaml.safeLoad(stripComments(fs.readFileSync(filePath, "utf8"))) || {};
125
- } catch (e) {
126
- debug("Error reading YAML file: " + filePath);
127
- e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
128
- throw e;
129
- }
130
- }
131
-
132
- if (path.basename(filePath) === PACKAGE_CONFIG_FILENAME) {
133
- config = config[PACKAGE_CONFIG_FIELD_NAME] || {};
134
- }
135
-
136
- } else {
137
-
138
- // it's a package
139
-
140
- if (filePath.charAt(0) === "@") {
141
- // it's a scoped package
142
-
143
- // package name is "eslint-config", or just a username
144
- var scopedPackageShortcutRegex = /^(@[^\/]+)(?:\/(?:eslint-config)?)?$/;
145
- if (scopedPackageShortcutRegex.test(filePath)) {
146
- filePath = filePath.replace(scopedPackageShortcutRegex, "$1/eslint-config");
147
- } else if (filePath.split("/")[1].indexOf("eslint-config-") !== 0) {
148
- // for scoped packages, insert the eslint-config after the first /
149
- filePath = filePath.replace(/^@([^\/]+)\/(.*)$/, "@$1/eslint-config-$2");
150
- }
151
- } else if (filePath.indexOf("eslint-config-") !== 0) {
152
- filePath = "eslint-config-" + filePath;
153
- }
154
-
155
- config = util.mergeConfigs(config, require(filePath));
156
- }
157
- return config;
158
- }
159
-
160
54
  /**
161
55
  * Load and parse a JSON config object from a file.
162
56
  * @param {string|Object} configToLoad the path to the JSON config file or the config object itself.
@@ -164,67 +58,24 @@ function readConfigFromFile(filePath) {
164
58
  * @private
165
59
  */
166
60
  function loadConfig(configToLoad) {
167
- var config = {};
168
- var filePath = "";
61
+ var config = {},
62
+ filePath = "";
169
63
 
170
64
  if (configToLoad) {
171
65
 
172
66
  if (isObject(configToLoad)) {
173
67
  config = configToLoad;
174
- } else {
175
- filePath = configToLoad;
176
- config = readConfigFromFile(filePath);
177
- }
178
-
179
- validator.validate(config, filePath);
180
-
181
- // If an `extends` property is defined, it represents a configuration file to use as
182
- // a "parent". Load the referenced file and merge the configuration recursively.
183
- if (config.extends) {
184
- var configExtends = config.extends;
185
68
 
186
- if (!Array.isArray(config.extends)) {
187
- configExtends = [config.extends];
69
+ if (config.extends) {
70
+ config = ConfigFile.applyExtends(config, filePath);
188
71
  }
189
-
190
- // Make the last element in an array take the highest precedence
191
- config = configExtends.reduceRight(function(previousValue, parentPath) {
192
-
193
- if (parentPath === "eslint:recommended") {
194
- // Add an explicit substitution for eslint:recommended to conf/eslint.json
195
- // this lets us use the eslint.json file as the recommended rules
196
- parentPath = path.resolve(__dirname, "../conf/eslint.json");
197
- } else if (isFilePath(parentPath)) {
198
- // If the `extends` path is relative, use the directory of the current configuration
199
- // file as the reference point. Otherwise, use as-is.
200
- parentPath = (!isAbsolutePath(parentPath) ?
201
- path.join(path.dirname(filePath), parentPath) :
202
- parentPath
203
- );
204
- }
205
-
206
- try {
207
- return util.mergeConfigs(loadConfig(parentPath), previousValue);
208
- } catch (e) {
209
- // If the file referenced by `extends` failed to load, add the path to the
210
- // configuration file that referenced it to the error message so the user is
211
- // able to see where it was referenced from, then re-throw
212
- e.message += "\nReferenced from: " + filePath;
213
- throw e;
214
- }
215
-
216
- }, config);
217
-
218
- }
219
-
220
- if (config.env) {
221
- // Merge in environment-specific globals and ecmaFeatures.
222
- config = util.mergeConfigs(createEnvironmentConfig(config.env), config);
72
+ } else {
73
+ filePath = configToLoad;
74
+ config = ConfigFile.load(filePath);
223
75
  }
224
76
 
225
77
  }
226
78
 
227
-
228
79
  return config;
229
80
  }
230
81
 
@@ -263,7 +114,7 @@ function getPluginsConfig(pluginNames) {
263
114
  rules[pluginNameWithoutPrefix + "/" + item] = plugin.rulesConfig[item];
264
115
  });
265
116
 
266
- pluginConfig = util.mergeConfigs(pluginConfig, rules);
117
+ pluginConfig = ConfigOps.merge(pluginConfig, rules);
267
118
  });
268
119
 
269
120
  return {rules: pluginConfig};
@@ -275,11 +126,16 @@ function getPluginsConfig(pluginNames) {
275
126
  * @private
276
127
  */
277
128
  function getPersonalConfig() {
278
- var config = {};
129
+ var config = {},
130
+ filename;
131
+
132
+ if (PERSONAL_CONFIG_DIR) {
133
+ filename = ConfigFile.getFilenameForDirectory(PERSONAL_CONFIG_DIR);
279
134
 
280
- if (PERSONAL_CONFIG_PATH && fs.existsSync(PERSONAL_CONFIG_PATH)) {
281
- debug("Using personal config");
282
- config = loadConfig(PERSONAL_CONFIG_PATH);
135
+ if (filename) {
136
+ debug("Using personal config");
137
+ config = loadConfig(filename);
138
+ }
283
139
  }
284
140
 
285
141
  return config;
@@ -300,7 +156,7 @@ function getLocalConfig(thisConfig, directory) {
300
156
  localConfigFiles = thisConfig.findLocalConfigFiles(directory),
301
157
  numFiles = localConfigFiles.length,
302
158
  rootPath,
303
- projectConfigPath = path.join(process.cwd(), LOCAL_CONFIG_FILENAME);
159
+ projectConfigPath = ConfigFile.getFilenameForDirectory(process.cwd());
304
160
 
305
161
  for (i = 0; i < numFiles; i++) {
306
162
 
@@ -308,7 +164,7 @@ function getLocalConfig(thisConfig, directory) {
308
164
 
309
165
  // Don't consider the personal config file in the home directory,
310
166
  // except if the home directory is the same as the current working directory
311
- if (localConfigFile === PERSONAL_CONFIG_PATH && localConfigFile !== projectConfigPath) {
167
+ if (path.dirname(localConfigFile) === PERSONAL_CONFIG_DIR && localConfigFile !== projectConfigPath) {
312
168
  continue;
313
169
  }
314
170
 
@@ -320,8 +176,8 @@ function getLocalConfig(thisConfig, directory) {
320
176
  debug("Loading " + localConfigFile);
321
177
  localConfig = loadConfig(localConfigFile);
322
178
 
323
- // Don't consider a local config file found if the config is empty.
324
- if (!Object.keys(localConfig).length) {
179
+ // Don't consider a local config file found if the config is null
180
+ if (!localConfig) {
325
181
  continue;
326
182
  }
327
183
 
@@ -332,11 +188,11 @@ function getLocalConfig(thisConfig, directory) {
332
188
 
333
189
  found = true;
334
190
  debug("Using " + localConfigFile);
335
- config = util.mergeConfigs(localConfig, config);
191
+ config = ConfigOps.merge(localConfig, config);
336
192
  }
337
193
 
338
194
  // Use the personal config file if there are no other local config files found.
339
- return found ? config : util.mergeConfigs(config, getPersonalConfig());
195
+ return found ? config : ConfigOps.merge(config, getPersonalConfig());
340
196
  }
341
197
 
342
198
  //------------------------------------------------------------------------------
@@ -420,42 +276,42 @@ Config.prototype.getConfig = function(filePath) {
420
276
  }
421
277
 
422
278
  // Step 2: Create a copy of the baseConfig
423
- config = util.mergeConfigs({parser: this.parser}, this.baseConfig);
279
+ config = ConfigOps.merge({parser: this.parser}, this.baseConfig);
424
280
 
425
281
  // Step 3: Merge in the user-specified configuration from .eslintrc and package.json
426
- config = util.mergeConfigs(config, userConfig);
282
+ config = ConfigOps.merge(config, userConfig);
427
283
 
428
284
  // Step 4: Merge in command line config file
429
285
  if (this.useSpecificConfig) {
430
286
  debug("Merging command line config file");
431
287
 
432
- config = util.mergeConfigs(config, this.useSpecificConfig);
288
+ config = ConfigOps.merge(config, this.useSpecificConfig);
433
289
  }
434
290
 
435
291
  // Step 5: Merge in command line environments
436
292
  debug("Merging command line environment settings");
437
- config = util.mergeConfigs(config, createEnvironmentConfig(this.env));
293
+ config = ConfigOps.merge(config, ConfigOps.createEnvironmentConfig(this.env));
438
294
 
439
295
  // Step 6: Merge in command line rules
440
296
  if (this.options.rules) {
441
297
  debug("Merging command line rules");
442
- config = util.mergeConfigs(config, { rules: this.options.rules });
298
+ config = ConfigOps.merge(config, { rules: this.options.rules });
443
299
  }
444
300
 
445
301
  // Step 7: Merge in command line globals
446
- config = util.mergeConfigs(config, { globals: this.globals });
302
+ config = ConfigOps.merge(config, { globals: this.globals });
447
303
 
448
304
  // Step 8: Merge in command line plugins
449
305
  if (this.options.plugins) {
450
306
  debug("Merging command line plugins");
451
307
  pluginConfig = getPluginsConfig(this.options.plugins);
452
- config = util.mergeConfigs(config, { plugins: this.options.plugins });
308
+ config = ConfigOps.merge(config, { plugins: this.options.plugins });
453
309
  }
454
310
 
455
311
  // Step 9: Merge in plugin specific rules in reverse
456
312
  if (config.plugins) {
457
313
  pluginConfig = getPluginsConfig(config.plugins);
458
- config = util.mergeConfigs(pluginConfig, config);
314
+ config = ConfigOps.merge(pluginConfig, config);
459
315
  }
460
316
 
461
317
  this.cache[directory] = config;
@@ -471,7 +327,7 @@ Config.prototype.getConfig = function(filePath) {
471
327
  Config.prototype.findLocalConfigFiles = function(directory) {
472
328
 
473
329
  if (!this.localConfigFinder) {
474
- this.localConfigFinder = new FileFinder(LOCAL_CONFIG_FILENAME, PACKAGE_CONFIG_FILENAME);
330
+ this.localConfigFinder = new FileFinder(ConfigFile.CONFIG_FILES, PACKAGE_CONFIG_FILENAME);
475
331
  }
476
332
 
477
333
  return this.localConfigFinder.findAllInDirectoryAndParents(directory);
package/lib/eslint.js CHANGED
@@ -16,15 +16,16 @@ var estraverse = require("./util/estraverse"),
16
16
  blankScriptAST = require("../conf/blank-script.json"),
17
17
  assign = require("object-assign"),
18
18
  rules = require("./rules"),
19
- util = require("./util"),
20
19
  RuleContext = require("./rule-context"),
21
20
  timing = require("./timing"),
22
21
  SourceCode = require("./util/source-code"),
23
22
  NodeEventGenerator = require("./util/node-event-generator"),
24
23
  CommentEventGenerator = require("./util/comment-event-generator"),
25
24
  EventEmitter = require("events").EventEmitter,
26
- validator = require("./config-validator"),
27
- replacements = require("../conf/replacements.json");
25
+ ConfigOps = require("./config/config-ops"),
26
+ validator = require("./config/config-validator"),
27
+ replacements = require("../conf/replacements.json"),
28
+ assert = require("assert");
28
29
 
29
30
  var DEFAULT_PARSER = require("../conf/eslint.json").parser;
30
31
 
@@ -111,25 +112,6 @@ function parseListConfig(string) {
111
112
  return items;
112
113
  }
113
114
 
114
- /**
115
- * @param {Scope} scope The scope object to check.
116
- * @param {string} name The name of the variable to look up.
117
- * @returns {Variable} The variable object if found or null if not.
118
- */
119
- function getVariable(scope, name) {
120
- var variable = null;
121
- scope.variables.some(function(v) {
122
- if (v.name === name) {
123
- variable = v;
124
- return true;
125
- } else {
126
- return false;
127
- }
128
-
129
- });
130
- return variable;
131
- }
132
-
133
115
  /**
134
116
  * Ensures that variables representing built-in properties of the Global Object,
135
117
  * and any globals declared by special block comments, are present in the global
@@ -161,29 +143,31 @@ function addDeclaredGlobals(program, globalScope, config) {
161
143
  assign(explicitGlobals, config.astGlobals);
162
144
 
163
145
  Object.keys(declaredGlobals).forEach(function(name) {
164
- var variable = getVariable(globalScope, name);
146
+ var variable = globalScope.set.get(name);
165
147
  if (!variable) {
166
148
  variable = new escope.Variable(name, globalScope);
167
149
  variable.eslintExplicitGlobal = false;
168
150
  globalScope.variables.push(variable);
151
+ globalScope.set.set(name, variable);
169
152
  }
170
153
  variable.writeable = declaredGlobals[name];
171
154
  });
172
155
 
173
156
  Object.keys(explicitGlobals).forEach(function(name) {
174
- var variable = getVariable(globalScope, name);
157
+ var variable = globalScope.set.get(name);
175
158
  if (!variable) {
176
159
  variable = new escope.Variable(name, globalScope);
177
160
  variable.eslintExplicitGlobal = true;
178
161
  variable.eslintExplicitGlobalComment = explicitGlobals[name].comment;
179
162
  globalScope.variables.push(variable);
163
+ globalScope.set.set(name, variable);
180
164
  }
181
165
  variable.writeable = explicitGlobals[name].value;
182
166
  });
183
167
 
184
168
  // mark all exported variables as such
185
169
  Object.keys(exportedGlobals).forEach(function(name) {
186
- var variable = getVariable(globalScope, name);
170
+ var variable = globalScope.set.get(name);
187
171
  if (variable) {
188
172
  variable.eslintUsed = true;
189
173
  }
@@ -329,12 +313,12 @@ function modifyConfigsFromComments(filename, ast, config, reportingConfig, messa
329
313
  // apply environment configs
330
314
  Object.keys(commentConfig.env).forEach(function(name) {
331
315
  if (environments[name]) {
332
- commentConfig = util.mergeConfigs(commentConfig, environments[name]);
316
+ commentConfig = ConfigOps.merge(commentConfig, environments[name]);
333
317
  }
334
318
  });
335
319
  assign(commentConfig.rules, commentRules);
336
320
 
337
- return util.mergeConfigs(config, commentConfig);
321
+ return ConfigOps.merge(config, commentConfig);
338
322
  }
339
323
 
340
324
  /**
@@ -399,10 +383,10 @@ function prepareConfig(config) {
399
383
  preparedConfig = {
400
384
  rules: copiedRules,
401
385
  parser: config.parser || DEFAULT_PARSER,
402
- globals: util.mergeConfigs({}, config.globals),
403
- env: util.mergeConfigs({}, config.env || {}),
404
- settings: util.mergeConfigs({}, config.settings || {}),
405
- ecmaFeatures: util.mergeConfigs(ecmaFeatures, config.ecmaFeatures || {})
386
+ globals: ConfigOps.merge({}, config.globals),
387
+ env: ConfigOps.merge({}, config.env || {}),
388
+ settings: ConfigOps.merge({}, config.settings || {}),
389
+ ecmaFeatures: ConfigOps.merge(ecmaFeatures, config.ecmaFeatures || {})
406
390
  };
407
391
 
408
392
  // can't have global return inside of modules
@@ -605,22 +589,32 @@ module.exports = (function() {
605
589
  * Verifies the text against the rules specified by the second argument.
606
590
  * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
607
591
  * @param {Object} config An object whose keys specify the rules to use.
608
- * @param {string=} filename The optional filename of the file being checked.
609
- * If this is not set, the filename will default to '<input>' in the rule context.
610
- * @param {boolean=} saveState Indicates if the state from the last run should be saved.
592
+ * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
593
+ * If this is not set, the filename will default to '<input>' in the rule context. If
594
+ * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
595
+ * @param {boolean} [saveState] Indicates if the state from the last run should be saved.
611
596
  * Mostly useful for testing purposes.
597
+ * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
598
+ * Useful if you want to validate JS without comments overriding rules.
612
599
  * @returns {Object[]} The results as an array of messages or null if no messages.
613
600
  */
614
- api.verify = function(textOrSourceCode, config, filename, saveState) {
601
+ api.verify = function(textOrSourceCode, config, filenameOrOptions, saveState) {
615
602
 
616
603
  var ast,
617
604
  shebang,
618
605
  ecmaFeatures,
619
606
  ecmaVersion,
607
+ allowInlineConfig,
620
608
  text = (typeof textOrSourceCode === "string") ? textOrSourceCode : null;
621
609
 
622
- // set the current parsed filename
623
- currentFilename = filename;
610
+ // evaluate arguments
611
+ if (typeof filenameOrOptions === "object") {
612
+ currentFilename = filenameOrOptions.filename;
613
+ allowInlineConfig = filenameOrOptions.allowInlineConfig;
614
+ saveState = filenameOrOptions.saveState;
615
+ } else {
616
+ currentFilename = filenameOrOptions;
617
+ }
624
618
 
625
619
  if (!saveState) {
626
620
  this.reset();
@@ -667,7 +661,9 @@ module.exports = (function() {
667
661
  if (ast) {
668
662
 
669
663
  // parse global comments and modify config
670
- config = modifyConfigsFromComments(filename, ast, config, reportingConfig, messages);
664
+ if (allowInlineConfig !== false) {
665
+ config = modifyConfigsFromComments(currentFilename, ast, config, reportingConfig, messages);
666
+ }
671
667
 
672
668
  // enable appropriate rules
673
669
  Object.keys(config.rules).filter(function(key) {
@@ -806,13 +802,19 @@ module.exports = (function() {
806
802
  * @returns {void}
807
803
  */
808
804
  api.report = function(ruleId, severity, node, location, message, opts, fix) {
805
+ if (node) {
806
+ assert.strictEqual(typeof node, "object", "Node must be an object");
807
+ }
809
808
 
810
809
  if (typeof location === "string") {
810
+ assert.ok(node, "Node must be provided when reporting error if location is not provided");
811
+
811
812
  fix = opts;
812
813
  opts = message;
813
814
  message = location;
814
815
  location = node.loc.start;
815
816
  }
817
+ // else, assume location was provided, so node may be omitted
816
818
 
817
819
  if (isDisabledByReportingConfig(reportingConfig, ruleId, location)) {
818
820
  return;
@@ -835,7 +837,7 @@ module.exports = (function() {
835
837
  message: message,
836
838
  line: location.line,
837
839
  column: location.column + 1, // switch to 1-base instead of 0-base
838
- nodeType: node.type,
840
+ nodeType: node && node.type,
839
841
  source: sourceCode.lines[location.line - 1] || ""
840
842
  };
841
843
 
@@ -878,8 +880,14 @@ module.exports = (function() {
878
880
 
879
881
  // copy over methods
880
882
  Object.keys(externalMethods).forEach(function(methodName) {
881
- api[methodName] = function() {
882
- return sourceCode ? sourceCode[externalMethods[methodName]].apply(sourceCode, arguments) : null;
883
+ var exMethodName = externalMethods[methodName];
884
+
885
+ // All functions expected to have less arguments than 5.
886
+ api[methodName] = function(a, b, c, d, e) {
887
+ if (sourceCode) {
888
+ return sourceCode[exMethodName](a, b, c, d, e);
889
+ }
890
+ return null;
883
891
  };
884
892
  });
885
893
 
@@ -2,6 +2,8 @@
2
2
  * @fileoverview Util class to find config files.
3
3
  * @author Aliaksei Shytkin
4
4
  * @copyright 2014 Michael McLaughlin. All rights reserved.
5
+ * @copyright 2014 Aliaksei Shytkin. All rights reserved.
6
+ * See LICENSE in root directory for full license.
5
7
  */
6
8
  "use strict";
7
9
 
@@ -61,6 +63,7 @@ FileFinder.prototype.findInDirectoryOrParents = function(directory) {
61
63
  filePath,
62
64
  i,
63
65
  name,
66
+ names,
64
67
  searched;
65
68
 
66
69
  if (!directory) {
@@ -74,20 +77,26 @@ FileFinder.prototype.findInDirectoryOrParents = function(directory) {
74
77
  dirs = [];
75
78
  searched = 0;
76
79
  name = this.fileNames[0];
80
+ names = Array.isArray(name) ? name : [name];
77
81
 
78
- while (directory !== child) {
79
- dirs[searched++] = directory;
82
+ (function() {
83
+ while (directory !== child) {
84
+ dirs[searched++] = directory;
80
85
 
81
- if (getDirectoryEntries(directory).indexOf(name) !== -1 && fs.statSync(path.resolve(directory, name)).isFile()) {
82
- filePath = path.resolve(directory, name);
83
- break;
84
- }
86
+ for (var k = 0, found = false; k < names.length && !found; k++) {
85
87
 
86
- child = directory;
88
+ if (getDirectoryEntries(directory).indexOf(names[k]) !== -1 && fs.statSync(path.resolve(directory, names[k])).isFile()) {
89
+ filePath = path.resolve(directory, names[k]);
90
+ return;
91
+ }
92
+ }
87
93
 
88
- // Assign parent directory to directory.
89
- directory = path.dirname(directory);
90
- }
94
+ child = directory;
95
+
96
+ // Assign parent directory to directory.
97
+ directory = path.dirname(directory);
98
+ }
99
+ }());
91
100
 
92
101
  for (i = 0; i < searched; i++) {
93
102
  cache[dirs[i]] = filePath;
@@ -137,14 +146,24 @@ FileFinder.prototype.findAllInDirectoryAndParents = function(directory) {
137
146
  for (i = 0; i < fileNamesCount; i++) {
138
147
  name = fileNames[i];
139
148
 
140
- if (getDirectoryEntries(directory).indexOf(name) !== -1 && fs.statSync(path.resolve(directory, name)).isFile()) {
141
- filePath = path.resolve(directory, name);
149
+ // convert to an array for easier handling
150
+ if (!Array.isArray(name)) {
151
+ name = [name];
152
+ }
142
153
 
143
- // Add the file path to the cache of each directory searched.
144
- for (j = 0; j < searched; j++) {
145
- cache[dirs[j]].push(filePath);
154
+ for (var k = 0, found = false; k < name.length && !found; k++) {
155
+
156
+ if (getDirectoryEntries(directory).indexOf(name[k]) !== -1 && fs.statSync(path.resolve(directory, name[k])).isFile()) {
157
+ filePath = path.resolve(directory, name[k]);
158
+ found = true;
159
+
160
+ // Add the file path to the cache of each directory searched.
161
+ for (j = 0; j < searched; j++) {
162
+ cache[dirs[j]].push(filePath);
163
+ }
146
164
  }
147
165
  }
166
+
148
167
  }
149
168
  child = directory;
150
169
 
@@ -103,7 +103,7 @@ IgnoredPaths.load = function(options) {
103
103
  }
104
104
 
105
105
  if (options.ignorePattern) {
106
- patterns.push(options.ignorePattern);
106
+ patterns = patterns.concat(options.ignorePattern);
107
107
  }
108
108
 
109
109
  return new IgnoredPaths(patterns);