]> git.proxmox.com Git - pve-eslint.git/blob - eslint/docs/src/extend/custom-rules.md
import 8.41.0 source
[pve-eslint.git] / eslint / docs / src / extend / custom-rules.md
1 ---
2 title: Custom Rules
3 eleventyNavigation:
4 key: custom rules
5 parent: create plugins
6 title: Custom Rules
7 order: 1
8
9 ---
10
11 You can create custom rules to use with ESLint. You might want to create a custom rule if the [core rules](../rules/) do not cover your use case.
12
13 **Note:** This page covers the most recent rule format for ESLint >= 3.0.0. There is also a [deprecated rule format](./custom-rules-deprecated).
14
15 Here's the basic format of a custom rule:
16
17 ```js
18 // customRule.js
19
20 module.exports = {
21 meta: {
22 type: "suggestion",
23 docs: {
24 description: "Description of the rule",
25 },
26 fixable: "code",
27 schema: [] // no options
28 },
29 create: function(context) {
30 return {
31 // callback functions
32 };
33 }
34 };
35 ```
36
37 ## Rule Structure
38
39 The source file for a rule exports an object with the following properties. Both custom rules and core rules follow this format.
40
41 `meta`: (`object`) Contains metadata for the rule:
42
43 * `type`: (`string`) Indicates the type of rule, which is one of `"problem"`, `"suggestion"`, or `"layout"`:
44
45 * `"problem"`: 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.
46 * `"suggestion"`: The rule is identifying something that could be done in a better way but no errors will occur if the code isn't changed.
47 * `"layout"`: 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.
48
49 * `docs`: (`object`) Required for core rules and optional for custom rules. Core rules have specific entries inside of `docs` while custom rules can include any properties that you need. The following properties are only relevant when working on core rules.
50
51 * `description`: (`string`) Provides the short description of the rule in the [rules index](../rules/).
52 * `recommended`: (`boolean`) Specifies whether the `"extends": "eslint:recommended"` property in a [configuration file](../use/configure/configuration-files#extending-configuration-files) enables the rule.
53 * `url`: (`string`) Specifies the URL at which the full documentation can be accessed (enabling code editors to provide a helpful link on highlighted rule violations).
54
55 * `fixable`: (`string`) Either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../use/command-line-interface#--fix) automatically fixes problems reported by the rule.
56
57 **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.
58
59 * `hasSuggestions`: (`boolean`) Specifies whether rules can return suggestions (defaults to `false` if omitted).
60
61 **Important:** the `hasSuggestions` property is mandatory for rules that provide suggestions. If this property isn't set to `true`, ESLint will throw an error whenever the rule attempts to produce a suggestion. Omit the `hasSuggestions` property if the rule does not provide suggestions.
62
63 * `schema`: (`object | array`) Specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules).
64
65 * `deprecated`: (`boolean`) Indicates whether the rule has been deprecated. You may omit the `deprecated` property if the rule has not been deprecated.
66
67 * `replacedBy`: (`array`) In the case of a deprecated rule, specify replacement rule(s).
68
69 `create()`: 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:
70
71 * If a key is a node type or a [selector](./selectors), ESLint calls that **visitor** function while going **down** the tree.
72 * If a key is a node type or a [selector](./selectors) plus `:exit`, ESLint calls that **visitor** function while going **up** the tree.
73 * If a key is an event name, ESLint calls that **handler** function for [code path analysis](code-path-analysis).
74
75 A rule can use the current node and its surrounding tree to report or fix problems.
76
77 Here are methods for the [array-callback-return](../rules/array-callback-return) rule:
78
79 ```js
80 function checkLastSegment (node) {
81 // report problem for function if last code path segment is reachable
82 }
83
84 module.exports = {
85 meta: { ... },
86 create: function(context) {
87 // declare the state of the rule
88 return {
89 ReturnStatement: function(node) {
90 // at a ReturnStatement node while going down
91 },
92 // at a function expression node while going up:
93 "FunctionExpression:exit": checkLastSegment,
94 "ArrowFunctionExpression:exit": checkLastSegment,
95 onCodePathStart: function (codePath, node) {
96 // at the start of analyzing a code path
97 },
98 onCodePathEnd: function(codePath, node) {
99 // at the end of analyzing a code path
100 }
101 };
102 }
103 };
104 ```
105
106 ## The Context Object
107
108 The `context` object is the only argument of the `create` method in a rule. For example:
109
110 ```js
111 // customRule.js
112
113 module.exports = {
114 meta: { ... },
115 // `context` object is the argument
116 create(context) {
117 // ...
118 }
119 };
120 ```
121
122 As the name implies, the `context` object contains information that is relevant to the context of the rule.
123
124 The `context` object has the following properties:
125
126 * `id`: (`string`) The rule ID.
127 * `filename`: (`string`) The filename associated with the source.
128 * `physicalFilename`: (`string`) When linting a file, it provides the full path of the file on disk without any code block information. When linting text, it provides the value passed to `—stdin-filename` or `<text>` if not specified.
129 * `cwd`: (`string`) The `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
130 * `options`: (`array`) An array of the [configured options](../use/configure/rules) for this rule. This array does not include the rule severity (see the [dedicated section](#accessing-options-passed-to-a-rule)).
131 * `sourceCode`: (`object`) A `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
132 * `settings`: (`object`) The [shared settings](../use/configure/configuration-files#adding-shared-settings) from the configuration.
133 * `parserPath`: (`string`) The name of the `parser` from the configuration.
134 * `parserServices`: (`object`) Contains 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.)
135 * `parserOptions`: The parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).
136
137 Additionally, the `context` object has the following methods:
138
139 * `getAncestors()`: (**Deprecated:** Use `SourceCode#getAncestors(node)` instead.) 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.
140 * `getCwd()`: (**Deprecated:** Use `context.cwd` instead.) Returns the `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
141 * `getDeclaredVariables(node)`: (**Deprecated:** Use `SourceCode#getDeclaredVariables(node)` instead.) Returns a list of [variables](./scope-manager-interface#variable-interface) declared by the given node. This information can be used to track references to variables.
142 * If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
143 * If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
144 * If the node is a `FunctionDeclaration` or `FunctionExpression`, the variable for the function name is returned, in addition to variables for the function parameters.
145 * If the node is an `ArrowFunctionExpression`, variables for the parameters are returned.
146 * If the node is a `ClassDeclaration` or a `ClassExpression`, the variable for the class name is returned.
147 * If the node is a `CatchClause`, the variable for the exception is returned.
148 * If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
149 * If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
150 * Otherwise, if the node does not declare any variables, an empty array is returned.
151 * `getFilename()`: (**Deprecated:** Use `context.filename` instead.) Returns the filename associated with the source.
152 * `getPhysicalFilename()`: (**Deprecated:** Use `context.physicalFilename` instead.) When linting a file, it returns the full path of the file on disk without any code block information. When linting text, it returns the value passed to `—stdin-filename` or `<text>` if not specified.
153 * `getScope()`: (**Deprecated:** Use `SourceCode#getScope(node)` instead.) Returns the [scope](./scope-manager-interface#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
154 * `getSourceCode()`: (**Deprecated:** Use `context.sourceCode` instead.) Returns a `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
155 * `markVariableAsUsed(name)`: (**Deprecated:** Use `SourceCode#markVariableAsUsed(name, node)` instead.) Marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.
156 * `report(descriptor)`. Reports a problem in the code (see the [dedicated section](#reporting-problems)).
157
158 **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.
159
160 ### Reporting Problems
161
162 The main method you'll use when writing custom rules 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:
163
164 * `message`: (`string`) The problem message.
165 * `node`: (optional `object`) 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.
166 * `loc`: (optional `object`) Specifies the location of the problem. If both `loc` and `node` are specified, then the location is used from `loc` instead of `node`.
167 * `start`: An object of the start location.
168 * `line`: (`number`) The 1-based line number at which the problem occurred.
169 * `column`: (`number`) The 0-based column number at which the problem occurred.
170 * `end`: An object of the end location.
171 * `line`: (`number`) The 1-based line number at which the problem occurred.
172 * `column`: (`number`) The 0-based column number at which the problem occurred.
173 * `data`: (optional `object`) [Placeholder](#using-message-placeholders) data for `message`.
174 * `fix(fixer)`: (optional `function`) Applies a [fix](#applying-fixes) to resolve the problem.
175
176 Note that at least one of `node` or `loc` is required.
177
178 The simplest example is to use just `node` and `message`:
179
180 ```js
181 context.report({
182 node: node,
183 message: "Unexpected identifier"
184 });
185 ```
186
187 The node contains all the information necessary to figure out the line and column number of the offending text as well as the source text representing the node.
188
189 #### Using Message Placeholders
190
191 You can also use placeholders in the message and provide `data`:
192
193 ```js
194 {% raw %}
195 context.report({
196 node: node,
197 message: "Unexpected identifier: {{ identifier }}",
198 data: {
199 identifier: node.name
200 }
201 });
202 {% endraw %}
203 ```
204
205 Note that leading and trailing whitespace is optional in message parameters.
206
207 The node contains all the information necessary to figure out the line and column number of the offending text as well as the source text representing the node.
208
209 #### `messageId`s
210
211 Instead of typing out messages in both the `context.report()` call and your tests, you can use `messageId`s instead.
212
213 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.
214
215 Rule file:
216
217 ```js
218 {% raw %}
219 // avoid-name.js
220
221 module.exports = {
222 meta: {
223 messages: {
224 avoidName: "Avoid using variables named '{{ name }}'"
225 }
226 },
227 create(context) {
228 return {
229 Identifier(node) {
230 if (node.name === "foo") {
231 context.report({
232 node,
233 messageId: "avoidName",
234 data: {
235 name: "foo",
236 }
237 });
238 }
239 }
240 };
241 }
242 };
243 {% endraw %}
244 ```
245
246 In the file to lint:
247
248 ```javascript
249 // someFile.js
250
251 var foo = 2;
252 // ^ error: Avoid using variables named 'foo'
253 ```
254
255 In your tests:
256
257 ```javascript
258 // avoid-name.test.js
259
260 var rule = require("../../../lib/rules/avoid-name");
261 var RuleTester = require("eslint").RuleTester;
262
263 var ruleTester = new RuleTester();
264 ruleTester.run("avoid-name", rule, {
265 valid: ["bar", "baz"],
266 invalid: [
267 {
268 code: "foo",
269 errors: [
270 {
271 messageId: "avoidName"
272 }
273 ]
274 }
275 ]
276 });
277 ```
278
279 #### Applying Fixes
280
281 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:
282
283 ```js
284 context.report({
285 node: node,
286 message: "Missing semicolon",
287 fix(fixer) {
288 return fixer.insertTextAfter(node, ";");
289 }
290 });
291 ```
292
293 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 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. Afterward, any remaining problems will be reported as usual.
294
295 **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-structure) the `meta.fixable` property.
296
297 The `fixer` object has the following methods:
298
299 * `insertTextAfter(nodeOrToken, text)`: Insert text after the given node or token.
300 * `insertTextAfterRange(range, text)`: Insert text after the given range.
301 * `insertTextBefore(nodeOrToken, text)`: Insert text before the given node or token.
302 * `insertTextBeforeRange(range, text)`: Insert text before the given range.
303 * `remove(nodeOrToken)`: Remove the given node or token.
304 * `removeRange(range)`: Remove text in the given range.
305 * `replaceText(nodeOrToken, text)`: Replace the text in the given node or token.
306 * `replaceTextRange(range, text)`: Replace the text in the given range.
307
308 A `range` is a two-item array containing character indices inside the source code. The first item is the start of the range (inclusive) and the second item is the end of the range (exclusive). Every node and token has a `range` property to identify the source code range they represent.
309
310 The above methods return a `fixing` object.
311 The `fix()` function can return the following values:
312
313 * A `fixing` object.
314 * An array which includes `fixing` objects.
315 * An iterable object which enumerates `fixing` objects. Especially, the `fix()` function can be a generator.
316
317 If you make a `fix()` function which returns multiple `fixing` objects, those `fixing` objects must not overlap.
318
319 Best practices for fixes:
320
321 1. Avoid any fixes that could change the runtime behavior of code and cause it to stop working.
322 1. Make fixes as small as possible. Fixes that are unnecessarily large could conflict with other fixes, and prevent them from being applied.
323 1. Only make one fix per message. This is enforced because you must return the result of the fixer operation from `fix()`.
324 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.
325 * 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.
326
327 ```js
328 ({ foo : 1 })
329
330 // should get fixed to either
331
332 ({ 'foo': 1 })
333
334 // or
335
336 ({ "foo": 1 })
337 ```
338
339 * 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`](../rules/quotes) rule.
340
341 Note: Making fixes as small as possible is a best practice, but in some cases it may be correct to extend the range of the fix in order to intentionally prevent other rules from making fixes in a surrounding range in the same pass. For instance, if replacement text declares a new variable, it can be useful to prevent other changes in the scope of the variable as they might cause name collisions.
342
343 The following example replaces `node` and also ensures that no other fixes will be applied in the range of `node.parent` in the same pass:
344
345 ```js
346 context.report({
347 node,
348 message,
349 *fix(fixer) {
350 yield fixer.replaceText(node, replacementText);
351
352 // extend range of the fix to the range of `node.parent`
353 yield fixer.insertTextBefore(node.parent, "");
354 yield fixer.insertTextAfter(node.parent, "");
355 }
356 });
357 ```
358
359 #### Conflicting Fixes
360
361 Conflicting fixes are fixes that apply different changes to the same part of the source code.
362 There is no way to specify which of the conflicting fixes is applied.
363
364 For example, if two fixes want to modify characters 0 through 5, only one is applied.
365
366 #### Providing Suggestions
367
368 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.
369
370 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)).
371
372 ```js
373 {% raw %}
374 context.report({
375 node: node,
376 message: "Unnecessary escape character: \\{{character}}.",
377 data: { character },
378 suggest: [
379 {
380 desc: "Remove the `\\`. This maintains the current functionality.",
381 fix: function(fixer) {
382 return fixer.removeRange(range);
383 }
384 },
385 {
386 desc: "Replace the `\\` with `\\\\` to include the actual backslash character.",
387 fix: function(fixer) {
388 return fixer.insertTextBeforeRange(range, "\\");
389 }
390 }
391 ]
392 });
393 {% endraw %}
394 ```
395
396 **Important:** The `meta.hasSuggestions` property is mandatory for rules that provide suggestions. ESLint will throw an error if a rule attempts to produce a suggestion but does not [export](#rule-structure) this property.
397
398 **Note:** Suggestions are applied as stand-alone changes, 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 conform to user preferences on the presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.
399
400 Best practices for suggestions:
401
402 1. Don't try to do too much and suggest large refactors that could introduce a lot of breaking changes.
403 1. As noted above, don't try to conform to user-defined styles.
404
405 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.
406
407 #### Suggestion `messageId`s
408
409 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 a suggestion `messageId` in a rule:
410
411 ```js
412 {% raw %}
413 module.exports = {
414 meta: {
415 messages: {
416 unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
417 removeEscape: "Remove the `\\`. This maintains the current functionality.",
418 escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
419 },
420 hasSuggestions: true
421 },
422 create: function(context) {
423 // ...
424 context.report({
425 node: node,
426 messageId: 'unnecessaryEscape',
427 data: { character },
428 suggest: [
429 {
430 messageId: "removeEscape", // suggestion messageId
431 fix: function(fixer) {
432 return fixer.removeRange(range);
433 }
434 },
435 {
436 messageId: "escapeBackslash", // suggestion messageId
437 fix: function(fixer) {
438 return fixer.insertTextBeforeRange(range, "\\");
439 }
440 }
441 ]
442 });
443 }
444 };
445 {% endraw %}
446 ```
447
448 #### Placeholders in Suggestion Messages
449
450 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)).
451
452 Please note that you have to provide `data` on the suggestion's object. Suggestion messages cannot use properties from the overall error's `data`.
453
454 ```js
455 {% raw %}
456 module.exports = {
457 meta: {
458 messages: {
459 unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
460 removeEscape: "Remove `\\` before {{character}}.",
461 },
462 hasSuggestions: true
463 },
464 create: function(context) {
465 // ...
466 context.report({
467 node: node,
468 messageId: "unnecessaryEscape",
469 data: { character }, // data for the unnecessaryEscape overall message
470 suggest: [
471 {
472 messageId: "removeEscape",
473 data: { character }, // data for the removeEscape suggestion message
474 fix: function(fixer) {
475 return fixer.removeRange(range);
476 }
477 }
478 ]
479 });
480 }
481 };
482 {% endraw %}
483 ```
484
485 ### Accessing Options Passed to a Rule
486
487 Some rules require options in order to function correctly. These options appear in configuration (`.eslintrc`, command line interface, or comments). For example:
488
489 ```json
490 {
491 "quotes": ["error", "double"]
492 }
493 ```
494
495 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"`:
496
497 ```js
498 module.exports = {
499 create: function(context) {
500 var isDouble = (context.options[0] === "double");
501
502 // ...
503 }
504 };
505 ```
506
507 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.
508
509 When using options, make sure that your rule has some logical defaults in case the options are not provided.
510
511 ### Accessing the Source Code
512
513 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 `context.sourceCode` property:
514
515 ```js
516 module.exports = {
517 create: function(context) {
518 var sourceCode = context.sourceCode;
519
520 // ...
521 }
522 };
523 ```
524
525 **Deprecated:** The `context.getSourceCode()` method is deprecated; make sure to use `context.sourceCode` property instead.
526
527 Once you have an instance of `SourceCode`, you can use the following methods on it to work with the code:
528
529 * `getText(node)`: Returns the source code for the given node. Omit `node` to get the whole source (see the [dedicated section](#accessing-the-source-text)).
530 * `getAllComments()`: Returns an array of all comments in the source (see the [dedicated section](#accessing-comments)).
531 * `getCommentsBefore(nodeOrToken)`: Returns an array of comment tokens that occur directly before the given node or token (see the [dedicated section](#accessing-comments)).
532 * `getCommentsAfter(nodeOrToken)`: Returns an array of comment tokens that occur directly after the given node or token (see the [dedicated section](#accessing-comments)).
533 * `getCommentsInside(node)`: Returns an array of all comment tokens inside a given node (see the [dedicated section](#accessing-comments)).
534 * `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.
535 * `getFirstToken(node, skipOptions)`: Returns the first token representing the given node.
536 * `getFirstTokens(node, countOptions)`: Returns the first `count` tokens representing the given node.
537 * `getLastToken(node, skipOptions)`: Returns the last token representing the given node.
538 * `getLastTokens(node, countOptions)`: Returns the last `count` tokens representing the given node.
539 * `getTokenAfter(nodeOrToken, skipOptions)`: Returns the first token after the given node or token.
540 * `getTokensAfter(nodeOrToken, countOptions)`: Returns `count` tokens after the given node or token.
541 * `getTokenBefore(nodeOrToken, skipOptions)`: Returns the first token before the given node or token.
542 * `getTokensBefore(nodeOrToken, countOptions)`: Returns `count` tokens before the given node or token.
543 * `getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`: Returns the first token between two nodes or tokens.
544 * `getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`: Returns the first `count` tokens between two nodes or tokens.
545 * `getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`: Returns the last token between two nodes or tokens.
546 * `getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`: Returns the last `count` tokens between two nodes or tokens.
547 * `getTokens(node)`: Returns all tokens for the given node.
548 * `getTokensBetween(nodeOrToken1, nodeOrToken2)`: Returns all tokens between two nodes.
549 * `getTokenByRangeStart(index, rangeOptions)`: Returns the token whose range starts at the given index in the source.
550 * `getNodeByRangeIndex(index)`: Returns the deepest node in the AST containing the given source index.
551 * `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.
552 * `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.
553 * `commentsExistBetween(nodeOrToken1, nodeOrToken2)`: Returns `true` if comments exist between two nodes.
554
555 `skipOptions` is an object which has 3 properties; `skip`, `includeComments`, and `filter`. Default is `{skip: 0, includeComments: false, filter: null}`.
556
557 * `skip`: (`number`) Positive integer, the number of skipping tokens. If `filter` option is given at the same time, it doesn't count filtered tokens as skipped.
558 * `includeComments`: (`boolean`) The flag to include comment tokens into the result.
559 * `filter(token)`: Function which gets a token as the first argument. If the function returns `false` then the result excludes the token.
560
561 `countOptions` is an object which has 3 properties; `count`, `includeComments`, and `filter`. Default is `{count: 0, includeComments: false, filter: null}`.
562
563 * `count`: (`number`) Positive integer, the maximum number of returning tokens.
564 * `includeComments`: (`boolean`) The flag to include comment tokens into the result.
565 * `filter(token)`: Function which gets a token as the first argument, if the function returns `false` then the result excludes the token.
566
567 `rangeOptions` is an object that has 1 property, `includeComments`. Default is `{includeComments: false}`.
568
569 * `includeComments`: (`boolean`) The flag to include comment tokens into the result.
570
571 There are also some properties you can access:
572
573 * `hasBOM`: (`boolean`) The flag to indicate whether the source code has Unicode BOM.
574 * `text`: (`string`) The full text of the code being linted. Unicode BOM has been stripped from this text.
575 * `ast`: (`object`) `Program` node of the AST for the code being linted.
576 * `scopeManager`: [ScopeManager](./scope-manager-interface#scopemanager-interface) object of the code.
577 * `visitorKeys`: (`object`) Visitor keys to traverse this AST.
578 * `lines`: (`array`) Array of lines, split according to the specification's definition of line breaks.
579
580 You should use a `SourceCode` object whenever you need to get more information about the code being linted.
581
582 #### Accessing the Source Text
583
584 If your rule needs to get the actual JavaScript source to work with, then use the `sourceCode.getText()` method. This method works as follows:
585
586 ```js
587
588 // get all source
589 var source = sourceCode.getText();
590
591 // get source for just this AST node
592 var nodeSource = sourceCode.getText(node);
593
594 // get source for AST node plus previous two characters
595 var nodeSourceWithPrev = sourceCode.getText(node, 2);
596
597 // get source for AST node plus following two characters
598 var nodeSourceWithFollowing = sourceCode.getText(node, 0, 2);
599 ```
600
601 In this way, you can look for patterns in the JavaScript text itself when the AST isn't providing the appropriate data (such as the location of commas, semicolons, parentheses, etc.).
602
603 #### Accessing Comments
604
605 While comments are not technically part of the AST, ESLint provides the `sourceCode.getAllComments()`, `sourceCode.getCommentsBefore()`, `sourceCode.getCommentsAfter()`, and `sourceCode.getCommentsInside()` to access them.
606
607 `sourceCode.getCommentsBefore()`, `sourceCode.getCommentsAfter()`, and `sourceCode.getCommentsInside()` are useful for rules that need to check comments in relation to a given node or token.
608
609 Keep in mind that the results of these methods are calculated on demand.
610
611 You can also access comments through many of `sourceCode`'s methods using the `includeComments` option.
612
613 ### Options Schemas
614
615 Rules may export a `schema` property, which is a [JSON Schema](https://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`.
616
617 There are two formats for a rule's exported `schema`:
618
619 1. A full JSON Schema object describing all possible options the rule accepts.
620 2. An array of JSON Schema objects for each optional positional argument.
621
622 In both cases, these should exclude the [severity](../use/configure/rules#rule-severities), as ESLint automatically validates this first.
623
624 For example, the `yoda` rule accepts a primary mode argument of `"always"` or `"never"`, as well as an extra options object with an optional property `exceptRange`:
625
626 ```js
627 // "yoda": ["error", "never", { "exceptRange": true }]
628 module.exports = {
629 meta: {
630 schema: [
631 {
632 "enum": ["always", "never"]
633 },
634 {
635 "type": "object",
636 "properties": {
637 "exceptRange": {
638 "type": "boolean"
639 }
640 },
641 "additionalProperties": false
642 }
643 ]
644 },
645 };
646 ```
647
648 **Note:** If your rule schema uses JSON schema [`$ref`](https://json-schema.org/understanding-json-schema/structuring.html#ref) properties, you must use the full JSON Schema object rather than the array of positional property schemas. This is because ESLint transforms the array shorthand into a single schema without updating references that makes them incorrect (they are ignored).
649
650 To learn more about JSON Schema, we recommend looking at some examples on the [JSON Schema website](https://json-schema.org/learn/), or reading the free [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/) ebook.
651
652 ### Accessing Shebangs
653
654 [Shebangs (#!)](https://en.wikipedia.org/wiki/Shebang_(Unix)) are represented by the unique tokens of type `"Shebang"`. They are treated as comments and can be accessed by the methods outlined in the [Accessing Comments](#accessing-comments) section, such as `sourceCode.getAllComments()`.
655
656 ### Accessing Variable Scopes
657
658 The `SourceCode#getScope(node)` method returns the scope of the given node. It is a useful method for finding information about the variables in a given scope and how they are used in other scopes.
659
660 **Deprecated:** The `context.getScope()` is deprecated; make sure to use `SourceCode#getScope(node)` instead.
661
662 #### Scope types
663
664 The following table contains a list of AST node types and the scope type that they correspond to. For more information about the scope types, refer to the [`Scope` object documentation](./scope-manager-interface#scope-interface).
665
666 | AST Node Type | Scope Type |
667 |:--------------------------|:-----------|
668 | `Program` | `global` |
669 | `FunctionDeclaration` | `function` |
670 | `FunctionExpression` | `function` |
671 | `ArrowFunctionExpression` | `function` |
672 | `ClassDeclaration` | `class` |
673 | `ClassExpression` | `class` |
674 | `BlockStatement` ※1 | `block` |
675 | `SwitchStatement` ※1 | `switch` |
676 | `ForStatement` ※2 | `for` |
677 | `ForInStatement` ※2 | `for` |
678 | `ForOfStatement` ※2 | `for` |
679 | `WithStatement` | `with` |
680 | `CatchClause` | `catch` |
681 | others | ※3 |
682
683 **※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>
684 **※2** Only if the `for` statement defines the iteration variable as a block-scoped variable (E.g., `for (let i = 0;;) {}`).<br>
685 **※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.).
686
687 #### Scope Variables
688
689 The `Scope#variables` property contains an array of [`Variable` objects](./scope-manager-interface#variable-interface). These are the variables declared in current scope. You can use these `Variable` objects to track references to a variable throughout the entire module.
690
691 Inside of each `Variable`, the `Variable#references` property contains an array of [`Reference` objects](./scope-manager-interface#reference-interface). The `Reference` array contains all the locations where the variable is referenced in the module's source code.
692
693 Also inside of each `Variable`, the `Variable#defs` property contains an array of [`Definition` objects](./scope-manager-interface#definition-interface). You can use the `Definitions` to find where the variable was defined.
694
695 Global variables have the following additional properties:
696
697 * `Variable#writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only.
698 * `Variable#eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file.
699 * `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.
700 * `Variable#eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments.
701
702 For examples of using `SourceCode#getScope()` to track variables, refer to the source code for the following built-in rules:
703
704 * [no-shadow](https://github.com/eslint/eslint/blob/main/lib/rules/no-shadow.js): Calls `sourceCode.getScope()` at the `Program` node and inspects all child scopes to make sure a variable name is not reused at a lower scope. ([no-shadow](../rules/no-shadow) documentation)
705 * [no-redeclare](https://github.com/eslint/eslint/blob/main/lib/rules/no-redeclare.js): Calls `sourceCode.getScope()` at each scope to make sure that a variable is not declared twice in the same scope. ([no-redeclare](../rules/no-redeclare) documentation)
706
707 ### Marking Variables as Used
708
709 **Deprecated:** The `context.markVariableAsUsed()` method is deprecated in favor of `sourceCode.markVariableAsUsed()`.
710
711 Certain ESLint rules, such as [`no-unused-vars`](../rules/no-unused-vars), check to see if a variable has been used. ESLint itself only knows about the standard rules of variable access and so custom ways of accessing variables may not register as "used".
712
713 To help with this, you can use the `sourceCode.markVariableAsUsed()` method. This method takes two arguments: the name of the variable to mark as used and an option reference node indicating the scope in which you are working. Here's an example:
714
715 ```js
716 module.exports = {
717 create: function(context) {
718 var sourceCode = context.sourceCode;
719
720 return {
721 ReturnStatement(node) {
722
723 // look in the scope of the function for myCustomVar and mark as used
724 sourceCode.markVariableAsUsed("myCustomVar", node);
725
726 // or: look in the global scope for myCustomVar and mark as used
727 sourceCode.markVariableAsUsed("myCustomVar");
728 }
729 }
730 // ...
731 }
732 };
733 ```
734
735 Here, the `myCustomVar` variable is marked as used relative to a `ReturnStatement` node, which means ESLint will start searching from the scope closest to that node. If you omit the second argument, then the top-level scope is used. (For ESM files, the top-level scope is the module scope; for CommonJS files, the top-level scope is the first function scope.)
736
737 ### Accessing Code Paths
738
739 ESLint analyzes code paths while traversing AST. You can access code path objects with five events related to code paths. For more information, refer to [Code Path Analysis](code-path-analysis).
740
741 ### Deprecated `SourceCode` Methods
742
743 Please note that the following `SourceCode` methods have been deprecated and will be removed in a future version of ESLint:
744
745 * `getComments()`: Replaced by `SourceCode#getCommentsBefore()`, `SourceCode#getCommentsAfter()`, and `SourceCode#getCommentsInside()`.
746 * `getTokenOrCommentBefore()`: Replaced by `SourceCode#getTokenBefore()` with the `{ includeComments: true }` option.
747 * `getTokenOrCommentAfter()`: Replaced by `SourceCode#getTokenAfter()` with the `{ includeComments: true }` option.
748 * `isSpaceBetweenTokens()`: Replaced by `SourceCode#isSpaceBetween()`
749 * `getJSDocComment()`
750
751 ## Rule Unit Tests
752
753 ESLint provides the [`RuleTester`](../integrate/nodejs-api#ruletester) utility to make it easy to write tests for rules.
754
755 ## Rule Naming Conventions
756
757 While you can give a custom rule any name you'd like, the core rules have naming conventions. It could be clearer to apply these same naming conventions to your custom rule. To learn more, refer to the [Core Rule Naming Conventions](../contribute/core-rules#rule-naming-conventions) documentation.
758
759 ## Runtime Rules
760
761 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 or be included in a plugin. Just write your rules and include them at runtime.
762
763 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:
764
765 1. Place all of your runtime rules in the same directory (e.g., `eslint_rules`).
766 2. Create a [configuration file](../use/configure/) 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.
767 3. Run the [command line interface](../use/command-line-interface) using the `--rulesdir` option to specify the location of your runtime rules.
768
769 ## Profile Rule Performance
770
771 ESLint has a built-in method to track the 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 (rule creation + rule execution) and relative performance impact as a percentage of total rule processing time (rule creation + rule execution).
772
773 ```bash
774 $ TIMING=1 eslint lib
775 Rule | Time (ms) | Relative
776 :-----------------------|----------:|--------:
777 no-multi-spaces | 52.472 | 6.1%
778 camelcase | 48.684 | 5.7%
779 no-irregular-whitespace | 43.847 | 5.1%
780 valid-jsdoc | 40.346 | 4.7%
781 handle-callback-err | 39.153 | 4.6%
782 space-infix-ops | 35.444 | 4.1%
783 no-undefined | 25.693 | 3.0%
784 no-shadow | 22.759 | 2.7%
785 no-empty-class | 21.976 | 2.6%
786 semi | 19.359 | 2.3%
787 ```
788
789 To test one rule explicitly, combine the `--no-eslintrc`, and `--rule` options:
790
791 ```bash
792 $ TIMING=1 eslint --no-eslintrc --rule "quotes: [2, 'double']" lib
793 Rule | Time (ms) | Relative
794 :------|----------:|--------:
795 quotes | 18.066 | 100.0%
796 ```
797
798 To see a longer list of results (more than 10), set the environment variable to another value such as `TIMING=50` or `TIMING=all`.