]> git.proxmox.com Git - pve-eslint.git/blob - eslint/docs/src/developer-guide/selectors.md
import 8.23.1 source
[pve-eslint.git] / eslint / docs / src / developer-guide / selectors.md
1 ---
2 title: Selectors
3 layout: doc
4
5 ---
6
7 Some rules and APIs allow the use of selectors to query an AST. This page is intended to:
8
9 1. Explain what selectors are
10 2. Describe the syntax for creating selectors
11 3. Describe what selectors can be used for
12
13 ## What is a selector?
14
15 A selector is a string that can be used to match nodes in an Abstract Syntax Tree (AST). This is useful for describing a particular syntax pattern in your code.
16
17 The syntax for AST selectors is similar to the syntax for [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors). If you've used CSS selectors before, the syntax for AST selectors should be easy to understand.
18
19 The simplest selector is just a node type. A node type selector will match all nodes with the given type. For example, consider the following program:
20
21 ```js
22 var foo = 1;
23 bar.baz();
24 ```
25
26 The selector "`Identifier`" will match all `Identifier` nodes in the program. In this case, the selector will match the nodes for `foo`, `bar`, and `baz`.
27
28 Selectors are not limited to matching against single node types. For example, the selector `VariableDeclarator > Identifier` will match all `Identifier` nodes that have a `VariableDeclarator` as a direct parent. In the program above, this will match the node for `foo`, but not the nodes for `bar` and `baz`.
29
30 ## What syntax can selectors have?
31
32 The following selectors are supported:
33
34 * AST node type: `ForStatement`
35 * wildcard (matches all nodes): `*`
36 * attribute existence: `[attr]`
37 * attribute value: `[attr="foo"]` or `[attr=123]`
38 * attribute regex: `[attr=/foo.*/]` <sub>(with some [known issues](#known-issues))</sub>
39 * attribute conditions: `[attr!="foo"]`, `[attr>2]`, `[attr<3]`, `[attr>=2]`, or `[attr<=3]`
40 * nested attribute: `[attr.level2="foo"]`
41 * field: `FunctionDeclaration > Identifier.id`
42 * First or last child: `:first-child` or `:last-child`
43 * nth-child (no ax+b support): `:nth-child(2)`
44 * nth-last-child (no ax+b support): `:nth-last-child(1)`
45 * descendant: `FunctionExpression ReturnStatement`
46 * child: `UnaryExpression > Literal`
47 * following sibling: `VariableDeclaration ~ VariableDeclaration`
48 * adjacent sibling: `ArrayExpression > Literal + SpreadElement`
49 * negation: `:not(ForStatement)`
50 * matches-any: `:matches([attr] > :first-child, :last-child)`
51 * class of AST node: `:statement`, `:expression`, `:declaration`, `:function`, or `:pattern`
52
53 This syntax is very powerful, and can be used to precisely select many syntactic patterns in your code.
54
55 <sup>The examples in this section were adapted from the [esquery](https://github.com/estools/esquery) documentation.</sup>
56
57 ## What can selectors be used for?
58
59 If you're writing custom ESLint rules, you might be interested in using selectors to examine specific parts of the AST. If you're configuring ESLint for your codebase, you might be interested in restricting particular syntax patterns with selectors.
60
61 ### Listening for selectors in rules
62
63 When writing a custom ESLint rule, you can listen for nodes that match a particular selector as the AST is traversed.
64
65 ```js
66 module.exports = {
67 create(context) {
68 // ...
69
70 return {
71
72 // This listener will be called for all IfStatement nodes with blocks.
73 "IfStatement > BlockStatement": function(blockStatementNode) {
74 // ...your logic here
75 },
76
77 // This listener will be called for all function declarations with more than 3 parameters.
78 "FunctionDeclaration[params.length>3]": function(functionDeclarationNode) {
79 // ...your logic here
80 }
81 };
82 }
83 };
84 ```
85
86 Adding `:exit` to the end of a selector will cause the listener to be called when the matching nodes are exited during traversal, rather than when they are entered.
87
88 If two or more selectors match the same node, their listeners will be called in order of increasing specificity. The specificity of an AST selector is similar to the specificity of a CSS selector:
89
90 * When comparing two selectors, the selector that contains more class selectors, attribute selectors, and pseudo-class selectors (excluding `:not()`) has higher specificity.
91 * If the class/attribute/pseudo-class count is tied, the selector that contains more node type selectors has higher specificity.
92
93 If multiple selectors have equal specificity, their listeners will be called in alphabetical order for that node.
94
95 ### Restricting syntax with selectors
96
97 With the [no-restricted-syntax](/docs/rules/no-restricted-syntax) rule, you can restrict the usage of particular syntax in your code. For example, you can use the following configuration to disallow using `if` statements that do not have block statements as their body:
98
99 ```json
100 {
101 "rules": {
102 "no-restricted-syntax": ["error", "IfStatement > :not(BlockStatement).consequent"]
103 }
104 }
105 ```
106
107 ...or equivalently, you can use this configuration:
108
109 ```json
110 {
111 "rules": {
112 "no-restricted-syntax": ["error", "IfStatement[consequent.type!='BlockStatement']"]
113 }
114 }
115 ```
116
117 As another example, you can disallow calls to `require()`:
118
119 ```json
120 {
121 "rules": {
122 "no-restricted-syntax": ["error", "CallExpression[callee.name='require']"]
123 }
124 }
125 ```
126
127 Or you can enforce that calls to `setTimeout` always have two arguments:
128
129 ```json
130 {
131 "rules": {
132 "no-restricted-syntax": ["error", "CallExpression[callee.name='setTimeout'][arguments.length!=2]"]
133 }
134 }
135 ```
136
137 Using selectors in the `no-restricted-syntax` rule can give you a lot of control over problematic patterns in your codebase, without needing to write custom rules to detect each pattern.
138
139 ### Known issues
140
141 Due to a [bug](https://github.com/estools/esquery/issues/68) in [esquery](https://github.com/estools/esquery), regular expressions that contain a forward-slash character `/` aren't properly parsed, so `[value=/some\/path/]` will be a syntax error. As a [workaround](https://github.com/estools/esquery/issues/68), you can replace the `/` character with its unicode counterpart, like so: `[value=/some\\u002Fpath/]`.