]> git.proxmox.com Git - pve-eslint.git/blob - eslint/docs/developer-guide/selectors.md
78bb260842662bb3517b69bb1b4312f33881e8be
[pve-eslint.git] / eslint / docs / developer-guide / selectors.md
1 # Selectors
2
3 Some rules and APIs allow the use of selectors to query an AST. This page is intended to:
4
5 1. Explain what selectors are
6 1. Describe the syntax for creating selectors
7 1. Describe what selectors can be used for
8
9 ## What is a selector?
10
11 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.
12
13 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.
14
15 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:
16
17 ```js
18 var foo = 1;
19 bar.baz();
20 ```
21
22 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`.
23
24 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`.
25
26 ## What syntax can selectors have?
27
28 The following selectors are supported:
29
30 * AST node type: `ForStatement`
31 * wildcard (matches all nodes): `*`
32 * attribute existence: `[attr]`
33 * attribute value: `[attr="foo"]` or `[attr=123]`
34 * attribute regex: `[attr=/foo.*/]` <sub>(with some [known issues](#known-issues))</sub>
35 * attribute conditions: `[attr!="foo"]`, `[attr>2]`, `[attr<3]`, `[attr>=2]`, or `[attr<=3]`
36 * nested attribute: `[attr.level2="foo"]`
37 * field: `FunctionDeclaration > Identifier.id`
38 * First or last child: `:first-child` or `:last-child`
39 * nth-child (no ax+b support): `:nth-child(2)`
40 * nth-last-child (no ax+b support): `:nth-last-child(1)`
41 * descendant: `FunctionExpression ReturnStatement`
42 * child: `UnaryExpression > Literal`
43 * following sibling: `VariableDeclaration ~ VariableDeclaration`
44 * adjacent sibling: `ArrayExpression > Literal + SpreadElement`
45 * negation: `:not(ForStatement)`
46 * matches-any: `:matches([attr] > :first-child, :last-child)`
47 * class of AST node: `:statement`, `:expression`, `:declaration`, `:function`, or `:pattern`
48
49 This syntax is very powerful, and can be used to precisely select many syntactic patterns in your code.
50
51 <sup>The examples in this section were adapted from the [esquery](https://github.com/estools/esquery) documentation.</sup>
52
53 ## What can selectors be used for?
54
55 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.
56
57 ### Listening for selectors in rules
58
59 When writing a custom ESLint rule, you can listen for nodes that match a particular selector as the AST is traversed.
60
61 ```js
62 module.exports = {
63 create(context) {
64 // ...
65
66 return {
67
68 // This listener will be called for all IfStatement nodes with blocks.
69 "IfStatement > BlockStatement": function(blockStatementNode) {
70 // ...your logic here
71 },
72
73 // This listener will be called for all function declarations with more than 3 parameters.
74 "FunctionDeclaration[params.length>3]": function(functionDeclarationNode) {
75 // ...your logic here
76 }
77 };
78 }
79 };
80 ```
81
82 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.
83
84 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:
85
86 * When comparing two selectors, the selector that contains more class selectors, attribute selectors, and pseudo-class selectors (excluding `:not()`) has higher specificity.
87 * If the class/attribute/pseudo-class count is tied, the selector that contains more node type selectors has higher specificity.
88
89 If multiple selectors have equal specificity, their listeners will be called in alphabetical order for that node.
90
91 ### Restricting syntax with selectors
92
93 With the [no-restricted-syntax](/docs/rules/no-restricted-syntax.md) 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:
94
95 ```json
96 {
97 "rules": {
98 "no-restricted-syntax": ["error", "IfStatement > :not(BlockStatement).consequent"]
99 }
100 }
101 ```
102
103 ...or equivalently, you can use this configuration:
104
105 ```json
106 {
107 "rules": {
108 "no-restricted-syntax": ["error", "IfStatement[consequent.type!='BlockStatement']"]
109 }
110 }
111 ```
112
113 As another example, you can disallow calls to `require()`:
114
115 ```json
116 {
117 "rules": {
118 "no-restricted-syntax": ["error", "CallExpression[callee.name='require']"]
119 }
120 }
121 ```
122
123 Or you can enforce that calls to `setTimeout` always have two arguments:
124
125 ```json
126 {
127 "rules": {
128 "no-restricted-syntax": ["error", "CallExpression[callee.name='setTimeout'][arguments.length!=2]"]
129 }
130 }
131 ```
132
133 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.
134
135 ### Known issues
136
137 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/]`.