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.
- package/LICENSE +20 -20
- package/README.md +111 -95
- package/bin/eslint.js +41 -41
- package/conf/environments.js +87 -81
- package/conf/eslint.json +186 -179
- package/lib/api.js +13 -12
- package/lib/cli-engine.js +441 -451
- package/lib/cli.js +196 -196
- package/lib/config-initializer.js +145 -145
- package/lib/config-validator.js +110 -110
- package/lib/config.js +428 -416
- package/lib/eslint.js +1072 -1073
- package/lib/file-finder.js +167 -167
- package/lib/formatters/checkstyle.js +68 -68
- package/lib/formatters/compact.js +53 -53
- package/lib/formatters/jslint-xml.js +40 -40
- package/lib/formatters/junit.js +63 -63
- package/lib/formatters/stylish.js +90 -90
- package/lib/formatters/tap.js +86 -86
- package/lib/ignored-paths.js +137 -137
- package/lib/load-rules.js +39 -39
- package/lib/options.js +132 -126
- package/lib/rule-context.js +107 -107
- package/lib/rules/accessor-pairs.js +65 -65
- package/lib/rules/array-bracket-spacing.js +180 -0
- package/lib/rules/block-scoped-var.js +339 -320
- package/lib/rules/brace-style.js +228 -228
- package/lib/rules/camelcase.js +111 -111
- package/lib/rules/comma-dangle.js +67 -64
- package/lib/rules/comma-spacing.js +191 -191
- package/lib/rules/comma-style.js +195 -195
- package/lib/rules/complexity.js +94 -94
- package/lib/rules/computed-property-spacing.js +144 -0
- package/lib/rules/consistent-return.js +75 -75
- package/lib/rules/consistent-this.js +119 -119
- package/lib/rules/constructor-super.js +108 -0
- package/lib/rules/curly.js +109 -109
- package/lib/rules/default-case.js +66 -66
- package/lib/rules/dot-location.js +63 -63
- package/lib/rules/dot-notation.js +119 -119
- package/lib/rules/eol-last.js +38 -38
- package/lib/rules/eqeqeq.js +96 -96
- package/lib/rules/func-names.js +45 -45
- package/lib/rules/func-style.js +49 -49
- package/lib/rules/generator-star-spacing.js +104 -87
- package/lib/rules/generator-star.js +76 -76
- package/lib/rules/global-strict.js +49 -49
- package/lib/rules/guard-for-in.js +32 -32
- package/lib/rules/handle-callback-err.js +81 -124
- package/lib/rules/indent.js +486 -486
- package/lib/rules/key-spacing.js +325 -325
- package/lib/rules/linebreak-style.js +44 -44
- package/lib/rules/lines-around-comment.js +228 -160
- package/lib/rules/max-depth.js +89 -89
- package/lib/rules/max-len.js +76 -76
- package/lib/rules/max-nested-callbacks.js +73 -73
- package/lib/rules/max-params.js +45 -45
- package/lib/rules/max-statements.js +61 -61
- package/lib/rules/new-cap.js +224 -224
- package/lib/rules/new-parens.js +29 -29
- package/lib/rules/newline-after-var.js +127 -127
- package/lib/rules/no-alert.js +153 -153
- package/lib/rules/no-array-constructor.js +31 -31
- package/lib/rules/no-bitwise.js +57 -57
- package/lib/rules/no-caller.js +29 -29
- package/lib/rules/no-catch-shadow.js +52 -52
- package/lib/rules/no-comma-dangle.js +45 -45
- package/lib/rules/no-cond-assign.js +123 -123
- package/lib/rules/no-console.js +27 -27
- package/lib/rules/no-constant-condition.js +73 -73
- package/lib/rules/no-continue.js +23 -23
- package/lib/rules/no-control-regex.js +58 -58
- package/lib/rules/no-debugger.js +22 -22
- package/lib/rules/no-delete-var.js +25 -25
- package/lib/rules/no-div-regex.js +27 -27
- package/lib/rules/no-dupe-args.js +89 -85
- package/lib/rules/no-dupe-keys.js +43 -43
- package/lib/rules/no-duplicate-case.js +67 -67
- package/lib/rules/no-else-return.js +125 -125
- package/lib/rules/no-empty-character-class.js +43 -43
- package/lib/rules/no-empty-class.js +45 -45
- package/lib/rules/no-empty-label.js +27 -27
- package/lib/rules/no-empty.js +49 -49
- package/lib/rules/no-eq-null.js +29 -29
- package/lib/rules/no-eval.js +26 -26
- package/lib/rules/no-ex-assign.js +42 -42
- package/lib/rules/no-extend-native.js +103 -103
- package/lib/rules/no-extra-bind.js +81 -81
- package/lib/rules/no-extra-boolean-cast.js +71 -71
- package/lib/rules/no-extra-parens.js +368 -355
- package/lib/rules/no-extra-semi.js +70 -23
- package/lib/rules/no-extra-strict.js +86 -86
- package/lib/rules/no-fallthrough.js +97 -97
- package/lib/rules/no-floating-decimal.js +30 -30
- package/lib/rules/no-func-assign.js +83 -83
- package/lib/rules/no-implied-eval.js +76 -76
- package/lib/rules/no-inline-comments.js +49 -49
- package/lib/rules/no-inner-declarations.js +78 -78
- package/lib/rules/no-invalid-regexp.js +53 -53
- package/lib/rules/no-irregular-whitespace.js +135 -135
- package/lib/rules/no-iterator.js +28 -28
- package/lib/rules/no-label-var.js +64 -64
- package/lib/rules/no-labels.js +44 -44
- package/lib/rules/no-lone-blocks.js +106 -27
- package/lib/rules/no-lonely-if.js +30 -30
- package/lib/rules/no-loop-func.js +58 -58
- package/lib/rules/no-mixed-requires.js +165 -165
- package/lib/rules/no-mixed-spaces-and-tabs.js +74 -74
- package/lib/rules/no-multi-spaces.js +119 -119
- package/lib/rules/no-multi-str.js +43 -43
- package/lib/rules/no-multiple-empty-lines.js +98 -98
- package/lib/rules/no-native-reassign.js +62 -62
- package/lib/rules/no-negated-in-lhs.js +25 -25
- package/lib/rules/no-nested-ternary.js +24 -24
- package/lib/rules/no-new-func.js +25 -25
- package/lib/rules/no-new-object.js +25 -25
- package/lib/rules/no-new-require.js +25 -25
- package/lib/rules/no-new-wrappers.js +26 -26
- package/lib/rules/no-new.js +27 -27
- package/lib/rules/no-obj-calls.js +28 -28
- package/lib/rules/no-octal-escape.js +39 -39
- package/lib/rules/no-octal.js +25 -25
- package/lib/rules/no-param-reassign.js +87 -87
- package/lib/rules/no-path-concat.js +39 -39
- package/lib/rules/no-plusplus.js +24 -24
- package/lib/rules/no-process-env.js +30 -30
- package/lib/rules/no-process-exit.js +33 -33
- package/lib/rules/no-proto.js +28 -28
- package/lib/rules/no-redeclare.js +68 -68
- package/lib/rules/no-regex-spaces.js +35 -35
- package/lib/rules/no-reserved-keys.js +56 -56
- package/lib/rules/no-restricted-modules.js +85 -85
- package/lib/rules/no-return-assign.js +53 -24
- package/lib/rules/no-script-url.js +34 -34
- package/lib/rules/no-self-compare.js +29 -29
- package/lib/rules/no-sequences.js +94 -94
- package/lib/rules/no-shadow-restricted-names.js +51 -51
- package/lib/rules/no-shadow.js +181 -136
- package/lib/rules/no-space-before-semi.js +98 -98
- package/lib/rules/no-spaced-func.js +37 -37
- package/lib/rules/no-sparse-arrays.js +33 -33
- package/lib/rules/no-sync.js +30 -30
- package/lib/rules/no-ternary.js +24 -24
- package/lib/rules/no-this-before-super.js +144 -0
- package/lib/rules/no-throw-literal.js +33 -33
- package/lib/rules/no-trailing-spaces.js +74 -63
- package/lib/rules/no-undef-init.js +28 -28
- package/lib/rules/no-undef.js +92 -92
- package/lib/rules/no-undefined.js +27 -27
- package/lib/rules/no-underscore-dangle.js +73 -73
- package/lib/rules/no-unexpected-multiline.js +58 -0
- package/lib/rules/no-unneeded-ternary.js +48 -48
- package/lib/rules/no-unreachable.js +98 -98
- package/lib/rules/no-unused-expressions.js +76 -76
- package/lib/rules/no-unused-vars.js +252 -250
- package/lib/rules/no-use-before-define.js +105 -105
- package/lib/rules/no-var.js +26 -26
- package/lib/rules/no-void.js +28 -28
- package/lib/rules/no-warning-comments.js +102 -102
- package/lib/rules/no-with.js +22 -22
- package/lib/rules/no-wrap-func.js +65 -65
- package/lib/rules/object-curly-spacing.js +231 -206
- package/lib/rules/object-shorthand.js +74 -73
- package/lib/rules/one-var.js +311 -304
- package/lib/rules/operator-assignment.js +118 -118
- package/lib/rules/operator-linebreak.js +114 -114
- package/lib/rules/padded-blocks.js +98 -98
- package/lib/rules/prefer-const.js +91 -0
- package/lib/rules/quote-props.js +72 -72
- package/lib/rules/quotes.js +92 -92
- package/lib/rules/radix.js +41 -41
- package/lib/rules/semi-spacing.js +167 -167
- package/lib/rules/semi.js +136 -136
- package/lib/rules/sort-vars.js +49 -49
- package/lib/rules/space-after-function-name.js +49 -49
- package/lib/rules/space-after-keywords.js +82 -82
- package/lib/rules/space-before-blocks.js +91 -91
- package/lib/rules/space-before-function-paren.js +139 -139
- package/lib/rules/space-before-function-parentheses.js +139 -139
- package/lib/rules/space-in-brackets.js +305 -305
- package/lib/rules/space-in-parens.js +281 -281
- package/lib/rules/space-infix-ops.js +106 -106
- package/lib/rules/space-return-throw-case.js +38 -38
- package/lib/rules/space-unary-ops.js +124 -133
- package/lib/rules/spaced-comment.js +143 -0
- package/lib/rules/spaced-line-comment.js +89 -89
- package/lib/rules/strict.js +242 -242
- package/lib/rules/use-isnan.js +26 -26
- package/lib/rules/valid-jsdoc.js +215 -215
- package/lib/rules/valid-typeof.js +42 -42
- package/lib/rules/vars-on-top.js +115 -115
- package/lib/rules/wrap-iife.js +48 -48
- package/lib/rules/wrap-regex.js +38 -38
- package/lib/rules/yoda.js +242 -225
- package/lib/rules.js +88 -88
- package/lib/timing.js +109 -109
- package/lib/token-store.js +201 -201
- package/lib/util/traverse.js +105 -105
- package/lib/util.js +125 -85
- package/package.json +6 -6
- package/CHANGELOG.md +0 -1638
package/lib/eslint.js
CHANGED
@@ -1,1073 +1,1072 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview Main ESLint object.
|
3
|
-
* @author Nicholas C. Zakas
|
4
|
-
*/
|
5
|
-
"use strict";
|
6
|
-
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Requirements
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
var estraverse = require("estraverse-fb"),
|
12
|
-
escope = require("escope"),
|
13
|
-
environments = require("../conf/environments"),
|
14
|
-
assign = require("object-assign"),
|
15
|
-
rules = require("./rules"),
|
16
|
-
util = require("./util"),
|
17
|
-
RuleContext = require("./rule-context"),
|
18
|
-
timing = require("./timing"),
|
19
|
-
createTokenStore = require("./token-store.js"),
|
20
|
-
EventEmitter = require("events").EventEmitter,
|
21
|
-
escapeRegExp = require("escape-string-regexp")
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
*
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
string
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
*
|
62
|
-
* @
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
}
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
*
|
127
|
-
*
|
128
|
-
*
|
129
|
-
* @
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
*
|
176
|
-
*
|
177
|
-
* @
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
function
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
*
|
203
|
-
*
|
204
|
-
* @
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
*
|
239
|
-
*
|
240
|
-
*
|
241
|
-
* @param {
|
242
|
-
* @param {Object}
|
243
|
-
* @
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
*
|
318
|
-
* @
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
if (rule
|
356
|
-
|
357
|
-
}
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
*
|
420
|
-
*
|
421
|
-
*
|
422
|
-
* @
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
*
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
messages.push({
|
464
|
-
fatal: true,
|
465
|
-
severity: 2,
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
*
|
480
|
-
*
|
481
|
-
* @param {
|
482
|
-
* @param {
|
483
|
-
* @
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
*
|
502
|
-
* @
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
*
|
511
|
-
* @
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
*
|
520
|
-
*
|
521
|
-
* @
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
*
|
536
|
-
* @
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
*
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
*
|
573
|
-
* @param {
|
574
|
-
* @param {
|
575
|
-
*
|
576
|
-
*
|
577
|
-
*
|
578
|
-
*
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
ecmaFeatures.
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
*
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
//
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
*
|
688
|
-
*
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
currentTokens
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
*
|
715
|
-
*
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
node.
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
*
|
756
|
-
* @param {
|
757
|
-
* @param {
|
758
|
-
* @param {
|
759
|
-
*
|
760
|
-
*
|
761
|
-
*
|
762
|
-
* @param {
|
763
|
-
*
|
764
|
-
*
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
*
|
797
|
-
* @param {
|
798
|
-
* @param {int=}
|
799
|
-
* @
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
*
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
*
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
*
|
830
|
-
* @
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
*
|
840
|
-
*
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
*
|
857
|
-
* @
|
858
|
-
*
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
*
|
868
|
-
* @
|
869
|
-
* @
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
*
|
892
|
-
* @
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
case "
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
*
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
*
|
937
|
-
* @
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
*
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
*
|
1000
|
-
* @
|
1001
|
-
*
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
*
|
1031
|
-
*
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
*
|
1044
|
-
* @param {
|
1045
|
-
* @
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
*
|
1054
|
-
* @
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
*
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
}());
|
1
|
+
/**
|
2
|
+
* @fileoverview Main ESLint object.
|
3
|
+
* @author Nicholas C. Zakas
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
var estraverse = require("estraverse-fb"),
|
12
|
+
escope = require("escope"),
|
13
|
+
environments = require("../conf/environments"),
|
14
|
+
assign = require("object-assign"),
|
15
|
+
rules = require("./rules"),
|
16
|
+
util = require("./util"),
|
17
|
+
RuleContext = require("./rule-context"),
|
18
|
+
timing = require("./timing"),
|
19
|
+
createTokenStore = require("./token-store.js"),
|
20
|
+
EventEmitter = require("events").EventEmitter,
|
21
|
+
escapeRegExp = require("escape-string-regexp"),
|
22
|
+
validator = require("./config-validator");
|
23
|
+
|
24
|
+
//------------------------------------------------------------------------------
|
25
|
+
// Helpers
|
26
|
+
//------------------------------------------------------------------------------
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Parses a list of "name:boolean_value" or/and "name" options divided by comma or
|
30
|
+
* whitespace.
|
31
|
+
* @param {string} string The string to parse.
|
32
|
+
* @returns {Object} Result map object of names and boolean values
|
33
|
+
*/
|
34
|
+
function parseBooleanConfig(string) {
|
35
|
+
var items = {};
|
36
|
+
// Collapse whitespace around : to make parsing easier
|
37
|
+
string = string.replace(/\s*:\s*/g, ":");
|
38
|
+
// Collapse whitespace around ,
|
39
|
+
string = string.replace(/\s*,\s*/g, ",");
|
40
|
+
string.split(/\s|,+/).forEach(function(name) {
|
41
|
+
if (!name) {
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
var pos = name.indexOf(":"),
|
45
|
+
value;
|
46
|
+
if (pos !== -1) {
|
47
|
+
value = name.substring(pos + 1, name.length);
|
48
|
+
name = name.substring(0, pos);
|
49
|
+
}
|
50
|
+
|
51
|
+
items[name] = (value === "true");
|
52
|
+
|
53
|
+
});
|
54
|
+
return items;
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Parses a JSON-like config.
|
59
|
+
* @param {string} string The string to parse.
|
60
|
+
* @param {Object} location Start line and column of comments for potential error message.
|
61
|
+
* @param {Object[]} messages The messages queue for potential error message.
|
62
|
+
* @returns {Object} Result map object
|
63
|
+
*/
|
64
|
+
function parseJsonConfig(string, location, messages) {
|
65
|
+
var items = {};
|
66
|
+
string = string.replace(/([a-zA-Z0-9\-\/]+):/g, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/, "$1,");
|
67
|
+
try {
|
68
|
+
items = JSON.parse("{" + string + "}");
|
69
|
+
} catch(ex) {
|
70
|
+
|
71
|
+
messages.push({
|
72
|
+
fatal: true,
|
73
|
+
severity: 2,
|
74
|
+
message: "Failed to parse JSON from '" + string + "': " + ex.message,
|
75
|
+
line: location.start.line,
|
76
|
+
column: location.start.column
|
77
|
+
});
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
return items;
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Parses a config of values separated by comma.
|
86
|
+
* @param {string} string The string to parse.
|
87
|
+
* @returns {Object} Result map of values and true values
|
88
|
+
*/
|
89
|
+
function parseListConfig(string) {
|
90
|
+
var items = {};
|
91
|
+
// Collapse whitespace around ,
|
92
|
+
string = string.replace(/\s*,\s*/g, ",");
|
93
|
+
string.split(/,+/).forEach(function(name) {
|
94
|
+
name = name.trim();
|
95
|
+
if (!name) {
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
items[name] = true;
|
99
|
+
});
|
100
|
+
return items;
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* @param {Scope} scope The scope object to check.
|
105
|
+
* @param {string} name The name of the variable to look up.
|
106
|
+
* @returns {Variable} The variable object if found or null if not.
|
107
|
+
*/
|
108
|
+
function getVariable(scope, name) {
|
109
|
+
var variable = null;
|
110
|
+
scope.variables.some(function(v) {
|
111
|
+
if (v.name === name) {
|
112
|
+
variable = v;
|
113
|
+
return true;
|
114
|
+
} else {
|
115
|
+
return false;
|
116
|
+
}
|
117
|
+
|
118
|
+
});
|
119
|
+
return variable;
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Ensures that variables representing built-in properties of the Global Object,
|
124
|
+
* and any globals declared by special block comments, are present in the global
|
125
|
+
* scope.
|
126
|
+
* @param {ASTNode} program The top node of the AST.
|
127
|
+
* @param {Scope} globalScope The global scope.
|
128
|
+
* @param {Object} config The existing configuration data.
|
129
|
+
* @returns {void}
|
130
|
+
*/
|
131
|
+
function addDeclaredGlobals(program, globalScope, config) {
|
132
|
+
var declaredGlobals = {},
|
133
|
+
explicitGlobals = {},
|
134
|
+
builtin = environments.builtin;
|
135
|
+
|
136
|
+
assign(declaredGlobals, builtin);
|
137
|
+
|
138
|
+
Object.keys(config.env).forEach(function (name) {
|
139
|
+
if (config.env[name]) {
|
140
|
+
var environmentGlobals = environments[name] && environments[name].globals;
|
141
|
+
if (environmentGlobals) {
|
142
|
+
assign(declaredGlobals, environmentGlobals);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
});
|
146
|
+
|
147
|
+
assign(declaredGlobals, config.globals);
|
148
|
+
assign(explicitGlobals, config.astGlobals);
|
149
|
+
|
150
|
+
Object.keys(declaredGlobals).forEach(function(name) {
|
151
|
+
var variable = getVariable(globalScope, name);
|
152
|
+
if (!variable) {
|
153
|
+
variable = new escope.Variable(name, globalScope);
|
154
|
+
variable.eslintExplicitGlobal = false;
|
155
|
+
globalScope.variables.push(variable);
|
156
|
+
}
|
157
|
+
variable.writeable = declaredGlobals[name];
|
158
|
+
});
|
159
|
+
|
160
|
+
Object.keys(explicitGlobals).forEach(function(name) {
|
161
|
+
var variable = getVariable(globalScope, name);
|
162
|
+
if (!variable) {
|
163
|
+
variable = new escope.Variable(name, globalScope);
|
164
|
+
variable.eslintExplicitGlobal = true;
|
165
|
+
globalScope.variables.push(variable);
|
166
|
+
}
|
167
|
+
variable.writeable = explicitGlobals[name];
|
168
|
+
});
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
* Add data to reporting configuration to disable reporting for list of rules
|
173
|
+
* starting from start location
|
174
|
+
* @param {Object[]} reportingConfig Current reporting configuration
|
175
|
+
* @param {Object} start Position to start
|
176
|
+
* @param {string[]} rulesToDisable List of rules
|
177
|
+
* @returns {void}
|
178
|
+
*/
|
179
|
+
function disableReporting(reportingConfig, start, rulesToDisable) {
|
180
|
+
|
181
|
+
if (rulesToDisable.length) {
|
182
|
+
rulesToDisable.forEach(function(rule) {
|
183
|
+
reportingConfig.push({
|
184
|
+
start: start,
|
185
|
+
end: null,
|
186
|
+
rule: rule
|
187
|
+
});
|
188
|
+
});
|
189
|
+
} else {
|
190
|
+
reportingConfig.push({
|
191
|
+
start: start,
|
192
|
+
end: null,
|
193
|
+
rule: null
|
194
|
+
});
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Add data to reporting configuration to enable reporting for list of rules
|
200
|
+
* starting from start location
|
201
|
+
* @param {Object[]} reportingConfig Current reporting configuration
|
202
|
+
* @param {Object} start Position to start
|
203
|
+
* @param {string[]} rulesToEnable List of rules
|
204
|
+
* @returns {void}
|
205
|
+
*/
|
206
|
+
function enableReporting(reportingConfig, start, rulesToEnable) {
|
207
|
+
var i;
|
208
|
+
|
209
|
+
if (rulesToEnable.length) {
|
210
|
+
rulesToEnable.forEach(function(rule) {
|
211
|
+
for (i = reportingConfig.length - 1; i >= 0; i--) {
|
212
|
+
if (!reportingConfig[i].end && reportingConfig[i].rule === rule ) {
|
213
|
+
reportingConfig[i].end = start;
|
214
|
+
break;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
});
|
218
|
+
} else {
|
219
|
+
// find all previous disabled locations if they was started as list of rules
|
220
|
+
var prevStart;
|
221
|
+
for (i = reportingConfig.length - 1; i >= 0; i--) {
|
222
|
+
if (prevStart && prevStart !== reportingConfig[i].start) {
|
223
|
+
break;
|
224
|
+
}
|
225
|
+
|
226
|
+
if (!reportingConfig[i].end) {
|
227
|
+
reportingConfig[i].end = start;
|
228
|
+
prevStart = reportingConfig[i].start;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Parses comments in file to extract file-specific config of rules, globals
|
236
|
+
* and environments and merges them with global config; also code blocks
|
237
|
+
* where reporting is disabled or enabled and merges them with reporting config.
|
238
|
+
* @param {string} filename The file being checked.
|
239
|
+
* @param {ASTNode} ast The top node of the AST.
|
240
|
+
* @param {Object} config The existing configuration data.
|
241
|
+
* @param {Object[]} reportingConfig The existing reporting configuration data.
|
242
|
+
* @param {Object[]} messages The messages queue.
|
243
|
+
* @returns {object} Modified config object
|
244
|
+
*/
|
245
|
+
function modifyConfigsFromComments(filename, ast, config, reportingConfig, messages) {
|
246
|
+
|
247
|
+
var commentConfig = {
|
248
|
+
astGlobals: {},
|
249
|
+
rules: {},
|
250
|
+
env: {}
|
251
|
+
};
|
252
|
+
var commentRules = {};
|
253
|
+
|
254
|
+
ast.comments.forEach(function(comment) {
|
255
|
+
|
256
|
+
var value = comment.value.trim();
|
257
|
+
var match = /^(eslint-\w+|eslint-\w+-\w+|eslint|globals?)(\s|$)/.exec(value);
|
258
|
+
|
259
|
+
if (match) {
|
260
|
+
value = value.substring(match.index + match[1].length);
|
261
|
+
|
262
|
+
if (comment.type === "Block") {
|
263
|
+
switch (match[1]) {
|
264
|
+
case "globals":
|
265
|
+
case "global":
|
266
|
+
assign(commentConfig.astGlobals, parseBooleanConfig(value));
|
267
|
+
break;
|
268
|
+
|
269
|
+
case "eslint-env":
|
270
|
+
assign(commentConfig.env, parseListConfig(value));
|
271
|
+
break;
|
272
|
+
|
273
|
+
case "eslint-disable":
|
274
|
+
disableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
|
275
|
+
break;
|
276
|
+
|
277
|
+
case "eslint-enable":
|
278
|
+
enableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
|
279
|
+
break;
|
280
|
+
|
281
|
+
case "eslint":
|
282
|
+
var items = parseJsonConfig(value, comment.loc, messages);
|
283
|
+
Object.keys(items).forEach(function(name) {
|
284
|
+
var ruleValue = items[name];
|
285
|
+
validator.validateRuleOptions(name, ruleValue, filename + " line " + comment.loc.start.line);
|
286
|
+
commentRules[name] = ruleValue;
|
287
|
+
});
|
288
|
+
break;
|
289
|
+
|
290
|
+
// no default
|
291
|
+
}
|
292
|
+
} else {
|
293
|
+
// comment.type === "Line"
|
294
|
+
if (match[1] === "eslint-disable-line") {
|
295
|
+
disableReporting(reportingConfig, { "line": comment.loc.start.line, "column": 0 }, Object.keys(parseListConfig(value)));
|
296
|
+
enableReporting(reportingConfig, comment.loc.end, Object.keys(parseListConfig(value)));
|
297
|
+
}
|
298
|
+
}
|
299
|
+
}
|
300
|
+
});
|
301
|
+
|
302
|
+
// apply environment configs
|
303
|
+
Object.keys(commentConfig.env).forEach(function (name) {
|
304
|
+
if (environments[name]) {
|
305
|
+
commentConfig = util.mergeConfigs(commentConfig, environments[name]);
|
306
|
+
}
|
307
|
+
});
|
308
|
+
assign(commentConfig.rules, commentRules);
|
309
|
+
|
310
|
+
return util.mergeConfigs(config, commentConfig);
|
311
|
+
}
|
312
|
+
|
313
|
+
/**
|
314
|
+
* Check if message of rule with ruleId should be ignored in location
|
315
|
+
* @param {Object[]} reportingConfig Collection of ignore records
|
316
|
+
* @param {string} ruleId Id of rule
|
317
|
+
* @param {Object} location Location of message
|
318
|
+
* @returns {boolean} True if message should be ignored, false otherwise
|
319
|
+
*/
|
320
|
+
function isDisabledByReportingConfig(reportingConfig, ruleId, location) {
|
321
|
+
|
322
|
+
for (var i = 0, c = reportingConfig.length; i < c; i++) {
|
323
|
+
|
324
|
+
var ignore = reportingConfig[i];
|
325
|
+
if ((!ignore.rule || ignore.rule === ruleId) &&
|
326
|
+
(location.line > ignore.start.line || (location.line === ignore.start.line && location.column >= ignore.start.column)) &&
|
327
|
+
(!ignore.end || (location.line < ignore.end.line || (location.line === ignore.end.line && location.column <= ignore.end.column)))) {
|
328
|
+
return true;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
|
332
|
+
return false;
|
333
|
+
}
|
334
|
+
|
335
|
+
/**
|
336
|
+
* Process initial config to make it safe to extend by file comment config
|
337
|
+
* @param {Object} config Initial config
|
338
|
+
* @returns {Object} Processed config
|
339
|
+
*/
|
340
|
+
function prepareConfig(config) {
|
341
|
+
|
342
|
+
config.globals = config.globals || config.global || {};
|
343
|
+
delete config.global;
|
344
|
+
|
345
|
+
var copiedRules = {},
|
346
|
+
ecmaFeatures = {},
|
347
|
+
preparedConfig;
|
348
|
+
|
349
|
+
if (typeof config.rules === "object") {
|
350
|
+
Object.keys(config.rules).forEach(function(k) {
|
351
|
+
var rule = config.rules[k];
|
352
|
+
if (rule === null) {
|
353
|
+
throw new Error("Invalid config for rule '" + k + "'\.");
|
354
|
+
}
|
355
|
+
if (Array.isArray(rule)) {
|
356
|
+
copiedRules[k] = rule.slice();
|
357
|
+
} else {
|
358
|
+
copiedRules[k] = rule;
|
359
|
+
}
|
360
|
+
});
|
361
|
+
}
|
362
|
+
|
363
|
+
// merge in environment ecmaFeatures
|
364
|
+
if (typeof config.env === "object") {
|
365
|
+
Object.keys(config.env).forEach(function(env) {
|
366
|
+
if (config.env[env] && environments[env].ecmaFeatures) {
|
367
|
+
assign(ecmaFeatures, environments[env].ecmaFeatures);
|
368
|
+
}
|
369
|
+
});
|
370
|
+
}
|
371
|
+
|
372
|
+
preparedConfig = {
|
373
|
+
rules: copiedRules,
|
374
|
+
parser: config.parser || "espree",
|
375
|
+
globals: util.mergeConfigs({}, config.globals),
|
376
|
+
env: util.mergeConfigs({}, config.env || {}),
|
377
|
+
settings: util.mergeConfigs({}, config.settings || {}),
|
378
|
+
ecmaFeatures: util.mergeConfigs(ecmaFeatures, config.ecmaFeatures || {})
|
379
|
+
};
|
380
|
+
|
381
|
+
// can't have global return inside of modules
|
382
|
+
if (preparedConfig.ecmaFeatures.modules) {
|
383
|
+
preparedConfig.ecmaFeatures.globalReturn = false;
|
384
|
+
}
|
385
|
+
|
386
|
+
return preparedConfig;
|
387
|
+
}
|
388
|
+
|
389
|
+
//------------------------------------------------------------------------------
|
390
|
+
// Public Interface
|
391
|
+
//------------------------------------------------------------------------------
|
392
|
+
|
393
|
+
/**
|
394
|
+
* Object that is responsible for verifying JavaScript text
|
395
|
+
* @name eslint
|
396
|
+
*/
|
397
|
+
module.exports = (function() {
|
398
|
+
|
399
|
+
var api = Object.create(new EventEmitter()),
|
400
|
+
messages = [],
|
401
|
+
currentText = null,
|
402
|
+
currentTextLines = [],
|
403
|
+
currentConfig = null,
|
404
|
+
currentTokens = null,
|
405
|
+
currentScopes = null,
|
406
|
+
scopeMap = null,
|
407
|
+
scopeManager = null,
|
408
|
+
currentFilename = null,
|
409
|
+
controller = null,
|
410
|
+
reportingConfig = [],
|
411
|
+
commentLocsEnter = [],
|
412
|
+
commentLocsExit = [],
|
413
|
+
currentAST = null;
|
414
|
+
|
415
|
+
/**
|
416
|
+
* Parses text into an AST. Moved out here because the try-catch prevents
|
417
|
+
* optimization of functions, so it's best to keep the try-catch as isolated
|
418
|
+
* as possible
|
419
|
+
* @param {string} text The text to parse.
|
420
|
+
* @param {Object} config The ESLint configuration object.
|
421
|
+
* @returns {ASTNode} The AST if successful or null if not.
|
422
|
+
* @private
|
423
|
+
*/
|
424
|
+
function parse(text, config) {
|
425
|
+
|
426
|
+
var parser;
|
427
|
+
|
428
|
+
try {
|
429
|
+
parser = require(config.parser);
|
430
|
+
} catch (ex) {
|
431
|
+
messages.push({
|
432
|
+
fatal: true,
|
433
|
+
severity: 2,
|
434
|
+
message: ex.message,
|
435
|
+
line: 0,
|
436
|
+
column: 0
|
437
|
+
});
|
438
|
+
|
439
|
+
return null;
|
440
|
+
}
|
441
|
+
|
442
|
+
/*
|
443
|
+
* Check for parsing errors first. If there's a parsing error, nothing
|
444
|
+
* else can happen. However, a parsing error does not throw an error
|
445
|
+
* from this method - it's just considered a fatal error message, a
|
446
|
+
* problem that ESLint identified just like any other.
|
447
|
+
*/
|
448
|
+
try {
|
449
|
+
return parser.parse(text, {
|
450
|
+
loc: true,
|
451
|
+
range: true,
|
452
|
+
raw: true,
|
453
|
+
tokens: true,
|
454
|
+
comment: true,
|
455
|
+
attachComment: true,
|
456
|
+
ecmaFeatures: config.ecmaFeatures
|
457
|
+
});
|
458
|
+
} catch (ex) {
|
459
|
+
|
460
|
+
// If the message includes a leading line number, strip it:
|
461
|
+
var message = ex.message.replace(/^line \d+:/i, "").trim();
|
462
|
+
|
463
|
+
messages.push({
|
464
|
+
fatal: true,
|
465
|
+
severity: 2,
|
466
|
+
|
467
|
+
message: message,
|
468
|
+
|
469
|
+
line: ex.lineNumber,
|
470
|
+
column: ex.column
|
471
|
+
});
|
472
|
+
|
473
|
+
return null;
|
474
|
+
}
|
475
|
+
}
|
476
|
+
|
477
|
+
/**
|
478
|
+
* Check collection of comments to prevent double event for comment as
|
479
|
+
* leading and trailing, then emit event if passing
|
480
|
+
* @param {ASTNode[]} comments Collection of comment nodes
|
481
|
+
* @param {Object[]} locs List of locations of previous comment nodes
|
482
|
+
* @param {string} eventName Event name postfix
|
483
|
+
* @returns {void}
|
484
|
+
*/
|
485
|
+
function emitComments(comments, locs, eventName) {
|
486
|
+
|
487
|
+
if (comments.length) {
|
488
|
+
comments.forEach(function(node) {
|
489
|
+
if (locs.indexOf(node.loc) >= 0) {
|
490
|
+
locs.splice(locs.indexOf(node.loc), 1);
|
491
|
+
} else {
|
492
|
+
locs.push(node.loc);
|
493
|
+
api.emit(node.type + eventName, node);
|
494
|
+
}
|
495
|
+
});
|
496
|
+
}
|
497
|
+
}
|
498
|
+
|
499
|
+
/**
|
500
|
+
* Shortcut to check and emit enter of comment nodes
|
501
|
+
* @param {ASTNode[]} comments Collection of comment nodes
|
502
|
+
* @returns {void}
|
503
|
+
*/
|
504
|
+
function emitCommentsEnter(comments) {
|
505
|
+
emitComments(comments, commentLocsEnter, "Comment");
|
506
|
+
}
|
507
|
+
|
508
|
+
/**
|
509
|
+
* Shortcut to check and emit exit of comment nodes
|
510
|
+
* @param {ASTNode[]} comments Collection of comment nodes
|
511
|
+
* @returns {void}
|
512
|
+
*/
|
513
|
+
function emitCommentsExit(comments) {
|
514
|
+
emitComments(comments, commentLocsExit, "Comment:exit");
|
515
|
+
}
|
516
|
+
|
517
|
+
/**
|
518
|
+
* Get the severity level of a rule (0 - none, 1 - warning, 2 - error)
|
519
|
+
* Returns 0 if the rule config is not valid (an Array or a number)
|
520
|
+
* @param {Array|number} ruleConfig rule configuration
|
521
|
+
* @returns {number} 0, 1, or 2, indicating rule severity
|
522
|
+
*/
|
523
|
+
function getRuleSeverity(ruleConfig) {
|
524
|
+
if (typeof ruleConfig === "number") {
|
525
|
+
return ruleConfig;
|
526
|
+
} else if (Array.isArray(ruleConfig)) {
|
527
|
+
return ruleConfig[0];
|
528
|
+
} else {
|
529
|
+
return 0;
|
530
|
+
}
|
531
|
+
}
|
532
|
+
|
533
|
+
/**
|
534
|
+
* Get the options for a rule (not including severity), if any
|
535
|
+
* @param {Array|number} ruleConfig rule configuration
|
536
|
+
* @returns {Array} of rule options, empty Array if none
|
537
|
+
*/
|
538
|
+
function getRuleOptions(ruleConfig) {
|
539
|
+
if (Array.isArray(ruleConfig)) {
|
540
|
+
return ruleConfig.slice(1);
|
541
|
+
} else {
|
542
|
+
return [];
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
// set unlimited listeners (see https://github.com/eslint/eslint/issues/524)
|
547
|
+
api.setMaxListeners(0);
|
548
|
+
|
549
|
+
/**
|
550
|
+
* Resets the internal state of the object.
|
551
|
+
* @returns {void}
|
552
|
+
*/
|
553
|
+
api.reset = function() {
|
554
|
+
this.removeAllListeners();
|
555
|
+
messages = [];
|
556
|
+
currentAST = null;
|
557
|
+
currentConfig = null;
|
558
|
+
currentText = null;
|
559
|
+
currentTextLines = [];
|
560
|
+
currentTokens = null;
|
561
|
+
currentScopes = null;
|
562
|
+
scopeMap = null;
|
563
|
+
scopeManager = null;
|
564
|
+
controller = null;
|
565
|
+
reportingConfig = [];
|
566
|
+
commentLocsEnter = [];
|
567
|
+
commentLocsExit = [];
|
568
|
+
};
|
569
|
+
|
570
|
+
/**
|
571
|
+
* Verifies the text against the rules specified by the second argument.
|
572
|
+
* @param {string} text The JavaScript text to verify.
|
573
|
+
* @param {Object} config An object whose keys specify the rules to use.
|
574
|
+
* @param {string=} filename The optional filename of the file being checked.
|
575
|
+
* If this is not set, the filename will default to '<input>' in the rule context.
|
576
|
+
* @param {boolean=} saveState Indicates if the state from the last run should be saved.
|
577
|
+
* Mostly useful for testing purposes.
|
578
|
+
* @returns {Object[]} The results as an array of messages or null if no messages.
|
579
|
+
*/
|
580
|
+
api.verify = function(text, config, filename, saveState) {
|
581
|
+
|
582
|
+
var ast,
|
583
|
+
shebang,
|
584
|
+
ecmaFeatures,
|
585
|
+
ecmaVersion;
|
586
|
+
|
587
|
+
// set the current parsed filename
|
588
|
+
currentFilename = filename;
|
589
|
+
|
590
|
+
if (!saveState) {
|
591
|
+
this.reset();
|
592
|
+
}
|
593
|
+
|
594
|
+
// there's no input, just exit here
|
595
|
+
if (text.trim().length === 0) {
|
596
|
+
currentText = text;
|
597
|
+
return messages;
|
598
|
+
}
|
599
|
+
|
600
|
+
// process initial config to make it safe to extend
|
601
|
+
config = prepareConfig(config || {});
|
602
|
+
|
603
|
+
ast = parse(text.replace(/^#!([^\r\n]+)/, function(match, captured) {
|
604
|
+
shebang = captured;
|
605
|
+
return "//" + captured;
|
606
|
+
}), config);
|
607
|
+
|
608
|
+
// if espree failed to parse the file, there's no sense in setting up rules
|
609
|
+
if (ast) {
|
610
|
+
|
611
|
+
currentAST = ast;
|
612
|
+
|
613
|
+
// parse global comments and modify config
|
614
|
+
config = modifyConfigsFromComments(filename, ast, config, reportingConfig, messages);
|
615
|
+
|
616
|
+
// enable appropriate rules
|
617
|
+
Object.keys(config.rules).filter(function(key) {
|
618
|
+
return getRuleSeverity(config.rules[key]) > 0;
|
619
|
+
}).forEach(function(key) {
|
620
|
+
|
621
|
+
var ruleCreator = rules.get(key),
|
622
|
+
severity = getRuleSeverity(config.rules[key]),
|
623
|
+
options = getRuleOptions(config.rules[key]),
|
624
|
+
rule;
|
625
|
+
|
626
|
+
if (ruleCreator) {
|
627
|
+
try {
|
628
|
+
rule = ruleCreator(new RuleContext(
|
629
|
+
key, api, severity, options,
|
630
|
+
config.settings, config.ecmaFeatures
|
631
|
+
));
|
632
|
+
|
633
|
+
// add all the node types as listeners
|
634
|
+
Object.keys(rule).forEach(function(nodeType) {
|
635
|
+
api.on(nodeType, timing.enabled
|
636
|
+
? timing.time(key, rule[nodeType])
|
637
|
+
: rule[nodeType]
|
638
|
+
);
|
639
|
+
});
|
640
|
+
} catch(ex) {
|
641
|
+
ex.message = "Error while loading rule '" + key + "': " + ex.message;
|
642
|
+
throw ex;
|
643
|
+
}
|
644
|
+
|
645
|
+
} else {
|
646
|
+
throw new Error("Definition for rule '" + key + "' was not found.");
|
647
|
+
}
|
648
|
+
});
|
649
|
+
|
650
|
+
// save config so rules can access as necessary
|
651
|
+
currentConfig = config;
|
652
|
+
currentText = text;
|
653
|
+
controller = new estraverse.Controller();
|
654
|
+
|
655
|
+
ecmaFeatures = currentConfig.ecmaFeatures;
|
656
|
+
ecmaVersion = (ecmaFeatures.blockBindings || ecmaFeatures.classes ||
|
657
|
+
ecmaFeatures.modules || ecmaFeatures.defaultParams ||
|
658
|
+
ecmaFeatures.destructuring) ? 6 : 5;
|
659
|
+
|
660
|
+
|
661
|
+
// gather data that may be needed by the rules
|
662
|
+
scopeManager = escope.analyze(ast, {
|
663
|
+
ignoreEval: true,
|
664
|
+
nodejsScope: ecmaFeatures.globalReturn,
|
665
|
+
ecmaVersion: ecmaVersion,
|
666
|
+
sourceType: ecmaFeatures.modules ? "module" : "script"
|
667
|
+
});
|
668
|
+
currentScopes = scopeManager.scopes;
|
669
|
+
|
670
|
+
/*
|
671
|
+
* Index the scopes by the start range of their block for efficient
|
672
|
+
* lookup in getScope.
|
673
|
+
*/
|
674
|
+
scopeMap = [];
|
675
|
+
currentScopes.forEach(function (scope, index) {
|
676
|
+
var range = scope.block.range[0];
|
677
|
+
|
678
|
+
// Sometimes two scopes are returned for a given node. This is
|
679
|
+
// handled later in a known way, so just don't overwrite here.
|
680
|
+
if (!scopeMap[range]) {
|
681
|
+
scopeMap[range] = index;
|
682
|
+
}
|
683
|
+
});
|
684
|
+
|
685
|
+
/*
|
686
|
+
* Split text here into array of lines so
|
687
|
+
* it's not being done repeatedly
|
688
|
+
* by individual rules.
|
689
|
+
*/
|
690
|
+
currentTextLines = currentText.split(/\r\n|\r|\n|\u2028|\u2029/g);
|
691
|
+
|
692
|
+
// Freezing so array isn't accidentally changed by a rule.
|
693
|
+
Object.freeze(currentTextLines);
|
694
|
+
|
695
|
+
currentTokens = createTokenStore(ast.tokens);
|
696
|
+
Object.keys(currentTokens).forEach(function(method) {
|
697
|
+
api[method] = currentTokens[method];
|
698
|
+
});
|
699
|
+
|
700
|
+
// augment global scope with declared global variables
|
701
|
+
addDeclaredGlobals(ast, currentScopes[0], currentConfig);
|
702
|
+
|
703
|
+
// remove shebang comments
|
704
|
+
if (shebang && ast.comments.length && ast.comments[0].value === shebang) {
|
705
|
+
ast.comments.splice(0, 1);
|
706
|
+
|
707
|
+
if (ast.body.length && ast.body[0].leadingComments && ast.body[0].leadingComments[0].value === shebang) {
|
708
|
+
ast.body[0].leadingComments.splice(0, 1);
|
709
|
+
}
|
710
|
+
}
|
711
|
+
|
712
|
+
/*
|
713
|
+
* Each node has a type property. Whenever a particular type of node is found,
|
714
|
+
* an event is fired. This allows any listeners to automatically be informed
|
715
|
+
* that this type of node has been found and react accordingly.
|
716
|
+
*/
|
717
|
+
controller.traverse(ast, {
|
718
|
+
enter: function(node, parent) {
|
719
|
+
|
720
|
+
var comments = api.getComments(node);
|
721
|
+
|
722
|
+
emitCommentsEnter(comments.leading);
|
723
|
+
node.parent = parent;
|
724
|
+
api.emit(node.type, node);
|
725
|
+
emitCommentsEnter(comments.trailing);
|
726
|
+
},
|
727
|
+
leave: function(node) {
|
728
|
+
|
729
|
+
var comments = api.getComments(node);
|
730
|
+
|
731
|
+
emitCommentsExit(comments.trailing);
|
732
|
+
api.emit(node.type + ":exit", node);
|
733
|
+
emitCommentsExit(comments.leading);
|
734
|
+
}
|
735
|
+
});
|
736
|
+
|
737
|
+
}
|
738
|
+
|
739
|
+
// sort by line and column
|
740
|
+
messages.sort(function(a, b) {
|
741
|
+
var lineDiff = a.line - b.line;
|
742
|
+
|
743
|
+
if (lineDiff === 0) {
|
744
|
+
return a.column - b.column;
|
745
|
+
} else {
|
746
|
+
return lineDiff;
|
747
|
+
}
|
748
|
+
});
|
749
|
+
|
750
|
+
return messages;
|
751
|
+
};
|
752
|
+
|
753
|
+
/**
|
754
|
+
* Reports a message from one of the rules.
|
755
|
+
* @param {string} ruleId The ID of the rule causing the message.
|
756
|
+
* @param {number} severity The severity level of the rule as configured.
|
757
|
+
* @param {ASTNode} node The AST node that the message relates to.
|
758
|
+
* @param {Object=} location An object containing the error line and column
|
759
|
+
* numbers. If location is not provided the node's start location will
|
760
|
+
* be used.
|
761
|
+
* @param {string} message The actual message.
|
762
|
+
* @param {Object} opts Optional template data which produces a formatted message
|
763
|
+
* with symbols being replaced by this object's values.
|
764
|
+
* @returns {void}
|
765
|
+
*/
|
766
|
+
api.report = function(ruleId, severity, node, location, message, opts) {
|
767
|
+
|
768
|
+
if (typeof location === "string") {
|
769
|
+
opts = message;
|
770
|
+
message = location;
|
771
|
+
location = node.loc.start;
|
772
|
+
}
|
773
|
+
|
774
|
+
Object.keys(opts || {}).forEach(function (key) {
|
775
|
+
var rx = new RegExp(escapeRegExp("{{" + key + "}}"), "g");
|
776
|
+
message = message.replace(rx, opts[key]);
|
777
|
+
});
|
778
|
+
|
779
|
+
if (isDisabledByReportingConfig(reportingConfig, ruleId, location)) {
|
780
|
+
return;
|
781
|
+
}
|
782
|
+
|
783
|
+
messages.push({
|
784
|
+
ruleId: ruleId,
|
785
|
+
severity: severity,
|
786
|
+
message: message,
|
787
|
+
line: location.line,
|
788
|
+
column: location.column,
|
789
|
+
nodeType: node.type,
|
790
|
+
source: currentTextLines[location.line - 1] || ""
|
791
|
+
});
|
792
|
+
};
|
793
|
+
|
794
|
+
/**
|
795
|
+
* Gets the source code for the given node.
|
796
|
+
* @param {ASTNode=} node The AST node to get the text for.
|
797
|
+
* @param {int=} beforeCount The number of characters before the node to retrieve.
|
798
|
+
* @param {int=} afterCount The number of characters after the node to retrieve.
|
799
|
+
* @returns {string} The text representing the AST node.
|
800
|
+
*/
|
801
|
+
api.getSource = function(node, beforeCount, afterCount) {
|
802
|
+
if (node) {
|
803
|
+
return (currentText !== null) ? currentText.slice(Math.max(node.range[0] - (beforeCount || 0), 0),
|
804
|
+
node.range[1] + (afterCount || 0)) : null;
|
805
|
+
} else {
|
806
|
+
return currentText;
|
807
|
+
}
|
808
|
+
|
809
|
+
};
|
810
|
+
|
811
|
+
/**
|
812
|
+
* Gets the entire source text split into an array of lines.
|
813
|
+
* @returns {Array} The source text as an array of lines.
|
814
|
+
*/
|
815
|
+
api.getSourceLines = function() {
|
816
|
+
return currentTextLines;
|
817
|
+
};
|
818
|
+
|
819
|
+
/**
|
820
|
+
* Retrieves an array containing all comments in the source code.
|
821
|
+
* @returns {ASTNode[]} An array of comment nodes.
|
822
|
+
*/
|
823
|
+
api.getAllComments = function() {
|
824
|
+
return currentAST.comments;
|
825
|
+
};
|
826
|
+
|
827
|
+
/**
|
828
|
+
* Gets all comments for the given node.
|
829
|
+
* @param {ASTNode} node The AST node to get the comments for.
|
830
|
+
* @returns {Object} The list of comments indexed by their position.
|
831
|
+
*/
|
832
|
+
api.getComments = function(node) {
|
833
|
+
|
834
|
+
var leadingComments = node.leadingComments || [],
|
835
|
+
trailingComments = node.trailingComments || [];
|
836
|
+
|
837
|
+
/*
|
838
|
+
* espree adds a "comments" array on Program nodes rather than
|
839
|
+
* leadingComments/trailingComments. Comments are only left in the
|
840
|
+
* Program node comments array if there is no executable code.
|
841
|
+
*/
|
842
|
+
if (node.type === "Program") {
|
843
|
+
if (node.body.length === 0) {
|
844
|
+
leadingComments = node.comments;
|
845
|
+
}
|
846
|
+
}
|
847
|
+
|
848
|
+
return {
|
849
|
+
leading: leadingComments,
|
850
|
+
trailing: trailingComments
|
851
|
+
};
|
852
|
+
};
|
853
|
+
|
854
|
+
/**
|
855
|
+
* Retrieves the JSDoc comment for a given node.
|
856
|
+
* @param {ASTNode} node The AST node to get the comment for.
|
857
|
+
* @returns {ASTNode} The BlockComment node containing the JSDoc for the
|
858
|
+
* given node or null if not found.
|
859
|
+
*/
|
860
|
+
api.getJSDocComment = function(node) {
|
861
|
+
|
862
|
+
var parent = node.parent,
|
863
|
+
line = node.loc.start.line;
|
864
|
+
|
865
|
+
/**
|
866
|
+
* Finds a JSDoc comment node in an array of comment nodes.
|
867
|
+
* @param {ASTNode[]} comments The array of comment nodes to search.
|
868
|
+
* @returns {ASTNode} The node if found, null if not.
|
869
|
+
* @private
|
870
|
+
*/
|
871
|
+
function findJSDocComment(comments) {
|
872
|
+
|
873
|
+
if (comments) {
|
874
|
+
for (var i = comments.length - 1; i >= 0; i--) {
|
875
|
+
if (comments[i].type === "Block" && comments[i].value.charAt(0) === "*") {
|
876
|
+
|
877
|
+
if (line - comments[i].loc.end.line <= 1) {
|
878
|
+
return comments[i];
|
879
|
+
} else {
|
880
|
+
break;
|
881
|
+
}
|
882
|
+
}
|
883
|
+
}
|
884
|
+
}
|
885
|
+
|
886
|
+
return null;
|
887
|
+
}
|
888
|
+
|
889
|
+
/**
|
890
|
+
* Check to see if its a ES6 export declaration
|
891
|
+
* @param {ASTNode} astNode - any node
|
892
|
+
* @returns {boolean} whether the given node represents a export declaration
|
893
|
+
*/
|
894
|
+
function looksLikeExport(astNode) {
|
895
|
+
return astNode.type === "ExportDefaultDeclaration" || astNode.type === "ExportNamedDeclaration" ||
|
896
|
+
astNode.type === "ExportAllDeclaration" || astNode.type === "ExportSpecifier";
|
897
|
+
}
|
898
|
+
|
899
|
+
switch (node.type) {
|
900
|
+
case "FunctionDeclaration":
|
901
|
+
if (looksLikeExport(parent)) {
|
902
|
+
return findJSDocComment(parent.leadingComments);
|
903
|
+
} else {
|
904
|
+
return findJSDocComment(node.leadingComments);
|
905
|
+
}
|
906
|
+
break;
|
907
|
+
|
908
|
+
case "ArrowFunctionExpression":
|
909
|
+
case "FunctionExpression":
|
910
|
+
|
911
|
+
if (parent.type !== "CallExpression") {
|
912
|
+
while (parent && !parent.leadingComments && !/Function/.test(parent.type)) {
|
913
|
+
parent = parent.parent;
|
914
|
+
}
|
915
|
+
|
916
|
+
return parent && (parent.type !== "FunctionDeclaration") ? findJSDocComment(parent.leadingComments) : null;
|
917
|
+
}
|
918
|
+
|
919
|
+
// falls through
|
920
|
+
|
921
|
+
default:
|
922
|
+
return null;
|
923
|
+
}
|
924
|
+
};
|
925
|
+
|
926
|
+
/**
|
927
|
+
* Gets nodes that are ancestors of current node.
|
928
|
+
* @returns {ASTNode[]} Array of objects representing ancestors.
|
929
|
+
*/
|
930
|
+
api.getAncestors = function() {
|
931
|
+
return controller.parents();
|
932
|
+
};
|
933
|
+
|
934
|
+
/**
|
935
|
+
* Gets the deepest node containing a range index.
|
936
|
+
* @param {int} index Range index of the desired node.
|
937
|
+
* @returns {ASTNode} [description]
|
938
|
+
*/
|
939
|
+
api.getNodeByRangeIndex = function(index) {
|
940
|
+
var result = null;
|
941
|
+
|
942
|
+
estraverse.traverse(controller.root, {
|
943
|
+
enter: function (node) {
|
944
|
+
if (node.range[0] <= index && index < node.range[1]) {
|
945
|
+
result = node;
|
946
|
+
} else {
|
947
|
+
this.skip();
|
948
|
+
}
|
949
|
+
},
|
950
|
+
leave: function (node) {
|
951
|
+
if (node === result) {
|
952
|
+
this.break();
|
953
|
+
}
|
954
|
+
}
|
955
|
+
});
|
956
|
+
|
957
|
+
return result;
|
958
|
+
};
|
959
|
+
|
960
|
+
/**
|
961
|
+
* Gets the scope for the current node.
|
962
|
+
* @returns {Object} An object representing the current node's scope.
|
963
|
+
*/
|
964
|
+
api.getScope = function() {
|
965
|
+
var parents = controller.parents(),
|
966
|
+
scope = currentScopes[0];
|
967
|
+
|
968
|
+
// Don't do this for Program nodes - they have no parents
|
969
|
+
if (parents.length) {
|
970
|
+
|
971
|
+
// if current node is function declaration, add it to the list
|
972
|
+
var current = controller.current();
|
973
|
+
if (["FunctionDeclaration", "FunctionExpression",
|
974
|
+
"ArrowFunctionExpression", "SwitchStatement"].indexOf(current.type) >= 0) {
|
975
|
+
parents.push(current);
|
976
|
+
}
|
977
|
+
|
978
|
+
// Ascend the current node's parents
|
979
|
+
for (var i = parents.length - 1; i >= 0; --i) {
|
980
|
+
|
981
|
+
scope = scopeManager.acquire(parents[i]);
|
982
|
+
if (scope) {
|
983
|
+
if (scope.type === "function-expression-name") {
|
984
|
+
return scope.childScopes[0];
|
985
|
+
} else {
|
986
|
+
return scope;
|
987
|
+
}
|
988
|
+
}
|
989
|
+
|
990
|
+
}
|
991
|
+
|
992
|
+
}
|
993
|
+
|
994
|
+
return currentScopes[0];
|
995
|
+
};
|
996
|
+
|
997
|
+
/**
|
998
|
+
* Record that a particular variable has been used in code
|
999
|
+
* @param {string} name The name of the variable to mark as used
|
1000
|
+
* @returns {boolean} True if the variable was found and marked as used,
|
1001
|
+
* false if not.
|
1002
|
+
*/
|
1003
|
+
api.markVariableAsUsed = function(name) {
|
1004
|
+
var scope = this.getScope(),
|
1005
|
+
specialScope = currentConfig.ecmaFeatures.globalReturn || currentConfig.ecmaFeatures.modules,
|
1006
|
+
variables,
|
1007
|
+
i,
|
1008
|
+
len;
|
1009
|
+
|
1010
|
+
// Special Node.js scope means we need to start one level deeper
|
1011
|
+
if (scope.type === "global" && specialScope) {
|
1012
|
+
scope = scope.childScopes[0];
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
do {
|
1016
|
+
variables = scope.variables;
|
1017
|
+
for (i = 0, len = variables.length; i < len; i++) {
|
1018
|
+
if (variables[i].name === name) {
|
1019
|
+
variables[i].eslintUsed = true;
|
1020
|
+
return true;
|
1021
|
+
}
|
1022
|
+
}
|
1023
|
+
} while ( (scope = scope.upper) );
|
1024
|
+
|
1025
|
+
return false;
|
1026
|
+
};
|
1027
|
+
|
1028
|
+
/**
|
1029
|
+
* Gets the filename for the currently parsed source.
|
1030
|
+
* @returns {string} The filename associated with the source being parsed.
|
1031
|
+
* Defaults to "<input>" if no filename info is present.
|
1032
|
+
*/
|
1033
|
+
api.getFilename = function() {
|
1034
|
+
if (typeof currentFilename === "string") {
|
1035
|
+
return currentFilename;
|
1036
|
+
} else {
|
1037
|
+
return "<input>";
|
1038
|
+
}
|
1039
|
+
};
|
1040
|
+
|
1041
|
+
/**
|
1042
|
+
* Defines a new linting rule.
|
1043
|
+
* @param {string} ruleId A unique rule identifier
|
1044
|
+
* @param {Function} ruleModule Function from context to object mapping AST node types to event handlers
|
1045
|
+
* @returns {void}
|
1046
|
+
*/
|
1047
|
+
var defineRule = api.defineRule = function(ruleId, ruleModule) {
|
1048
|
+
rules.define(ruleId, ruleModule);
|
1049
|
+
};
|
1050
|
+
|
1051
|
+
/**
|
1052
|
+
* Defines many new linting rules.
|
1053
|
+
* @param {object} rulesToDefine map from unique rule identifier to rule
|
1054
|
+
* @returns {void}
|
1055
|
+
*/
|
1056
|
+
api.defineRules = function(rulesToDefine) {
|
1057
|
+
Object.getOwnPropertyNames(rulesToDefine).forEach(function(ruleId) {
|
1058
|
+
defineRule(ruleId, rulesToDefine[ruleId]);
|
1059
|
+
});
|
1060
|
+
};
|
1061
|
+
|
1062
|
+
/**
|
1063
|
+
* Gets the default eslint configuration.
|
1064
|
+
* @returns {Object} Object mapping rule IDs to their default configurations
|
1065
|
+
*/
|
1066
|
+
api.defaults = function() {
|
1067
|
+
return require("../conf/eslint.json");
|
1068
|
+
};
|
1069
|
+
|
1070
|
+
return api;
|
1071
|
+
|
1072
|
+
}());
|