]> git.proxmox.com Git - pve-eslint.git/blob - eslint/docs/developer-guide/working-with-rules.md
89d37c483a4f03b53b68eb6da2c378712c06267c
[pve-eslint.git] / eslint / docs / developer-guide / working-with-rules.md
1 # Working with Rules
2
3 **Note:** This page covers the most recent rule format for ESLint >= 3.0.0. There is also a [deprecated rule format](./working-with-rules-deprecated.md).
4
5 Each rule in ESLint has three files named with its identifier (for example, `no-extra-semi`).
6
7 * in the `lib/rules` directory: a source file (for example, `no-extra-semi.js`)
8 * in the `tests/lib/rules` directory: a test file (for example, `no-extra-semi.js`)
9 * in the `docs/rules` directory: a Markdown documentation file (for example, `no-extra-semi.md`)
10
11 **Important:** If you submit a **core** rule to the ESLint repository, you **must** follow some conventions explained below.
12
13 Here is the basic format of the source file for a rule:
14
15 ```js
16 /**
17 * @fileoverview Rule to disallow unnecessary semicolons
18 * @author Nicholas C. Zakas
19 */
20
21 "use strict";
22
23 //------------------------------------------------------------------------------
24 // Rule Definition
25 //------------------------------------------------------------------------------
26
27 module.exports = {
28 meta: {
29 type: "suggestion",
30
31 docs: {
32 description: "disallow unnecessary semicolons",
33 category: "Possible Errors",
34 recommended: true,
35 url: "https://eslint.org/docs/rules/no-extra-semi"
36 },
37 fixable: "code",
38 schema: [] // no options
39 },
40 create: function(context) {
41 return {
42 // callback functions
43 };
44 }
45 };
46 ```
47
48 ## Rule Basics
49
50 The source file for a rule exports an object with the following properties.
51
52 `meta` (object) contains metadata for the rule:
53
54 * `type` (string) indicates the type of rule, which is one of `"problem"`, `"suggestion"`, or `"layout"`:
55 * `"problem"` means the rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve.
56 * `"suggestion"` means the rule is identifying something that could be done in a better way but no errors will occur if the code isn't changed.
57 * `"layout"` means the rule cares primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes. These rules work on parts of the code that aren't specified in the AST.
58
59 * `docs` (object) is required for core rules of ESLint:
60
61 * `description` (string) provides the short description of the rule in the [rules index](../rules/)
62 * `category` (string) specifies the heading under which the rule is listed in the [rules index](../rules/)
63 * `recommended` (boolean) is whether the `"extends": "eslint:recommended"` property in a [configuration file](../user-guide/configuring.md#extending-configuration-files) enables the rule
64 * `url` (string) specifies the URL at which the full documentation can be accessed
65 * `suggestion` (boolean) specifies whether rules can return suggestions (defaults to false if omitted)
66
67 In a custom rule or plugin, you can omit `docs` or include any properties that you need in it.
68
69 * `fixable` (string) is either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../user-guide/command-line-interface.md#fix) automatically fixes problems reported by the rule
70
71 **Important:** the `fixable` property is mandatory for fixable rules. If this property isn't specified, ESLint will throw an error whenever the rule attempts to produce a fix. Omit the `fixable` property if the rule is not fixable.
72
73 * `schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../user-guide/configuring.md#configuring-rules)
74
75 * `deprecated` (boolean) indicates whether the rule has been deprecated. You may omit the `deprecated` property if the rule has not been deprecated.
76
77 * `replacedBy` (array) in the case of a deprecated rule, specifies replacement rule(s)
78
79 `create` (function) returns an object with methods that ESLint calls to "visit" nodes while traversing the abstract syntax tree (AST as defined by [ESTree](https://github.com/estree/estree)) of JavaScript code:
80
81 * if a key is a node type or a [selector](./selectors.md), ESLint calls that **visitor** function while going **down** the tree
82 * if a key is a node type or a [selector](./selectors.md) plus `:exit`, ESLint calls that **visitor** function while going **up** the tree
83 * if a key is an event name, ESLint calls that **handler** function for [code path analysis](./code-path-analysis.md)
84
85 A rule can use the current node and its surrounding tree to report or fix problems.
86
87 Here are methods for the [array-callback-return](../rules/array-callback-return.md) rule:
88
89 ```js
90 function checkLastSegment (node) {
91 // report problem for function if last code path segment is reachable
92 }
93
94 module.exports = {
95 meta: { ... },
96 create: function(context) {
97 // declare the state of the rule
98 return {
99 ReturnStatement: function(node) {
100 // at a ReturnStatement node while going down
101 },
102 // at a function expression node while going up:
103 "FunctionExpression:exit": checkLastSegment,
104 "ArrowFunctionExpression:exit": checkLastSegment,
105 onCodePathStart: function (codePath, node) {
106 // at the start of analyzing a code path
107 },
108 onCodePathEnd: function(codePath, node) {
109 // at the end of analyzing a code path
110 }
111 };
112 }
113 };
114 ```
115
116 ## The Context Object
117
118 The `context` object contains additional functionality that is helpful for rules to do their jobs. As the name implies, the `context` object contains information that is relevant to the context of the rule. The `context` object has the following properties:
119
120 * `parserOptions` - the parser options configured for this run (more details [here](../user-guide/configuring.md#specifying-parser-options)).
121 * `id` - the rule ID.
122 * `options` - an array of the [configured options](/docs/user-guide/configuring.md#configuring-rules) for this rule. This array does not include the rule severity. For more information, see [here](#contextoptions).
123 * `settings` - the [shared settings](/docs/user-guide/configuring.md#adding-shared-settings) from configuration.
124 * `parserPath` - the name of the `parser` from configuration.
125 * `parserServices` - an object containing parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
126
127 Additionally, the `context` object has the following methods:
128
129 * `getAncestors()` - returns an array of the ancestors of the currently-traversed node, starting at the root of the AST and continuing through the direct parent of the current node. This array does not include the currently-traversed node itself.
130 * `getCwd()` - returns the `cwd` passed to [Linter](./nodejs-api.md#Linter). It is a path to a directory that should be considered as the current working directory.
131 * `getDeclaredVariables(node)` - returns a list of [variables](./scope-manager-interface.md#variable-interface) declared by the given node. This information can be used to track references to variables.
132 * If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
133 * If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
134 * If the node is a `FunctionDeclaration` or `FunctionExpression`, the variable for the function name is returned, in addition to variables for the function parameters.
135 * If the node is an `ArrowFunctionExpression`, variables for the parameters are returned.
136 * If the node is a `ClassDeclaration` or a `ClassExpression`, the variable for the class name is returned.
137 * If the node is a `CatchClause`, the variable for the exception is returned.
138 * If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
139 * If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
140 * Otherwise, if the node does not declare any variables, an empty array is returned.
141 * `getFilename()` - returns the filename associated with the source.
142 * `getScope()` - returns the [scope](./scope-manager-interface.md#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
143 * `getSourceCode()` - returns a [`SourceCode`](#context-getsourcecode) object that you can use to work with the source that was passed to ESLint.
144 * `markVariableAsUsed(name)` - marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars.md) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.
145 * `report(descriptor)` - reports a problem in the code (see the [dedicated section](#context-report)).
146
147 **Note:** Earlier versions of ESLint supported additional methods on the `context` object. Those methods were removed in the new format and should not be relied upon.
148
149 ### context.getScope()
150
151 This method returns the scope which has the following types:
152
153 | AST Node Type | Scope Type |
154 |:--------------------------|:-----------|
155 | `Program` | `global` |
156 | `FunctionDeclaration` | `function` |
157 | `FunctionExpression` | `function` |
158 | `ArrowFunctionExpression` | `function` |
159 | `ClassDeclaration` | `class` |
160 | `ClassExpression` | `class` |
161 | `BlockStatement` ※1 | `block` |
162 | `SwitchStatement` ※1 | `switch` |
163 | `ForStatement` ※2 | `for` |
164 | `ForInStatement` ※2 | `for` |
165 | `ForOfStatement` ※2 | `for` |
166 | `WithStatement` | `with` |
167 | `CatchClause` | `catch` |
168 | others | ※3 |
169
170 **※1** Only if the configured parser provided the block-scope feature. The default parser provides the block-scope feature if `parserOptions.ecmaVersion` is not less than `6`.<br>
171 **※2** Only if the `for` statement defines the iteration variable as a block-scoped variable (E.g., `for (let i = 0;;) {}`).<br>
172 **※3** The scope of the closest ancestor node which has own scope. If the closest ancestor node has multiple scopes then it chooses the innermost scope (E.g., the `Program` node has a `global` scope and a `module` scope if `Program#sourceType` is `"module"`. The innermost scope is the `module` scope.).
173
174 The returned value is a [`Scope` object](scope-manager-interface.md) defined by the `eslint-scope` package. The `Variable` objects of global variables have some additional properties.
175
176 * `variable.writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only.
177 * `variable.eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file.
178 * `variable.eslintExplicitGlobalComments` (`Comment[] | undefined`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. This property is `undefined` if there are no `/* globals */` directive comments.
179 * `variable.eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments.
180
181 ### context.report()
182
183 The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:
184
185 * `message` - the problem message.
186 * `node` - (optional) the AST node related to the problem. If present and `loc` is not specified, then the starting location of the node is used as the location of the problem.
187 * `loc` - (optional) an object specifying the location of the problem. If both `loc` and `node` are specified, then the location is used from `loc` instead of `node`.
188 * `start` - An object of the start location.
189 * `line` - the 1-based line number at which the problem occurred.
190 * `column` - the 0-based column number at which the problem occurred.
191 * `end` - An object of the end location.
192 * `line` - the 1-based line number at which the problem occurred.
193 * `column` - the 0-based column number at which the problem occurred.
194 * `data` - (optional) [placeholder](#using-message-placeholders) data for `message`.
195 * `fix` - (optional) a function that applies a [fix](#applying-fixes) to resolve the problem.
196
197 Note that at least one of `node` or `loc` is required.
198
199 The simplest example is to use just `node` and `message`:
200
201 ```js
202 context.report({
203 node: node,
204 message: "Unexpected identifier"
205 });
206 ```
207
208 The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node.
209
210 ### Using message placeholders
211
212 You can also use placeholders in the message and provide `data`:
213
214 ```js
215 {% raw %}
216 context.report({
217 node: node,
218 message: "Unexpected identifier: {{ identifier }}",
219 data: {
220 identifier: node.name
221 }
222 });
223 {% endraw %}
224 ```
225
226 Note that leading and trailing whitespace is optional in message parameters.
227
228 The node contains all of the information necessary to figure out the line and column number of the offending text as well the source text representing the node.
229
230 ### `messageId`s
231
232 Instead of typing out messages in both the `context.report()` call and your tests, you can use `messageId`s instead.
233
234 This allows you to avoid retyping error messages. It also prevents errors reported in different sections of your rule from having out-of-date messages.
235
236 ```js
237 {% raw %}
238 // in your rule
239 module.exports = {
240 meta: {
241 messages: {
242 avoidName: "Avoid using variables named '{{ name }}'"
243 }
244 },
245 create(context) {
246 return {
247 Identifier(node) {
248 if (node.name === "foo") {
249 context.report({
250 node,
251 messageId: "avoidName",
252 data: {
253 name: "foo",
254 }
255 });
256 }
257 }
258 };
259 }
260 };
261
262 // in the file to lint:
263
264 var foo = 2;
265 // ^ error: Avoid using variables named 'foo'
266
267 // In your tests:
268 var rule = require("../../../lib/rules/my-rule");
269 var RuleTester = require("eslint").RuleTester;
270
271 var ruleTester = new RuleTester();
272 ruleTester.run("my-rule", rule, {
273 valid: ["bar", "baz"],
274 invalid: [
275 {
276 code: "foo",
277 errors: [
278 {
279 messageId: "avoidName"
280 }
281 ]
282 }
283 ]
284 });
285 {% endraw %}
286 ```
287
288 ### Applying Fixes
289
290 If you'd like ESLint to attempt to fix the problem you're reporting, you can do so by specifying the `fix` function when using `context.report()`. The `fix` function receives a single argument, a `fixer` object, that you can use to apply a fix. For example:
291
292 ```js
293 context.report({
294 node: node,
295 message: "Missing semicolon",
296 fix: function(fixer) {
297 return fixer.insertTextAfter(node, ";");
298 }
299 });
300 ```
301
302 Here, the `fix()` function is used to insert a semicolon after the node. Note that a fix is not immediately applied, and may not be applied at all if there are conflicts with other fixes. After applying fixes, ESLint will run all of the enabled rules again on the fixed code, potentially applying more fixes. This process will repeat up to 10 times, or until no more fixable problems are found. Afterwards, any remaining problems will be reported as usual.
303
304 **Important:** The `meta.fixable` property is mandatory for fixable rules. ESLint will throw an error if a rule that implements `fix` functions does not [export](#rule-basics) the `meta.fixable` property.
305
306 The `fixer` object has the following methods:
307
308 * `insertTextAfter(nodeOrToken, text)` - inserts text after the given node or token
309 * `insertTextAfterRange(range, text)` - inserts text after the given range
310 * `insertTextBefore(nodeOrToken, text)` - inserts text before the given node or token
311 * `insertTextBeforeRange(range, text)` - inserts text before the given range
312 * `remove(nodeOrToken)` - removes the given node or token
313 * `removeRange(range)` - removes text in the given range
314 * `replaceText(nodeOrToken, text)` - replaces the text in the given node or token
315 * `replaceTextRange(range, text)` - replaces the text in the given range
316
317 The above methods return a `fixing` object.
318 The `fix()` function can return the following values:
319
320 * A `fixing` object.
321 * An array which includes `fixing` objects.
322 * An iterable object which enumerates `fixing` objects. Especially, the `fix()` function can be a generator.
323
324 If you make a `fix()` function which returns multiple `fixing` objects, those `fixing` objects must not be overlapped.
325
326 Best practices for fixes:
327
328 1. Avoid any fixes that could change the runtime behavior of code and cause it to stop working.
329 1. Make fixes as small as possible. Fixes that are unnecessarily large could conflict with other fixes, and prevent them from being applied.
330 1. Only make one fix per message. This is enforced because you must return the result of the fixer operation from `fix()`.
331 1. Since all rules are run again after the initial round of fixes is applied, it's not necessary for a rule to check whether the code style of a fix will cause errors to be reported by another rule.
332 * For example, suppose a fixer would like to surround an object key with quotes, but it's not sure whether the user would prefer single or double quotes.
333
334 ```js
335 ({ foo : 1 })
336
337 // should get fixed to either
338
339 ({ 'foo': 1 })
340
341 // or
342
343 ({ "foo": 1 })
344 ```
345
346 * This fixer can just select a quote type arbitrarily. If it guesses wrong, the resulting code will be automatically reported and fixed by the [`quotes`](/docs/rules/quotes.md) rule.
347
348 ### Providing Suggestions
349
350 In some cases fixes aren't appropriate to be automatically applied, for example, if a fix potentially changes functionality or if there are multiple valid ways to fix a rule depending on the implementation intent (see the best practices for [applying fixes](#applying-fixes) listed above). In these cases, there is an alternative `suggest` option on `context.report()` that allows other tools, such as editors, to expose helpers for users to manually apply a suggestion.
351
352 In order to provide suggestions, use the `suggest` key in the report argument with an array of suggestion objects. The suggestion objects represent individual suggestions that could be applied and require either a `desc` key string that describes what applying the suggestion would do or a `messageId` key (see [below](#suggestion-messageids)), and a `fix` key that is a function defining the suggestion result. This `fix` function follows the same API as regular fixes (described above in [applying fixes](#applying-fixes)).
353
354 ```js
355 {% raw %}
356 context.report({
357 node: node,
358 message: "Unnecessary escape character: \\{{character}}.",
359 data: { character },
360 suggest: [
361 {
362 desc: "Remove the `\\`. This maintains the current functionality.",
363 fix: function(fixer) {
364 return fixer.removeRange(range);
365 }
366 },
367 {
368 desc: "Replace the `\\` with `\\\\` to include the actual backslash character.",
369 fix: function(fixer) {
370 return fixer.insertTextBeforeRange(range, "\\");
371 }
372 }
373 ]
374 });
375 {% endraw %}
376 ```
377
378 Note: Suggestions will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or confirm to user preferences on presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.
379
380 Best practices for suggestions:
381
382 1. Don't try to do too much and suggest large refactors that could introduce a lot of breaking changes.
383 1. As noted above, don't try to conform to user-defined styles.
384
385 Suggestions are intended to provide fixes. ESLint will automatically remove the whole suggestion from the linting output if the suggestion's `fix` function returned `null` or an empty array/sequence.
386
387 #### Suggestion `messageId`s
388
389 Instead of using a `desc` key for suggestions a `messageId` can be used instead. This works the same way as `messageId`s for the overall error (see [messageIds](#messageIds)). Here is an example of how to use it in a rule:
390
391 ```js
392 {% raw %}
393 module.exports = {
394 meta: {
395 messages: {
396 unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
397 removeEscape: "Remove the `\\`. This maintains the current functionality.",
398 escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
399 }
400 },
401 create: function(context) {
402 // ...
403 context.report({
404 node: node,
405 messageId: 'unnecessaryEscape',
406 data: { character },
407 suggest: [
408 {
409 messageId: "removeEscape",
410 fix: function(fixer) {
411 return fixer.removeRange(range);
412 }
413 },
414 {
415 messageId: "escapeBackslash",
416 fix: function(fixer) {
417 return fixer.insertTextBeforeRange(range, "\\");
418 }
419 }
420 ]
421 });
422 }
423 };
424 {% endraw %}
425 ```
426
427 #### Placeholders in suggestion messages
428
429 You can also use placeholders in the suggestion message. This works the same way as placeholders for the overall error (see [using message placeholders](#using-message-placeholders)).
430
431 Please note that you have to provide `data` on the suggestion's object. Suggestion messages cannot use properties from the overall error's `data`.
432
433 ```js
434 {% raw %}
435 module.exports = {
436 meta: {
437 messages: {
438 unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
439 removeEscape: "Remove `\\` before {{character}}.",
440 }
441 },
442 create: function(context) {
443 // ...
444 context.report({
445 node: node,
446 messageId: "unnecessaryEscape",
447 data: { character }, // data for the unnecessaryEscape overall message
448 suggest: [
449 {
450 messageId: "removeEscape",
451 data: { character }, // data for the removeEscape suggestion message
452 fix: function(fixer) {
453 return fixer.removeRange(range);
454 }
455 }
456 ]
457 });
458 }
459 };
460 {% endraw %}
461 ```
462
463 ### context.options
464
465 Some rules require options in order to function correctly. These options appear in configuration (`.eslintrc`, command line, or in comments). For example:
466
467 ```json
468 {
469 "quotes": ["error", "double"]
470 }
471 ```
472
473 The `quotes` rule in this example has one option, `"double"` (the `error` is the error level). You can retrieve the options for a rule by using `context.options`, which is an array containing every configured option for the rule. In this case, `context.options[0]` would contain `"double"`:
474
475 ```js
476 module.exports = {
477 create: function(context) {
478 var isDouble = (context.options[0] === "double");
479
480 // ...
481 }
482 };
483 ```
484
485 Since `context.options` is just an array, you can use it to determine how many options have been passed as well as retrieving the actual options themselves. Keep in mind that the error level is not part of `context.options`, as the error level cannot be known or modified from inside a rule.
486
487 When using options, make sure that your rule has some logical defaults in case the options are not provided.
488
489 ### context.getSourceCode()
490
491 The `SourceCode` object is the main object for getting more information about the source code being linted. You can retrieve the `SourceCode` object at any time by using the `getSourceCode()` method:
492
493 ```js
494 module.exports = {
495 create: function(context) {
496 var sourceCode = context.getSourceCode();
497
498 // ...
499 }
500 };
501 ```
502
503 Once you have an instance of `SourceCode`, you can use the methods on it to work with the code:
504
505 * `getText(node)` - returns the source code for the given node. Omit `node` to get the whole source.
506 * `getAllComments()` - returns an array of all comments in the source.
507 * `getCommentsBefore(nodeOrToken)` - returns an array of comment tokens that occur directly before the given node or token.
508 * `getCommentsAfter(nodeOrToken)` - returns an array of comment tokens that occur directly after the given node or token.
509 * `getCommentsInside(node)` - returns an array of all comment tokens inside a given node.
510 * `getJSDocComment(node)` - returns the JSDoc comment for a given node or `null` if there is none.
511 * `isSpaceBetween(nodeOrToken, nodeOrToken)` - returns true if there is a whitespace character between the two tokens or, if given a node, the last token of the first node and the first token of the second node.
512 * `getFirstToken(node, skipOptions)` - returns the first token representing the given node.
513 * `getFirstTokens(node, countOptions)` - returns the first `count` tokens representing the given node.
514 * `getLastToken(node, skipOptions)` - returns the last token representing the given node.
515 * `getLastTokens(node, countOptions)` - returns the last `count` tokens representing the given node.
516 * `getTokenAfter(nodeOrToken, skipOptions)` - returns the first token after the given node or token.
517 * `getTokensAfter(nodeOrToken, countOptions)` - returns `count` tokens after the given node or token.
518 * `getTokenBefore(nodeOrToken, skipOptions)` - returns the first token before the given node or token.
519 * `getTokensBefore(nodeOrToken, countOptions)` - returns `count` tokens before the given node or token.
520 * `getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)` - returns the first token between two nodes or tokens.
521 * `getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)` - returns the first `count` tokens between two nodes or tokens.
522 * `getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)` - returns the last token between two nodes or tokens.
523 * `getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)` - returns the last `count` tokens between two nodes or tokens.
524 * `getTokens(node)` - returns all tokens for the given node.
525 * `getTokensBetween(nodeOrToken1, nodeOrToken2)` - returns all tokens between two nodes.
526 * `getTokenByRangeStart(index, rangeOptions)` - returns the token whose range starts at the given index in the source.
527 * `getNodeByRangeIndex(index)` - returns the deepest node in the AST containing the given source index.
528 * `getLocFromIndex(index)` - returns an object with `line` and `column` properties, corresponding to the location of the given source index. `line` is 1-based and `column` is 0-based.
529 * `getIndexFromLoc(loc)` - returns the index of a given location in the source code, where `loc` is an object with a 1-based `line` key and a 0-based `column` key.
530 * `commentsExistBetween(nodeOrToken1, nodeOrToken2)` - returns `true` if comments exist between two nodes.
531
532 `skipOptions` is an object which has 3 properties; `skip`, `includeComments`, and `filter`. Default is `{skip: 0, includeComments: false, filter: null}`.
533
534 * `skip` is a positive integer, the number of skipping tokens. If `filter` option is given at the same time, it doesn't count filtered tokens as skipped.
535 * `includeComments` is a boolean value, the flag to include comment tokens into the result.
536 * `filter` is a function which gets a token as the first argument, if the function returns `false` then the result excludes the token.
537
538 `countOptions` is an object which has 3 properties; `count`, `includeComments`, and `filter`. Default is `{count: 0, includeComments: false, filter: null}`.
539
540 * `count` is a positive integer, the maximum number of returning tokens.
541 * `includeComments` is a boolean value, the flag to include comment tokens into the result.
542 * `filter` is a function which gets a token as the first argument, if the function returns `false` then the result excludes the token.
543
544 `rangeOptions` is an object which has 1 property: `includeComments`.
545
546 * `includeComments` is a boolean value, the flag to include comment tokens into the result.
547
548 There are also some properties you can access:
549
550 * `hasBOM` - the flag to indicate whether or not the source code has Unicode BOM.
551 * `text` - the full text of the code being linted. Unicode BOM has been stripped from this text.
552 * `ast` - the `Program` node of the AST for the code being linted.
553 * `scopeManager` - the [ScopeManager](./scope-manager-interface.md#scopemanager-interface) object of the code.
554 * `visitorKeys` - the visitor keys to traverse this AST.
555 * `lines` - an array of lines, split according to the specification's definition of line breaks.
556
557 You should use a `SourceCode` object whenever you need to get more information about the code being linted.
558
559 #### Deprecated
560
561 Please note that the following methods have been deprecated and will be removed in a future version of ESLint:
562
563 * `getComments()` - replaced by `getCommentsBefore()`, `getCommentsAfter()`, and `getCommentsInside()`
564 * `getTokenOrCommentBefore()` - replaced by `getTokenBefore()` with the `{ includeComments: true }` option
565 * `getTokenOrCommentAfter()` - replaced by `getTokenAfter()` with the `{ includeComments: true }` option
566 * `isSpaceBetweenTokens()` - replaced by `isSpaceBetween()`
567
568 ### Options Schemas
569
570 Rules may export a `schema` property, which is a [JSON schema](http://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
571
572 There are two formats for a rule's exported `schema`. The first is a full JSON Schema object describing all possible options the rule accepts, including the rule's error level as the first argument and any optional arguments thereafter.
573
574 However, to simplify schema creation, rules may also export an array of schemas for each optional positional argument, and ESLint will automatically validate the required error level first. For example, the `yoda` rule accepts a primary mode argument, as well as an extra options object with named properties.
575
576 ```js
577 // "yoda": [2, "never", { "exceptRange": true }]
578 module.exports = {
579 meta: {
580 schema: [
581 {
582 "enum": ["always", "never"]
583 },
584 {
585 "type": "object",
586 "properties": {
587 "exceptRange": {
588 "type": "boolean"
589 }
590 },
591 "additionalProperties": false
592 }
593 ]
594 },
595 };
596 ```
597
598 In the preceding example, the error level is assumed to be the first argument. It is followed by the first optional argument, a string which may be either `"always"` or `"never"`. The final optional argument is an object, which may have a Boolean property named `exceptRange`.
599
600 To learn more about JSON Schema, we recommend looking at some examples in [website](http://json-schema.org/learn/) to start, and also reading [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/) (a free ebook).
601
602 **Note:** Currently you need to use full JSON Schema object rather than array in case your schema has references ($ref), because in case of array format ESLint transforms this array into a single schema without updating references that makes them incorrect (they are ignored).
603
604 ### Getting the Source
605
606 If your rule needs to get the actual JavaScript source to work with, then use the `sourceCode.getText()` method. This method works as follows:
607
608 ```js
609
610 // get all source
611 var source = sourceCode.getText();
612
613 // get source for just this AST node
614 var nodeSource = sourceCode.getText(node);
615
616 // get source for AST node plus previous two characters
617 var nodeSourceWithPrev = sourceCode.getText(node, 2);
618
619 // get source for AST node plus following two characters
620 var nodeSourceWithFollowing = sourceCode.getText(node, 0, 2);
621 ```
622
623 In this way, you can look for patterns in the JavaScript text itself when the AST isn't providing the appropriate data (such as location of commas, semicolons, parentheses, etc.).
624
625 ### Accessing Comments
626
627 While comments are not technically part of the AST, ESLint provides a few ways for rules to access them:
628
629 #### sourceCode.getAllComments()
630
631 This method returns an array of all the comments found in the program. This is useful for rules that need to check all comments regardless of location.
632
633 #### sourceCode.getCommentsBefore(), sourceCode.getCommentsAfter(), and sourceCode.getCommentsInside()
634
635 These methods return an array of comments that appear directly before, directly after, and inside nodes, respectively. They are useful for rules that need to check comments in relation to a given node or token.
636
637 Keep in mind that the results of this method are calculated on demand.
638
639 #### Token traversal methods
640
641 Finally, comments can be accessed through many of `sourceCode`'s methods using the `includeComments` option.
642
643 ### Accessing Shebangs
644
645 Shebangs are represented by tokens of type `"Shebang"`. They are treated as comments and can be accessed by the methods outlined above.
646
647 ### Accessing Code Paths
648
649 ESLint analyzes code paths while traversing AST.
650 You can access that code path objects with five events related to code paths.
651
652 [details here](./code-path-analysis.md)
653
654 ## Rule Unit Tests
655
656 Each bundled rule for ESLint core must have a set of unit tests submitted with it to be accepted. The test file is named the same as the source file but lives in `tests/lib/`. For example, if the rule source file is `lib/rules/foo.js` then the test file should be `tests/lib/rules/foo.js`.
657
658 ESLint provides the [`RuleTester`](/docs/developer-guide/nodejs-api.md#ruletester) utility to make it easy to write tests for rules.
659
660 ## Performance Testing
661
662 To keep the linting process efficient and unobtrusive, it is useful to verify the performance impact of new rules or modifications to existing rules.
663
664 ### Overall Performance
665
666 When developing in the ESLint core repository, the `npm run perf` command gives a high-level overview of ESLint running time with all core rules enabled.
667
668 ```bash
669 $ git checkout master
670 Switched to branch 'master'
671
672 $ npm run perf
673 CPU Speed is 2200 with multiplier 7500000
674 Performance Run #1: 1394.689313ms
675 Performance Run #2: 1423.295351ms
676 Performance Run #3: 1385.09515ms
677 Performance Run #4: 1382.406982ms
678 Performance Run #5: 1409.68566ms
679 Performance budget ok: 1394.689313ms (limit: 3409.090909090909ms)
680
681 $ git checkout my-rule-branch
682 Switched to branch 'my-rule-branch'
683
684 $ npm run perf
685 CPU Speed is 2200 with multiplier 7500000
686 Performance Run #1: 1443.736547ms
687 Performance Run #2: 1419.193291ms
688 Performance Run #3: 1436.018228ms
689 Performance Run #4: 1473.605485ms
690 Performance Run #5: 1457.455283ms
691 Performance budget ok: 1443.736547ms (limit: 3409.090909090909ms)
692 ```
693
694 ### Per-rule Performance
695
696 ESLint has a built-in method to track performance of individual rules. Setting the `TIMING` environment variable will trigger the display, upon linting completion, of the ten longest-running rules, along with their individual running time and relative performance impact as a percentage of total rule processing time.
697
698 ```bash
699 $ TIMING=1 eslint lib
700 Rule | Time (ms) | Relative
701 :-----------------------|----------:|--------:
702 no-multi-spaces | 52.472 | 6.1%
703 camelcase | 48.684 | 5.7%
704 no-irregular-whitespace | 43.847 | 5.1%
705 valid-jsdoc | 40.346 | 4.7%
706 handle-callback-err | 39.153 | 4.6%
707 space-infix-ops | 35.444 | 4.1%
708 no-undefined | 25.693 | 3.0%
709 no-shadow | 22.759 | 2.7%
710 no-empty-class | 21.976 | 2.6%
711 semi | 19.359 | 2.3%
712 ```
713
714 To test one rule explicitly, combine the `--no-eslintrc`, and `--rule` options:
715
716 ```bash
717 $ TIMING=1 eslint --no-eslintrc --rule "quotes: [2, 'double']" lib
718 Rule | Time (ms) | Relative
719 :------|----------:|--------:
720 quotes | 18.066 | 100.0%
721 ```
722
723 ## Rule Naming Conventions
724
725 The rule naming conventions for ESLint are fairly simple:
726
727 * If your rule is disallowing something, prefix it with `no-` such as `no-eval` for disallowing `eval()` and `no-debugger` for disallowing `debugger`.
728 * If your rule is enforcing the inclusion of something, use a short name without a special prefix.
729 * Use dashes between words.
730
731 ## Runtime Rules
732
733 The thing that makes ESLint different from other linters is the ability to define custom rules at runtime. This is perfect for rules that are specific to your project or company and wouldn't make sense for ESLint to ship with. With runtime rules, you don't have to wait for the next version of ESLint or be disappointed that your rule isn't general enough to apply to the larger JavaScript community, just write your rules and include them at runtime.
734
735 Runtime rules are written in the same format as all other rules. Create your rule as you would any other and then follow these steps:
736
737 1. Place all of your runtime rules in the same directory (e.g., `eslint_rules`).
738 2. Create a [configuration file](../user-guide/configuring.md) and specify your rule ID error level under the `rules` key. Your rule will not run unless it has a value of `"warn"` or `"error"` in the configuration file.
739 3. Run the [command line interface](../user-guide/command-line-interface.md) using the `--rulesdir` option to specify the location of your runtime rules.