eslint 0.22.0 → 0.24.1

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 (201) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +111 -95
  3. package/bin/eslint.js +41 -41
  4. package/conf/environments.js +87 -81
  5. package/conf/eslint.json +186 -179
  6. package/lib/api.js +13 -12
  7. package/lib/cli-engine.js +441 -451
  8. package/lib/cli.js +196 -196
  9. package/lib/config-initializer.js +145 -145
  10. package/lib/config-validator.js +110 -110
  11. package/lib/config.js +428 -416
  12. package/lib/eslint.js +1072 -1073
  13. package/lib/file-finder.js +167 -167
  14. package/lib/formatters/checkstyle.js +68 -68
  15. package/lib/formatters/compact.js +53 -53
  16. package/lib/formatters/jslint-xml.js +40 -40
  17. package/lib/formatters/junit.js +63 -63
  18. package/lib/formatters/stylish.js +90 -90
  19. package/lib/formatters/tap.js +86 -86
  20. package/lib/ignored-paths.js +137 -137
  21. package/lib/load-rules.js +39 -39
  22. package/lib/options.js +132 -126
  23. package/lib/rule-context.js +107 -107
  24. package/lib/rules/accessor-pairs.js +65 -65
  25. package/lib/rules/array-bracket-spacing.js +180 -0
  26. package/lib/rules/block-scoped-var.js +339 -320
  27. package/lib/rules/brace-style.js +228 -228
  28. package/lib/rules/camelcase.js +111 -111
  29. package/lib/rules/comma-dangle.js +67 -64
  30. package/lib/rules/comma-spacing.js +191 -191
  31. package/lib/rules/comma-style.js +195 -195
  32. package/lib/rules/complexity.js +94 -94
  33. package/lib/rules/computed-property-spacing.js +144 -0
  34. package/lib/rules/consistent-return.js +75 -75
  35. package/lib/rules/consistent-this.js +119 -119
  36. package/lib/rules/constructor-super.js +108 -0
  37. package/lib/rules/curly.js +109 -109
  38. package/lib/rules/default-case.js +66 -66
  39. package/lib/rules/dot-location.js +63 -63
  40. package/lib/rules/dot-notation.js +119 -119
  41. package/lib/rules/eol-last.js +38 -38
  42. package/lib/rules/eqeqeq.js +96 -96
  43. package/lib/rules/func-names.js +45 -45
  44. package/lib/rules/func-style.js +49 -49
  45. package/lib/rules/generator-star-spacing.js +104 -87
  46. package/lib/rules/generator-star.js +76 -76
  47. package/lib/rules/global-strict.js +49 -49
  48. package/lib/rules/guard-for-in.js +32 -32
  49. package/lib/rules/handle-callback-err.js +81 -124
  50. package/lib/rules/indent.js +486 -486
  51. package/lib/rules/key-spacing.js +325 -325
  52. package/lib/rules/linebreak-style.js +44 -44
  53. package/lib/rules/lines-around-comment.js +228 -160
  54. package/lib/rules/max-depth.js +89 -89
  55. package/lib/rules/max-len.js +76 -76
  56. package/lib/rules/max-nested-callbacks.js +73 -73
  57. package/lib/rules/max-params.js +45 -45
  58. package/lib/rules/max-statements.js +61 -61
  59. package/lib/rules/new-cap.js +224 -224
  60. package/lib/rules/new-parens.js +29 -29
  61. package/lib/rules/newline-after-var.js +127 -127
  62. package/lib/rules/no-alert.js +153 -153
  63. package/lib/rules/no-array-constructor.js +31 -31
  64. package/lib/rules/no-bitwise.js +57 -57
  65. package/lib/rules/no-caller.js +29 -29
  66. package/lib/rules/no-catch-shadow.js +52 -52
  67. package/lib/rules/no-comma-dangle.js +45 -45
  68. package/lib/rules/no-cond-assign.js +123 -123
  69. package/lib/rules/no-console.js +27 -27
  70. package/lib/rules/no-constant-condition.js +73 -73
  71. package/lib/rules/no-continue.js +23 -23
  72. package/lib/rules/no-control-regex.js +58 -58
  73. package/lib/rules/no-debugger.js +22 -22
  74. package/lib/rules/no-delete-var.js +25 -25
  75. package/lib/rules/no-div-regex.js +27 -27
  76. package/lib/rules/no-dupe-args.js +89 -85
  77. package/lib/rules/no-dupe-keys.js +43 -43
  78. package/lib/rules/no-duplicate-case.js +67 -67
  79. package/lib/rules/no-else-return.js +125 -125
  80. package/lib/rules/no-empty-character-class.js +43 -43
  81. package/lib/rules/no-empty-class.js +45 -45
  82. package/lib/rules/no-empty-label.js +27 -27
  83. package/lib/rules/no-empty.js +49 -49
  84. package/lib/rules/no-eq-null.js +29 -29
  85. package/lib/rules/no-eval.js +26 -26
  86. package/lib/rules/no-ex-assign.js +42 -42
  87. package/lib/rules/no-extend-native.js +103 -103
  88. package/lib/rules/no-extra-bind.js +81 -81
  89. package/lib/rules/no-extra-boolean-cast.js +71 -71
  90. package/lib/rules/no-extra-parens.js +368 -355
  91. package/lib/rules/no-extra-semi.js +70 -23
  92. package/lib/rules/no-extra-strict.js +86 -86
  93. package/lib/rules/no-fallthrough.js +97 -97
  94. package/lib/rules/no-floating-decimal.js +30 -30
  95. package/lib/rules/no-func-assign.js +83 -83
  96. package/lib/rules/no-implied-eval.js +76 -76
  97. package/lib/rules/no-inline-comments.js +49 -49
  98. package/lib/rules/no-inner-declarations.js +78 -78
  99. package/lib/rules/no-invalid-regexp.js +53 -53
  100. package/lib/rules/no-irregular-whitespace.js +135 -135
  101. package/lib/rules/no-iterator.js +28 -28
  102. package/lib/rules/no-label-var.js +64 -64
  103. package/lib/rules/no-labels.js +44 -44
  104. package/lib/rules/no-lone-blocks.js +106 -27
  105. package/lib/rules/no-lonely-if.js +30 -30
  106. package/lib/rules/no-loop-func.js +58 -58
  107. package/lib/rules/no-mixed-requires.js +165 -165
  108. package/lib/rules/no-mixed-spaces-and-tabs.js +74 -74
  109. package/lib/rules/no-multi-spaces.js +119 -119
  110. package/lib/rules/no-multi-str.js +43 -43
  111. package/lib/rules/no-multiple-empty-lines.js +98 -98
  112. package/lib/rules/no-native-reassign.js +62 -62
  113. package/lib/rules/no-negated-in-lhs.js +25 -25
  114. package/lib/rules/no-nested-ternary.js +24 -24
  115. package/lib/rules/no-new-func.js +25 -25
  116. package/lib/rules/no-new-object.js +25 -25
  117. package/lib/rules/no-new-require.js +25 -25
  118. package/lib/rules/no-new-wrappers.js +26 -26
  119. package/lib/rules/no-new.js +27 -27
  120. package/lib/rules/no-obj-calls.js +28 -28
  121. package/lib/rules/no-octal-escape.js +39 -39
  122. package/lib/rules/no-octal.js +25 -25
  123. package/lib/rules/no-param-reassign.js +87 -87
  124. package/lib/rules/no-path-concat.js +39 -39
  125. package/lib/rules/no-plusplus.js +24 -24
  126. package/lib/rules/no-process-env.js +30 -30
  127. package/lib/rules/no-process-exit.js +33 -33
  128. package/lib/rules/no-proto.js +28 -28
  129. package/lib/rules/no-redeclare.js +68 -68
  130. package/lib/rules/no-regex-spaces.js +35 -35
  131. package/lib/rules/no-reserved-keys.js +56 -56
  132. package/lib/rules/no-restricted-modules.js +85 -85
  133. package/lib/rules/no-return-assign.js +53 -24
  134. package/lib/rules/no-script-url.js +34 -34
  135. package/lib/rules/no-self-compare.js +29 -29
  136. package/lib/rules/no-sequences.js +94 -94
  137. package/lib/rules/no-shadow-restricted-names.js +51 -51
  138. package/lib/rules/no-shadow.js +181 -136
  139. package/lib/rules/no-space-before-semi.js +98 -98
  140. package/lib/rules/no-spaced-func.js +37 -37
  141. package/lib/rules/no-sparse-arrays.js +33 -33
  142. package/lib/rules/no-sync.js +30 -30
  143. package/lib/rules/no-ternary.js +24 -24
  144. package/lib/rules/no-this-before-super.js +144 -0
  145. package/lib/rules/no-throw-literal.js +33 -33
  146. package/lib/rules/no-trailing-spaces.js +74 -63
  147. package/lib/rules/no-undef-init.js +28 -28
  148. package/lib/rules/no-undef.js +92 -92
  149. package/lib/rules/no-undefined.js +27 -27
  150. package/lib/rules/no-underscore-dangle.js +73 -73
  151. package/lib/rules/no-unexpected-multiline.js +58 -0
  152. package/lib/rules/no-unneeded-ternary.js +48 -48
  153. package/lib/rules/no-unreachable.js +98 -98
  154. package/lib/rules/no-unused-expressions.js +76 -76
  155. package/lib/rules/no-unused-vars.js +252 -250
  156. package/lib/rules/no-use-before-define.js +105 -105
  157. package/lib/rules/no-var.js +26 -26
  158. package/lib/rules/no-void.js +28 -28
  159. package/lib/rules/no-warning-comments.js +102 -102
  160. package/lib/rules/no-with.js +22 -22
  161. package/lib/rules/no-wrap-func.js +65 -65
  162. package/lib/rules/object-curly-spacing.js +231 -206
  163. package/lib/rules/object-shorthand.js +74 -73
  164. package/lib/rules/one-var.js +311 -304
  165. package/lib/rules/operator-assignment.js +118 -118
  166. package/lib/rules/operator-linebreak.js +114 -114
  167. package/lib/rules/padded-blocks.js +98 -98
  168. package/lib/rules/prefer-const.js +91 -0
  169. package/lib/rules/quote-props.js +72 -72
  170. package/lib/rules/quotes.js +92 -92
  171. package/lib/rules/radix.js +41 -41
  172. package/lib/rules/semi-spacing.js +167 -167
  173. package/lib/rules/semi.js +136 -136
  174. package/lib/rules/sort-vars.js +49 -49
  175. package/lib/rules/space-after-function-name.js +49 -49
  176. package/lib/rules/space-after-keywords.js +82 -82
  177. package/lib/rules/space-before-blocks.js +91 -91
  178. package/lib/rules/space-before-function-paren.js +139 -139
  179. package/lib/rules/space-before-function-parentheses.js +139 -139
  180. package/lib/rules/space-in-brackets.js +305 -305
  181. package/lib/rules/space-in-parens.js +281 -281
  182. package/lib/rules/space-infix-ops.js +106 -106
  183. package/lib/rules/space-return-throw-case.js +38 -38
  184. package/lib/rules/space-unary-ops.js +124 -133
  185. package/lib/rules/spaced-comment.js +143 -0
  186. package/lib/rules/spaced-line-comment.js +89 -89
  187. package/lib/rules/strict.js +242 -242
  188. package/lib/rules/use-isnan.js +26 -26
  189. package/lib/rules/valid-jsdoc.js +215 -215
  190. package/lib/rules/valid-typeof.js +42 -42
  191. package/lib/rules/vars-on-top.js +115 -115
  192. package/lib/rules/wrap-iife.js +48 -48
  193. package/lib/rules/wrap-regex.js +38 -38
  194. package/lib/rules/yoda.js +242 -225
  195. package/lib/rules.js +88 -88
  196. package/lib/timing.js +109 -109
  197. package/lib/token-store.js +201 -201
  198. package/lib/util/traverse.js +105 -105
  199. package/lib/util.js +125 -85
  200. package/package.json +6 -6
  201. package/CHANGELOG.md +0 -1638
