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.
- package/README.md +1 -1
- package/bin/eslint.js +1 -2
- package/lib/ast-utils.js +22 -1
- package/lib/cli-engine.js +15 -7
- package/lib/cli.js +2 -1
- package/lib/config/config-file.js +440 -0
- package/lib/config/config-initializer.js +241 -0
- package/lib/config/config-ops.js +186 -0
- package/lib/{config-validator.js → config/config-validator.js} +10 -2
- package/lib/config.js +39 -183
- package/lib/eslint.js +49 -41
- package/lib/file-finder.js +34 -15
- package/lib/ignored-paths.js +1 -1
- package/lib/options.js +7 -1
- package/lib/rules/block-spacing.js +7 -2
- package/lib/rules/brace-style.js +3 -1
- package/lib/rules/comma-spacing.js +25 -66
- package/lib/rules/consistent-this.js +25 -29
- package/lib/rules/curly.js +37 -7
- package/lib/rules/eqeqeq.js +17 -2
- package/lib/rules/id-length.js +2 -2
- package/lib/rules/indent.js +7 -10
- package/lib/rules/lines-around-comment.js +32 -12
- package/lib/rules/new-cap.js +11 -1
- package/lib/rules/no-alert.js +2 -3
- package/lib/rules/no-catch-shadow.js +7 -13
- package/lib/rules/no-extend-native.js +1 -2
- package/lib/rules/no-fallthrough.js +1 -2
- package/lib/rules/no-implicit-coercion.js +19 -0
- package/lib/rules/no-label-var.js +9 -23
- package/lib/rules/no-multiple-empty-lines.js +1 -1
- package/lib/rules/no-sequences.js +2 -1
- package/lib/rules/no-shadow.js +31 -58
- package/lib/rules/no-spaced-func.js +16 -12
- package/lib/rules/no-undef-init.js +5 -3
- package/lib/rules/no-undef.js +10 -13
- package/lib/rules/no-use-before-define.js +7 -21
- package/lib/rules/operator-linebreak.js +3 -2
- package/lib/rules/quotes.js +34 -15
- package/lib/rules/require-jsdoc.js +61 -2
- package/lib/rules/space-after-keywords.js +2 -0
- package/lib/rules/space-before-function-paren.js +5 -26
- package/lib/rules/space-before-keywords.js +5 -2
- package/lib/rules/spaced-comment.js +1 -3
- package/lib/rules/valid-jsdoc.js +8 -4
- package/lib/rules/vars-on-top.js +2 -2
- package/lib/testers/rule-tester.js +48 -7
- package/lib/util/source-code.js +4 -0
- package/lib/util.js +0 -92
- package/package.json +4 -6
- 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
|
15
|
-
|
16
|
-
|
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
|
34
|
-
|
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
|
-
|
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 (
|
187
|
-
|
69
|
+
if (config.extends) {
|
70
|
+
config = ConfigFile.applyExtends(config, filePath);
|
188
71
|
}
|
189
|
-
|
190
|
-
|
191
|
-
config =
|
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 =
|
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
|
-
|
281
|
-
|
282
|
-
|
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 =
|
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 ===
|
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
|
324
|
-
if (!
|
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 =
|
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 :
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
298
|
+
config = ConfigOps.merge(config, { rules: this.options.rules });
|
443
299
|
}
|
444
300
|
|
445
301
|
// Step 7: Merge in command line globals
|
446
|
-
config =
|
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 =
|
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 =
|
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(
|
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
|
-
|
27
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
316
|
+
commentConfig = ConfigOps.merge(commentConfig, environments[name]);
|
333
317
|
}
|
334
318
|
});
|
335
319
|
assign(commentConfig.rules, commentRules);
|
336
320
|
|
337
|
-
return
|
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:
|
403
|
-
env:
|
404
|
-
settings:
|
405
|
-
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
|
609
|
-
* If this is not set, the filename will default to '<input>' in the rule context.
|
610
|
-
*
|
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,
|
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
|
-
//
|
623
|
-
|
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
|
-
|
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
|
-
|
882
|
-
|
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
|
|
package/lib/file-finder.js
CHANGED
@@ -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
|
-
|
79
|
-
|
82
|
+
(function() {
|
83
|
+
while (directory !== child) {
|
84
|
+
dirs[searched++] = directory;
|
80
85
|
|
81
|
-
|
82
|
-
filePath = path.resolve(directory, name);
|
83
|
-
break;
|
84
|
-
}
|
86
|
+
for (var k = 0, found = false; k < names.length && !found; k++) {
|
85
87
|
|
86
|
-
|
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
|
-
|
89
|
-
|
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
|
-
|
141
|
-
|
149
|
+
// convert to an array for easier handling
|
150
|
+
if (!Array.isArray(name)) {
|
151
|
+
name = [name];
|
152
|
+
}
|
142
153
|
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
|
package/lib/ignored-paths.js
CHANGED