package/lib/config.js CHANGED
@@ -1,416 +1,428 @@
1
- /**
2
- * @fileoverview Responsible for loading config files
3
- * @author Seth McLaughlin
4
- * @copyright 2014 Nicholas C. Zakas. All rights reserved.
5
- * @copyright 2013 Seth McLaughlin. All rights reserved.
6
- * @copyright 2014 Michael McLaughlin. All rights reserved.
7
- */
8
- "use strict";
9
-
10
- //------------------------------------------------------------------------------
11
- // Requirements
12
- //------------------------------------------------------------------------------
13
-
14
- var fs = require("fs"),
15
- path = require("path"),
16
- environments = require("../conf/environments"),
17
- util = require("./util"),
18
- FileFinder = require("./file-finder"),
19
- stripComments = require("strip-json-comments"),
20
- assign = require("object-assign"),
21
- debug = require("debug"),
22
- yaml = require("js-yaml"),
23
- userHome = require("user-home"),
24
- isAbsolutePath = require("path-is-absolute");
25
-
26
- //------------------------------------------------------------------------------
27
- // Constants
28
- //------------------------------------------------------------------------------
29
-
30
- var LOCAL_CONFIG_FILENAME = ".eslintrc",
31
- PACKAGE_CONFIG_FILENAME = "package.json",
32
- PACKAGE_CONFIG_FIELD_NAME = "eslintConfig",
33
- PERSONAL_CONFIG_PATH = userHome ? path.join(userHome, LOCAL_CONFIG_FILENAME) : null;
34
-
35
- //------------------------------------------------------------------------------
36
- // Private
37
- //------------------------------------------------------------------------------
38
-
39
- var loadedPlugins = Object.create(null);
40
-
41
- //------------------------------------------------------------------------------
42
- // Helpers
43
- //------------------------------------------------------------------------------
44
-
45
- debug = debug("eslint:config");
46
-
47
- /**
48
- * Determines if a given string represents a filepath or not using the same
49
- * conventions as require(), meaning that the first character must be nonalphanumeric
50
- * and not the @ sign which is used for scoped packages to be considered a file path.
51
- * @param {string} filePath The string to check.
52
- * @returns {boolean} True if it's a filepath, false if not.
53
- * @private
54
- */
55
- function isFilePath(filePath) {
56
- return isAbsolutePath(filePath) || !/\w|@/.test(filePath[0]);
57
- }
58
-
59
- /**
60
- * Load and parse a JSON config object from a file.
61
- * @param {string} filePath the path to the JSON config file
62
- * @returns {Object} the parsed config object (empty object if there was a parse error)
63
- * @private
64
- */
65
- function loadConfig(filePath) {
66
- var config = {};
67
-
68
- if (filePath) {
69
-
70
- if (isFilePath(filePath)) {
71
- try {
72
- config = yaml.safeLoad(stripComments(fs.readFileSync(filePath, "utf8"))) || {};
73
- } catch (e) {
74
- debug("Error reading YAML file: " + filePath);
75
- e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
76
- throw e;
77
- }
78
-
79
- } else {
80
-
81
- // it's a package
82
- if (filePath.indexOf("eslint-config-") === -1) {
83
- if (filePath.indexOf("@") === 0) {
84
- // for scoped packages, insert the eslint-config after the first /
85
- filePath = filePath.replace(/^([^\/]+\/)(.*)$/, "$1eslint-config-$2");
86
- } else {
87
- filePath = "eslint-config-" + filePath;
88
- }
89
- }
90
-
91
- config = util.mergeConfigs(config, require(filePath));
92
- }
93
-
94
- // If an `extends` property is defined, it represents a configuration file to use as
95
- // a "parent". Load the referenced file and merge the configuration recursively.
96
- if (config.extends) {
97
-
98
- var parentPath = config.extends;
99
- if (isFilePath(parentPath)) {
100
- // If the `extends` path is relative, use the directory of the current configuration
101
- // file as the reference point. Otherwise, use as-is.
102
- parentPath = (!isAbsolutePath(parentPath) ?
103
- path.join(path.dirname(filePath), parentPath) :
104
- parentPath
105
- );
106
- }
107
-
108
- // Merge the configuration, ensuring children get preference over the parent
109
- try {
110
- config = util.mergeConfigs(loadConfig(parentPath), config);
111
- } catch (e) {
112
- // If the file referenced by `extends` failed to load, add the path to the
113
- // configuration file that referenced it to the error message so the user is
114
- // able to see where it was referenced from, then re-throw
115
- e.message += "\nReferenced from: " + filePath;
116
- throw e;
117
- }
118
- }
119
-
120
-
121
- }
122
-
123
-
124
- return config;
125
- }
126
-
127
- /**
128
- * Load configuration for all plugins provided.
129
- * @param {string[]} pluginNames An array of plugin names which should be loaded.
130
- * @returns {Object} all plugin configurations merged together
131
- */
132
- function getPluginsConfig(pluginNames) {
133
- var pluginConfig = {};
134
-
135
- pluginNames.forEach(function (pluginName) {
136
- var pluginNamespace = util.getNamespace(pluginName),
137
- pluginNameWithoutNamespace = util.removeNameSpace(pluginName),
138
- pluginNameWithoutPrefix = util.removePluginPrefix(pluginNameWithoutNamespace),
139
- plugin = {},
140
- rules = {};
141
-
142
- if (!loadedPlugins[pluginNameWithoutPrefix]) {
143
- try {
144
- plugin = require(pluginNamespace + util.PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix);
145
- loadedPlugins[pluginNameWithoutPrefix] = plugin;
146
- } catch(err) {
147
- debug("Failed to load plugin configuration for " + pluginNameWithoutPrefix + ". Proceeding without it.");
148
- plugin = { rulesConfig: {}};
149
- }
150
- } else {
151
- plugin = loadedPlugins[pluginNameWithoutPrefix];
152
- }
153
-
154
- if (!plugin.rulesConfig) {
155
- plugin.rulesConfig = {};
156
- }
157
-
158
- Object.keys(plugin.rulesConfig).forEach(function(item) {
159
- rules[pluginNameWithoutPrefix + "/" + item] = plugin.rulesConfig[item];
160
- });
161
-
162
- pluginConfig = util.mergeConfigs(pluginConfig, rules);
163
- });
164
-
165
- return {rules: pluginConfig};
166
- }
167
-
168
- /**
169
- * Get personal config object from ~/.eslintrc.
170
- * @returns {Object} the personal config object (empty object if there is no personal config)
171
- * @private
172
- */
173
- function getPersonalConfig() {
174
- var config = {};
175
-
176
- if (PERSONAL_CONFIG_PATH && fs.existsSync(PERSONAL_CONFIG_PATH)) {
177
- debug("Using personal config");
178
- config = loadConfig(PERSONAL_CONFIG_PATH);
179
- }
180
-
181
- return config;
182
- }
183
-
184
- /**
185
- * Get a local config object.
186
- * @param {Object} thisConfig A Config object.
187
- * @param {string} directory The directory to start looking in for a local config file.
188
- * @returns {Object} The local config object, or an empty object if there is no local config.
189
- */
190
- function getLocalConfig(thisConfig, directory) {
191
- var found,
192
- i,
193
- localConfig,
194
- localConfigFile,
195
- config = {},
196
- localConfigFiles = thisConfig.findLocalConfigFiles(directory),
197
- numFiles = localConfigFiles.length;
198
-
199
- for (i = 0; i < numFiles; i++) {
200
-
201
- localConfigFile = localConfigFiles[i];
202
-
203
- // Don't consider the personal config file in the home directory.
204
- if (localConfigFile === PERSONAL_CONFIG_PATH) {
205
- continue;
206
- }
207
-
208
- debug("Loading " + localConfigFile);
209
- localConfig = loadConfig(localConfigFile);
210
-
211
- if (path.basename(localConfigFile) !== LOCAL_CONFIG_FILENAME) {
212
-
213
- // Don't consider a local config file found if the package.json doesn't have the eslintConfig field.
214
- if (!localConfig.hasOwnProperty(PACKAGE_CONFIG_FIELD_NAME)) {
215
- continue;
216
- }
217
- localConfig = localConfig[PACKAGE_CONFIG_FIELD_NAME] || {};
218
- }
219
-
220
- found = true;
221
- debug("Using " + localConfigFile);
222
- config = util.mergeConfigs(localConfig, config);
223
- }
224
-
225
- // Use the personal config file if there are no other local config files found.
226
- return found ? config : util.mergeConfigs(config, getPersonalConfig());
227
- }
228
-
229
- /**
230
- * Creates an environment config based on the specified environments.
231
- * @param {Object<string,boolean>} envs The environment settings.
232
- * @param {boolean} reset The value of the command line reset option. If true,
233
- * rules are not automatically merged into the config.
234
- * @returns {Object} A configuration object with the appropriate rules and globals
235
- * set.
236
- * @private
237
- */
238
- function createEnvironmentConfig(envs, reset) {
239
-
240
- var envConfig = {
241
- globals: {},
242
- env: envs || {},
243
- rules: {},
244
- ecmaFeatures: {}
245
- };
246
-
247
- if (envs) {
248
- Object.keys(envs).filter(function (name) {
249
- return envs[name];
250
- }).forEach(function(name) {
251
- var environment = environments[name];
252
-
253
- if (environment) {
254
-
255
- if (!reset && environment.rules) {
256
- assign(envConfig.rules, environment.rules);
257
- }
258
-
259
- if (environment.globals) {
260
- assign(envConfig.globals, environment.globals);
261
- }
262
-
263
- if (environment.ecmaFeatures) {
264
- assign(envConfig.ecmaFeatures, environment.ecmaFeatures);
265
- }
266
- }
267
- });
268
- }
269
-
270
- return envConfig;
271
- }
272
-
273
- //------------------------------------------------------------------------------
274
- // API
275
- //------------------------------------------------------------------------------
276
-
277
- /**
278
- * Config
279
- * @constructor
280
- * @class Config
281
- * @param {Object} options Options to be passed in
282
- * @param {string} [cwd] current working directory. Defaults to process.cwd()
283
- */
284
- function Config(options) {
285
- var useConfig;
286
-
287
- options = options || {};
288
-
289
- this.ignore = options.ignore;
290
- this.ignorePath = options.ignorePath;
291
- this.cache = {};
292
-
293
- if (options.reset || options.baseConfig === false) {
294
- // If `options.reset` is truthy or `options.baseConfig` is set to `false`,
295
- // disable all default rules and environments
296
- this.baseConfig = { rules: {} };
297
- } else {
298
- // If `options.baseConfig` is an object, just use it,
299
- // otherwise use default base config from `conf/eslint.json`
300
- this.baseConfig = options.baseConfig ||
301
- require(path.resolve(__dirname, "..", "conf", "eslint.json"));
302
- }
303
-
304
- this.useEslintrc = (options.useEslintrc !== false);
305
-
306
- this.env = (options.envs || []).reduce(function (envs, name) {
307
- envs[name] = true;
308
- return envs;
309
- }, {});
310
-
311
- this.globals = (options.globals || []).reduce(function (globals, def) {
312
- // Default "foo" to false and handle "foo:false" and "foo:true"
313
- var parts = def.split(":");
314
- globals[parts[0]] = (parts.length > 1 && parts[1] === "true");
315
- return globals;
316
- }, {});
317
-
318
- useConfig = options.configFile;
319
- this.options = options;
320
-
321
- if (useConfig) {
322
- debug("Using command line config " + useConfig);
323
- this.useSpecificConfig = loadConfig(path.resolve(process.cwd(), useConfig));
324
- }
325
- }
326
-
327
- /**
328
- * Build a config object merging the base config (conf/eslint.json), the
329
- * environments config (conf/environments.js) and eventually the user config.
330
- * @param {string} filePath a file in whose directory we start looking for a local config
331
- * @returns {Object} config object
332
- */
333
- Config.prototype.getConfig = function (filePath) {
334
- var config,
335
- userConfig,
336
- directory = filePath ? path.dirname(filePath) : process.cwd(),
337
- pluginConfig;
338
-
339
- debug("Constructing config for " + (filePath ? filePath : "text"));
340
-
341
- config = this.cache[directory];
342
-
343
- if (config) {
344
- debug("Using config from cache");
345
- return config;
346
- }
347
-
348
- // Step 1: Determine user-specified config from .eslintrc and package.json files
349
- if (this.useEslintrc) {
350
- debug("Using .eslintrc and package.json files");
351
- userConfig = getLocalConfig(this, directory);
352
- } else {
353
- debug("Not using .eslintrc or package.json files");
354
- userConfig = {};
355
- }
356
-
357
- // Step 2: Create a copy of the baseConfig
358
- config = util.mergeConfigs({}, this.baseConfig);
359
-
360
- // Step 3: Merge in environment-specific globals and rules from .eslintrc files
361
- config = util.mergeConfigs(config, createEnvironmentConfig(userConfig.env, this.options.reset));
362
-
363
- // Step 4: Merge in the user-specified configuration from .eslintrc and package.json
364
- config = util.mergeConfigs(config, userConfig);
365
-
366
- // Step 5: Merge in command line config file
367
- if (this.useSpecificConfig) {
368
- debug("Merging command line config file");
369
-
370
- if (this.useSpecificConfig.env) {
371
- config = util.mergeConfigs(config, createEnvironmentConfig(this.useSpecificConfig.env, this.options.reset));
372
- }
373
-
374
- config = util.mergeConfigs(config, this.useSpecificConfig);
375
- }
376
-
377
- // Step 6: Merge in command line environments
378
- debug("Merging command line environment settings");
379
- config = util.mergeConfigs(config, createEnvironmentConfig(this.env, this.options.reset));
380
-
381
- // Step 7: Merge in command line rules
382
- if (this.options.rules) {
383
- debug("Merging command line rules");
384
- config = util.mergeConfigs(config, { rules: this.options.rules });
385
- }
386
-
387
- // Step 8: Merge in command line globals
388
- config = util.mergeConfigs(config, { globals: this.globals });
389
-
390
-
391
- // Step 9: Merge in plugin specific rules in reverse
392
- if (config.plugins) {
393
- pluginConfig = getPluginsConfig(config.plugins);
394
- config = util.mergeConfigs(pluginConfig, config);
395
- }
396
-
397
- this.cache[directory] = config;
398
-
399
- return config;
400
- };
401
-
402
- /**
403
- * Find local config files from directory and parent directories.
404
- * @param {string} directory The directory to start searching from.
405
- * @returns {string[]} The paths of local config files found.
406
- */
407
- Config.prototype.findLocalConfigFiles = function (directory) {
408
-
409
- if (!this.localConfigFinder) {
410
- this.localConfigFinder = new FileFinder(LOCAL_CONFIG_FILENAME, PACKAGE_CONFIG_FILENAME);
411
- }
412
-
413
- return this.localConfigFinder.findAllInDirectoryAndParents(directory);
414
- };
415
-
416
- module.exports = Config;
1
+ /**
2
+ * @fileoverview Responsible for loading config files
3
+ * @author Seth McLaughlin
4
+ * @copyright 2014 Nicholas C. Zakas. All rights reserved.
5
+ * @copyright 2013 Seth McLaughlin. All rights reserved.
6
+ * @copyright 2014 Michael McLaughlin. All rights reserved.
7
+ */
8
+ "use strict";
9
+
10
+ //------------------------------------------------------------------------------
11
+ // Requirements
12
+ //------------------------------------------------------------------------------
13
+
14
+ var fs = require("fs"),
15
+ path = require("path"),
16
+ environments = require("../conf/environments"),
17
+ util = require("./util"),
18
+ FileFinder = require("./file-finder"),
19
+ stripComments = require("strip-json-comments"),
20
+ assign = require("object-assign"),
21
+ debug = require("debug"),
22
+ yaml = require("js-yaml"),
23
+ userHome = require("user-home"),
24
+ isAbsolutePath = require("path-is-absolute"),
25
+ validator = require("./config-validator");
26
+
27
+ //------------------------------------------------------------------------------
28
+ // Constants
29
+ //------------------------------------------------------------------------------
30
+
31
+ var LOCAL_CONFIG_FILENAME = ".eslintrc",
32
+ PACKAGE_CONFIG_FILENAME = "package.json",
33
+ PACKAGE_CONFIG_FIELD_NAME = "eslintConfig",
34
+ PERSONAL_CONFIG_PATH = userHome ? path.join(userHome, LOCAL_CONFIG_FILENAME) : null;
35
+
36
+ //------------------------------------------------------------------------------
37
+ // Private
38
+ //------------------------------------------------------------------------------
39
+
40
+ var loadedPlugins = Object.create(null);
41
+
42
+ //------------------------------------------------------------------------------
43
+ // Helpers
44
+ //------------------------------------------------------------------------------
45
+
46
+ debug = debug("eslint:config");
47
+
48
+ /**
49
+ * Determines if a given string represents a filepath or not using the same
50
+ * conventions as require(), meaning that the first character must be nonalphanumeric
51
+ * and not the @ sign which is used for scoped packages to be considered a file path.
52
+ * @param {string} filePath The string to check.
53
+ * @returns {boolean} True if it's a filepath, false if not.
54
+ * @private
55
+ */
56
+ function isFilePath(filePath) {
57
+ return isAbsolutePath(filePath) || !/\w|@/.test(filePath[0]);
58
+ }
59
+
60
+ /**
61
+ * Load and parse a JSON config object from a file.
62
+ * @param {string} filePath the path to the JSON config file
63
+ * @returns {Object} the parsed config object (empty object if there was a parse error)
64
+ * @private
65
+ */
66
+ function loadConfig(filePath) {
67
+ var config = {};
68
+
69
+ if (filePath) {
70
+
71
+ if (isFilePath(filePath)) {
72
+ try {
73
+ config = yaml.safeLoad(stripComments(fs.readFileSync(filePath, "utf8"))) || {};
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
+ if (path.basename(filePath) === PACKAGE_CONFIG_FILENAME) {
81
+ config = config[PACKAGE_CONFIG_FIELD_NAME] || {};
82
+ }
83
+
84
+ } else {
85
+
86
+ // it's a package
87
+ if (filePath.indexOf("eslint-config-") === -1) {
88
+ if (filePath.indexOf("@") === 0) {
89
+ // for scoped packages, insert the eslint-config after the first /
90
+ filePath = filePath.replace(/^([^\/]+\/)(.*)$/, "$1eslint-config-$2");
91
+ } else {
92
+ filePath = "eslint-config-" + filePath;
93
+ }
94
+ }
95
+
96
+ config = util.mergeConfigs(config, require(filePath));
97
+ }
98
+
99
+ validator.validate(config, filePath);
100
+
101
+ // If an `extends` property is defined, it represents a configuration file to use as
102
+ // a "parent". Load the referenced file and merge the configuration recursively.
103
+ if (config.extends) {
104
+ var configExtends = config.extends;
105
+
106
+ if (!Array.isArray(config.extends)) {
107
+ configExtends = [config.extends];
108
+ }
109
+
110
+ // Make the last element in an array take the highest precedence
111
+ config = configExtends.reduceRight(function (previousValue, parentPath) {
112
+
113
+ if (isFilePath(parentPath)) {
114
+ // If the `extends` path is relative, use the directory of the current configuration
115
+ // file as the reference point. Otherwise, use as-is.
116
+ parentPath = (!isAbsolutePath(parentPath) ?
117
+ path.join(path.dirname(filePath), parentPath) :
118
+ parentPath
119
+ );
120
+ }
121
+
122
+ try {
123
+ return util.mergeConfigs(loadConfig(parentPath), previousValue);
124
+ } catch (e) {
125
+ // If the file referenced by `extends` failed to load, add the path to the
126
+ // configuration file that referenced it to the error message so the user is
127
+ // able to see where it was referenced from, then re-throw
128
+ e.message += "\nReferenced from: " + filePath;
129
+ throw e;
130
+ }
131
+
132
+ }, config);
133
+
134
+ }
135
+
136
+
137
+ }
138
+
139
+
140
+ return config;
141
+ }
142
+
143
+ /**
144
+ * Load configuration for all plugins provided.
145
+ * @param {string[]} pluginNames An array of plugin names which should be loaded.
146
+ * @returns {Object} all plugin configurations merged together
147
+ */
148
+ function getPluginsConfig(pluginNames) {
149
+ var pluginConfig = {};
150
+
151
+ pluginNames.forEach(function (pluginName) {
152
+ var pluginNamespace = util.getNamespace(pluginName),
153
+ pluginNameWithoutNamespace = util.removeNameSpace(pluginName),
154
+ pluginNameWithoutPrefix = util.removePluginPrefix(pluginNameWithoutNamespace),
155
+ plugin = {},
156
+ rules = {};
157
+
158
+ if (!loadedPlugins[pluginNameWithoutPrefix]) {
159
+ try {
160
+ plugin = require(pluginNamespace + util.PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix);
161
+ loadedPlugins[pluginNameWithoutPrefix] = plugin;
162
+ } catch(err) {
163
+ debug("Failed to load plugin configuration for " + pluginNameWithoutPrefix + ". Proceeding without it.");
164
+ plugin = { rulesConfig: {}};
165
+ }
166
+ } else {
167
+ plugin = loadedPlugins[pluginNameWithoutPrefix];
168
+ }
169
+
170
+ if (!plugin.rulesConfig) {
171
+ plugin.rulesConfig = {};
172
+ }
173
+
174
+ Object.keys(plugin.rulesConfig).forEach(function(item) {
175
+ rules[pluginNameWithoutPrefix + "/" + item] = plugin.rulesConfig[item];
176
+ });
177
+
178
+ pluginConfig = util.mergeConfigs(pluginConfig, rules);
179
+ });
180
+
181
+ return {rules: pluginConfig};
182
+ }
183
+
184
+ /**
185
+ * Get personal config object from ~/.eslintrc.
186
+ * @returns {Object} the personal config object (empty object if there is no personal config)
187
+ * @private
188
+ */
189
+ function getPersonalConfig() {
190
+ var config = {};
191
+
192
+ if (PERSONAL_CONFIG_PATH && fs.existsSync(PERSONAL_CONFIG_PATH)) {
193
+ debug("Using personal config");
194
+ config = loadConfig(PERSONAL_CONFIG_PATH);
195
+ }
196
+
197
+ return config;
198
+ }
199
+
200
+ /**
201
+ * Get a local config object.
202
+ * @param {Object} thisConfig A Config object.
203
+ * @param {string} directory The directory to start looking in for a local config file.
204
+ * @returns {Object} The local config object, or an empty object if there is no local config.
205
+ */
206
+ function getLocalConfig(thisConfig, directory) {
207
+ var found,
208
+ i,
209
+ localConfig,
210
+ localConfigFile,
211
+ config = {},
212
+ localConfigFiles = thisConfig.findLocalConfigFiles(directory),
213
+ numFiles = localConfigFiles.length;
214
+
215
+ for (i = 0; i < numFiles; i++) {
216
+
217
+ localConfigFile = localConfigFiles[i];
218
+
219
+ // Don't consider the personal config file in the home directory.
220
+ if (localConfigFile === PERSONAL_CONFIG_PATH) {
221
+ continue;
222
+ }
223
+
224
+ debug("Loading " + localConfigFile);
225
+ localConfig = loadConfig(localConfigFile);
226
+
227
+ // Don't consider a local config file found if the config is empty.
228
+ if (!Object.keys(localConfig).length) {
229
+ continue;
230
+ }
231
+
232
+ found = true;
233
+ debug("Using " + localConfigFile);
234
+ config = util.mergeConfigs(localConfig, config);
235
+ }
236
+
237
+ // Use the personal config file if there are no other local config files found.
238
+ return found ? config : util.mergeConfigs(config, getPersonalConfig());
239
+ }
240
+
241
+ /**
242
+ * Creates an environment config based on the specified environments.
243
+ * @param {Object<string,boolean>} envs The environment settings.
244
+ * @param {boolean} reset The value of the command line reset option. If true,
245
+ * rules are not automatically merged into the config.
246
+ * @returns {Object} A configuration object with the appropriate rules and globals
247
+ * set.
248
+ * @private
249
+ */
250
+ function createEnvironmentConfig(envs, reset) {
251
+
252
+ var envConfig = {
253
+ globals: {},
254
+ env: envs || {},
255
+ rules: {},
256
+ ecmaFeatures: {}
257
+ };
258
+
259
+ if (envs) {
260
+ Object.keys(envs).filter(function (name) {
261
+ return envs[name];
262
+ }).forEach(function(name) {
263
+ var environment = environments[name];
264
+
265
+ if (environment) {
266
+
267
+ if (!reset && environment.rules) {
268
+ assign(envConfig.rules, environment.rules);
269
+ }
270
+
271
+ if (environment.globals) {
272
+ assign(envConfig.globals, environment.globals);
273
+ }
274
+
275
+ if (environment.ecmaFeatures) {
276
+ assign(envConfig.ecmaFeatures, environment.ecmaFeatures);
277
+ }
278
+ }
279
+ });
280
+ }
281
+
282
+ return envConfig;
283
+ }
284
+
285
+ //------------------------------------------------------------------------------
286
+ // API
287
+ //------------------------------------------------------------------------------
288
+
289
+ /**
290
+ * Config
291
+ * @constructor
292
+ * @class Config
293
+ * @param {Object} options Options to be passed in
294
+ * @param {string} [cwd] current working directory. Defaults to process.cwd()
295
+ */
296
+ function Config(options) {
297
+ var useConfig;
298
+
299
+ options = options || {};
300
+
301
+ this.ignore = options.ignore;
302
+ this.ignorePath = options.ignorePath;
303
+ this.cache = {};
304
+
305
+ if (options.reset || options.baseConfig === false) {
306
+ // If `options.reset` is truthy or `options.baseConfig` is set to `false`,
307
+ // disable all default rules and environments
308
+ this.baseConfig = { rules: {} };
309
+ } else {
310
+ // If `options.baseConfig` is an object, just use it,
311
+ // otherwise use default base config from `conf/eslint.json`
312
+ this.baseConfig = options.baseConfig ||
313
+ require(path.resolve(__dirname, "..", "conf", "eslint.json"));
314
+ }
315
+
316
+ this.useEslintrc = (options.useEslintrc !== false);
317
+
318
+ this.env = (options.envs || []).reduce(function (envs, name) {
319
+ envs[name] = true;
320
+ return envs;
321
+ }, {});
322
+
323
+ this.globals = (options.globals || []).reduce(function (globals, def) {
324
+ // Default "foo" to false and handle "foo:false" and "foo:true"
325
+ var parts = def.split(":");
326
+ globals[parts[0]] = (parts.length > 1 && parts[1] === "true");
327
+ return globals;
328
+ }, {});
329
+
330
+ useConfig = options.configFile;
331
+ this.options = options;
332
+
333
+ if (useConfig) {
334
+ debug("Using command line config " + useConfig);
335
+ this.useSpecificConfig = loadConfig(path.resolve(process.cwd(), useConfig));
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Build a config object merging the base config (conf/eslint.json), the
341
+ * environments config (conf/environments.js) and eventually the user config.
342
+ * @param {string} filePath a file in whose directory we start looking for a local config
343
+ * @returns {Object} config object
344
+ */
345
+ Config.prototype.getConfig = function (filePath) {
346
+ var config,
347
+ userConfig,
348
+ directory = filePath ? path.dirname(filePath) : process.cwd(),
349
+ pluginConfig;
350
+
351
+ debug("Constructing config for " + (filePath ? filePath : "text"));
352
+
353
+ config = this.cache[directory];
354
+
355
+ if (config) {
356
+ debug("Using config from cache");
357
+ return config;
358
+ }
359
+
360
+ // Step 1: Determine user-specified config from .eslintrc and package.json files
361
+ if (this.useEslintrc) {
362
+ debug("Using .eslintrc and package.json files");
363
+ userConfig = getLocalConfig(this, directory);
364
+ } else {
365
+ debug("Not using .eslintrc or package.json files");
366
+ userConfig = {};
367
+ }
368
+
369
+ // Step 2: Create a copy of the baseConfig
370
+ config = util.mergeConfigs({}, this.baseConfig);
371
+
372
+ // Step 3: Merge in environment-specific globals and rules from .eslintrc files
373
+ config = util.mergeConfigs(config, createEnvironmentConfig(userConfig.env, this.options.reset));
374
+
375
+ // Step 4: Merge in the user-specified configuration from .eslintrc and package.json
376
+ config = util.mergeConfigs(config, userConfig);
377
+
378
+ // Step 5: Merge in command line config file
379
+ if (this.useSpecificConfig) {
380
+ debug("Merging command line config file");
381
+
382
+ if (this.useSpecificConfig.env) {
383
+ config = util.mergeConfigs(config, createEnvironmentConfig(this.useSpecificConfig.env, this.options.reset));
384
+ }
385
+
386
+ config = util.mergeConfigs(config, this.useSpecificConfig);
387
+ }
388
+
389
+ // Step 6: Merge in command line environments
390
+ debug("Merging command line environment settings");
391
+ config = util.mergeConfigs(config, createEnvironmentConfig(this.env, this.options.reset));
392
+
393
+ // Step 7: Merge in command line rules
394
+ if (this.options.rules) {
395
+ debug("Merging command line rules");
396
+ config = util.mergeConfigs(config, { rules: this.options.rules });
397
+ }
398
+
399
+ // Step 8: Merge in command line globals
400
+ config = util.mergeConfigs(config, { globals: this.globals });
401
+
402
+
403
+ // Step 9: Merge in plugin specific rules in reverse
404
+ if (config.plugins) {
405
+ pluginConfig = getPluginsConfig(config.plugins);
406
+ config = util.mergeConfigs(pluginConfig, config);
407
+ }
408
+
409
+ this.cache[directory] = config;
410
+
411
+ return config;
412
+ };
413
+
414
+ /**
415
+ * Find local config files from directory and parent directories.
416
+ * @param {string} directory The directory to start searching from.
417
+ * @returns {string[]} The paths of local config files found.
418
+ */
419
+ Config.prototype.findLocalConfigFiles = function (directory) {
420
+
421
+ if (!this.localConfigFinder) {
422
+ this.localConfigFinder = new FileFinder(LOCAL_CONFIG_FILENAME, PACKAGE_CONFIG_FILENAME);
423
+ }
424
+
425
+ return this.localConfigFinder.findAllInDirectoryAndParents(directory);
426
+ };
427
+
428
+ module.exports = Config;