Skip to content

Commit 65e4733

Browse files
committed
feat(linter/plugins): implement deprecated SourceCode tokens methods
1 parent 197f074 commit 65e4733

File tree

5 files changed

+197
-8
lines changed

5 files changed

+197
-8
lines changed

apps/oxlint/src-js/plugins/source_code.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,10 @@ export const SOURCE_CODE = Object.freeze({
226226
getLastToken: tokenMethods.getLastToken,
227227
getLastTokens: tokenMethods.getLastTokens,
228228
getTokenBefore: tokenMethods.getTokenBefore,
229+
getTokenOrCommentBefore: tokenMethods.getTokenOrCommentBefore,
229230
getTokensBefore: tokenMethods.getTokensBefore,
230231
getTokenAfter: tokenMethods.getTokenAfter,
232+
getTokenOrCommentAfter: tokenMethods.getTokenOrCommentAfter,
231233
getTokensAfter: tokenMethods.getTokensAfter,
232234
getTokensBetween: tokenMethods.getTokensBetween,
233235
getFirstTokenBetween: tokenMethods.getFirstTokenBetween,
@@ -236,6 +238,7 @@ export const SOURCE_CODE = Object.freeze({
236238
getLastTokensBetween: tokenMethods.getLastTokensBetween,
237239
getTokenByRangeStart: tokenMethods.getTokenByRangeStart,
238240
isSpaceBetween: tokenMethods.isSpaceBetween,
241+
isSpaceBetweenTokens: tokenMethods.isSpaceBetweenTokens,
239242
});
240243

241244
export type SourceCode = typeof SOURCE_CODE;

apps/oxlint/src-js/plugins/tokens.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,24 @@ export function getTokenBefore(
139139
}
140140
/* oxlint-enable no-unused-vars */
141141

142+
/**
143+
* Get the token that precedes a given node or token.
144+
*
145+
* @deprecated Use `sourceCode.getTokenBefore` with `includeComments: true` instead.
146+
*
147+
* @param nodeOrToken The AST node or token.
148+
* @param skip - Number of tokens to skip.
149+
* @returns `Token`, or `null` if all were skipped.
150+
*/
151+
/* oxlint-disable no-unused-vars */
152+
export function getTokenOrCommentBefore(nodeOrToken: NodeOrToken | Comment, skip?: number): Token | null {
153+
// TODO: Implement equivalent of:
154+
// `return getTokenBefore(nodeOrToken, { includeComments: true, skip });`
155+
// But could use a const object at top level for options object, to avoid creating temporary object on each call.
156+
throw new Error('`sourceCode.getTokenOrCommentBefore` not implemented yet');
157+
}
158+
/* oxlint-enable no-unused-vars */
159+
142160
/**
143161
* Get the tokens that precede a given node or token.
144162
* @param nodeOrToken - The AST node or token.
@@ -169,6 +187,24 @@ export function getTokenAfter(
169187
}
170188
/* oxlint-enable no-unused-vars */
171189

190+
/**
191+
* Get the token that follows a given node or token.
192+
*
193+
* @deprecated Use `sourceCode.getTokenAfter` with `includeComments: true` instead.
194+
*
195+
* @param nodeOrToken The AST node or token.
196+
* @param skip - Number of tokens to skip.
197+
* @returns `Token`, or `null` if all were skipped.
198+
*/
199+
/* oxlint-disable no-unused-vars */
200+
export function getTokenOrCommentAfter(nodeOrToken: NodeOrToken | Comment, skip?: number): Token | null {
201+
// TODO: Implement equivalent of:
202+
// `return getTokenAfter(nodeOrToken, { includeComments: true, skip });`
203+
// But could use a const object at top level for options object, to avoid creating temporary object on each call.
204+
throw new Error('`sourceCode.getTokenOrCommentAfter` not implemented yet');
205+
}
206+
/* oxlint-enable no-unused-vars */
207+
172208
/**
173209
* Get the tokens that follow a given node or token.
174210
* @param nodeOrToken - The AST node or token.
@@ -346,3 +382,29 @@ export function isSpaceBetween(nodeOrToken1: NodeOrToken, nodeOrToken2: NodeOrTo
346382

347383
return WHITESPACE_REGEXP.test(sourceText.slice(gapStart, gapEnd));
348384
}
385+
386+
/**
387+
* Determine if two nodes or tokens have at least one whitespace character between them.
388+
* Order does not matter.
389+
*
390+
* Returns `false` if the given nodes or tokens overlap.
391+
*
392+
* Checks for whitespace *between tokens*, not including whitespace *inside tokens*.
393+
* e.g. Returns `false` for `isSpaceBetween(x, y)` in `x+" "+y`.
394+
*
395+
* Unlike `SourceCode#isSpaceBetween`, this function does return `true` if there is a `JSText` token between the two
396+
* input tokens, and it contains whitespace.
397+
* e.g. Returns `true` for `isSpaceBetweenTokens(x, slash)` in `<X>a b</X>`.
398+
*
399+
* @deprecated Use `sourceCode.isSpaceBetween` instead.
400+
*
401+
* TODO: Implementation is not quite right at present, for same reasons as `SourceCode#isSpaceBetween`.
402+
*
403+
* @param nodeOrToken1 - The first node or token to check between.
404+
* @param nodeOrToken2 - The second node or token to check between.
405+
* @returns `true` if there is a whitespace character between
406+
* any of the tokens found between the two given nodes or tokens.
407+
*/
408+
export function isSpaceBetweenTokens(token1: NodeOrToken, token2: NodeOrToken): boolean {
409+
return isSpaceBetween(token1, token2);
410+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Foo>aaa</Foo>;
2+
3+
// We should return `false` for `isSpaceBetween(openingElement, closingElement)`, but we currently return `true`
4+
<Bar>b c</Bar>;
5+
6+
// We should return `false` for `isSpaceBetween(openingElement, closingElement)`, but we currently return `true`
7+
<Qux>
8+
d
9+
e
10+
</Qux>;

apps/oxlint/test/fixtures/isSpaceBetween/output.snap.md

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@
55
```
66
x test-plugin(is-space-between):
77
| isSpaceBetween(left, right): false
8+
| isSpaceBetweenTokens(left, right): false
89
| isSpaceBetween(right, left): false
10+
| isSpaceBetweenTokens(right, left): false
911
| isSpaceBetween(left, node): false
12+
| isSpaceBetweenTokens(left, node): false
1013
| isSpaceBetween(node, left): false
14+
| isSpaceBetweenTokens(node, left): false
1115
| isSpaceBetween(right, node): false
16+
| isSpaceBetweenTokens(right, node): false
1217
| isSpaceBetween(node, right): false
18+
| isSpaceBetweenTokens(node, right): false
1319
,-[files/index.js:2:1]
1420
1 | // prettier-ignore
1521
2 | noSpace=1;
@@ -19,7 +25,9 @@
1925
2026
x test-plugin(is-space-between):
2127
| isSpaceBetween(leftExtended, right): false
28+
| isSpaceBetweenTokens(leftExtended, right): false
2229
| isSpaceBetween(right, leftExtended): false
30+
| isSpaceBetweenTokens(right, leftExtended): false
2331
,-[files/index.js:2:1]
2432
1 | // prettier-ignore
2533
2 | noSpace=1;
@@ -29,11 +37,17 @@
2937
3038
x test-plugin(is-space-between):
3139
| isSpaceBetween(left, right): true
40+
| isSpaceBetweenTokens(left, right): true
3241
| isSpaceBetween(right, left): true
42+
| isSpaceBetweenTokens(right, left): true
3343
| isSpaceBetween(left, node): false
44+
| isSpaceBetweenTokens(left, node): false
3445
| isSpaceBetween(node, left): false
46+
| isSpaceBetweenTokens(node, left): false
3547
| isSpaceBetween(right, node): false
48+
| isSpaceBetweenTokens(right, node): false
3649
| isSpaceBetween(node, right): false
50+
| isSpaceBetweenTokens(node, right): false
3751
,-[files/index.js:5:1]
3852
4 | // prettier-ignore
3953
5 | singleSpaceBefore =2;
@@ -43,11 +57,17 @@
4357
4458
x test-plugin(is-space-between):
4559
| isSpaceBetween(left, right): true
60+
| isSpaceBetweenTokens(left, right): true
4661
| isSpaceBetween(right, left): true
62+
| isSpaceBetweenTokens(right, left): true
4763
| isSpaceBetween(left, node): false
64+
| isSpaceBetweenTokens(left, node): false
4865
| isSpaceBetween(node, left): false
66+
| isSpaceBetweenTokens(node, left): false
4967
| isSpaceBetween(right, node): false
68+
| isSpaceBetweenTokens(right, node): false
5069
| isSpaceBetween(node, right): false
70+
| isSpaceBetweenTokens(node, right): false
5171
,-[files/index.js:8:1]
5272
7 | // prettier-ignore
5373
8 | singleSpaceAfter= 3;
@@ -57,11 +77,17 @@
5777
5878
x test-plugin(is-space-between):
5979
| isSpaceBetween(left, right): true
80+
| isSpaceBetweenTokens(left, right): true
6081
| isSpaceBetween(right, left): true
82+
| isSpaceBetweenTokens(right, left): true
6183
| isSpaceBetween(left, node): false
84+
| isSpaceBetweenTokens(left, node): false
6285
| isSpaceBetween(node, left): false
86+
| isSpaceBetweenTokens(node, left): false
6387
| isSpaceBetween(right, node): false
88+
| isSpaceBetweenTokens(right, node): false
6489
| isSpaceBetween(node, right): false
90+
| isSpaceBetweenTokens(node, right): false
6591
,-[files/index.js:11:1]
6692
10 | // prettier-ignore
6793
11 | multipleSpaces = 4;
@@ -71,11 +97,17 @@
7197
7298
x test-plugin(is-space-between):
7399
| isSpaceBetween(left, right): true
100+
| isSpaceBetweenTokens(left, right): true
74101
| isSpaceBetween(right, left): true
102+
| isSpaceBetweenTokens(right, left): true
75103
| isSpaceBetween(left, node): false
104+
| isSpaceBetweenTokens(left, node): false
76105
| isSpaceBetween(node, left): false
106+
| isSpaceBetweenTokens(node, left): false
77107
| isSpaceBetween(right, node): false
108+
| isSpaceBetweenTokens(right, node): false
78109
| isSpaceBetween(node, right): false
110+
| isSpaceBetweenTokens(node, right): false
79111
,-[files/index.js:14:1]
80112
13 | // prettier-ignore
81113
14 | ,-> newlineBefore=
@@ -85,11 +117,17 @@
85117
86118
x test-plugin(is-space-between):
87119
| isSpaceBetween(left, right): true
120+
| isSpaceBetweenTokens(left, right): true
88121
| isSpaceBetween(right, left): true
122+
| isSpaceBetweenTokens(right, left): true
89123
| isSpaceBetween(left, node): false
124+
| isSpaceBetweenTokens(left, node): false
90125
| isSpaceBetween(node, left): false
126+
| isSpaceBetweenTokens(node, left): false
91127
| isSpaceBetween(right, node): false
128+
| isSpaceBetweenTokens(right, node): false
92129
| isSpaceBetween(node, right): false
130+
| isSpaceBetweenTokens(node, right): false
93131
,-[files/index.js:18:1]
94132
17 | // prettier-ignore
95133
18 | ,-> newlineAfter
@@ -99,7 +137,9 @@
99137
100138
x test-plugin(is-space-between):
101139
| isSpaceBetween(node, binaryLeft): false
140+
| isSpaceBetweenTokens(node, binaryLeft): false
102141
| isSpaceBetween(binaryLeft, node): false
142+
| isSpaceBetweenTokens(binaryLeft, node): false
103143
,-[files/index.js:22:1]
104144
21 | // prettier-ignore
105145
22 | nested = 7 + 8;
@@ -109,11 +149,17 @@
109149
110150
x test-plugin(is-space-between):
111151
| isSpaceBetween(left, right): true
152+
| isSpaceBetweenTokens(left, right): true
112153
| isSpaceBetween(right, left): true
154+
| isSpaceBetweenTokens(right, left): true
113155
| isSpaceBetween(left, node): false
156+
| isSpaceBetweenTokens(left, node): false
114157
| isSpaceBetween(node, left): false
158+
| isSpaceBetweenTokens(node, left): false
115159
| isSpaceBetween(right, node): false
160+
| isSpaceBetweenTokens(right, node): false
116161
| isSpaceBetween(node, right): false
162+
| isSpaceBetweenTokens(node, right): false
117163
,-[files/index.js:22:1]
118164
21 | // prettier-ignore
119165
22 | nested = 7 + 8;
@@ -123,15 +169,53 @@
123169
124170
x test-plugin(is-space-between):
125171
| isSpaceBetween(beforeString, afterString): true
172+
| isSpaceBetweenTokens(beforeString, afterString): true
126173
| isSpaceBetween(afterString, beforeString): true
174+
| isSpaceBetweenTokens(afterString, beforeString): true
127175
,-[files/index.js:26:1]
128176
25 | // prettier-ignore
129177
26 | beforeString," ",afterString;
130178
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131179
`----
132180
133-
Found 0 warnings and 10 errors.
134-
Finished in Xms on 1 file using X threads.
181+
x test-plugin(is-space-between):
182+
| isSpaceBetween(openingElement, closingElement): false
183+
| isSpaceBetweenTokens(openingElement, closingElement): false
184+
| isSpaceBetween(closingElement, openingElement): false
185+
| isSpaceBetweenTokens(closingElement, openingElement): false
186+
,-[files/index.jsx:1:1]
187+
1 | <Foo>aaa</Foo>;
188+
: ^^^^^^^^^^^^^^
189+
2 |
190+
`----
191+
192+
x test-plugin(is-space-between):
193+
| isSpaceBetween(openingElement, closingElement): true
194+
| isSpaceBetweenTokens(openingElement, closingElement): true
195+
| isSpaceBetween(closingElement, openingElement): true
196+
| isSpaceBetweenTokens(closingElement, openingElement): true
197+
,-[files/index.jsx:4:1]
198+
3 | // We should return `false` for `isSpaceBetween(openingElement, closingElement)`, but we currently return `true`
199+
4 | <Bar>b c</Bar>;
200+
: ^^^^^^^^^^^^^^
201+
5 |
202+
`----
203+
204+
x test-plugin(is-space-between):
205+
| isSpaceBetween(openingElement, closingElement): true
206+
| isSpaceBetweenTokens(openingElement, closingElement): true
207+
| isSpaceBetween(closingElement, openingElement): true
208+
| isSpaceBetweenTokens(closingElement, openingElement): true
209+
,-[files/index.jsx:7:1]
210+
6 | // We should return `false` for `isSpaceBetween(openingElement, closingElement)`, but we currently return `true`
211+
7 | ,-> <Qux>
212+
8 | | d
213+
9 | | e
214+
10 | `-> </Qux>;
215+
`----
216+
217+
Found 0 warnings and 13 errors.
218+
Finished in Xms on 2 files using X threads.
135219
```
136220

137221
# stderr

apps/oxlint/test/fixtures/isSpaceBetween/plugin.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,26 @@ const testRule: Rule = {
66
create(context) {
77
return {
88
AssignmentExpression(node) {
9-
const { isSpaceBetween } = context.sourceCode,
9+
const { isSpaceBetween, isSpaceBetweenTokens } = context.sourceCode,
1010
{ left, right } = node;
1111

1212
context.report({
1313
message:
1414
'\n' +
1515
// Test where 2 nodes are separated, maybe with whitespace in between
1616
`isSpaceBetween(left, right): ${isSpaceBetween(left, right)}\n` +
17+
`isSpaceBetweenTokens(left, right): ${isSpaceBetweenTokens(left, right)}\n` +
1718
`isSpaceBetween(right, left): ${isSpaceBetween(right, left)}\n` +
19+
`isSpaceBetweenTokens(right, left): ${isSpaceBetweenTokens(right, left)}\n` +
1820
// Test where 1 node is inside another, sharing same `start` or `end`
1921
`isSpaceBetween(left, node): ${isSpaceBetween(left, node)}\n` +
22+
`isSpaceBetweenTokens(left, node): ${isSpaceBetweenTokens(left, node)}\n` +
2023
`isSpaceBetween(node, left): ${isSpaceBetween(node, left)}\n` +
24+
`isSpaceBetweenTokens(node, left): ${isSpaceBetweenTokens(node, left)}\n` +
2125
`isSpaceBetween(right, node): ${isSpaceBetween(right, node)}\n` +
22-
`isSpaceBetween(node, right): ${isSpaceBetween(node, right)}`,
26+
`isSpaceBetweenTokens(right, node): ${isSpaceBetweenTokens(right, node)}\n` +
27+
`isSpaceBetween(node, right): ${isSpaceBetween(node, right)}\n` +
28+
`isSpaceBetweenTokens(node, right): ${isSpaceBetweenTokens(node, right)}`,
2329
node,
2430
});
2531

@@ -30,7 +36,9 @@ const testRule: Rule = {
3036
message:
3137
'\n' +
3238
`isSpaceBetween(node, binaryLeft): ${isSpaceBetween(node, binaryLeft)}\n` +
33-
`isSpaceBetween(binaryLeft, node): ${isSpaceBetween(binaryLeft, node)}`,
39+
`isSpaceBetweenTokens(node, binaryLeft): ${isSpaceBetweenTokens(node, binaryLeft)}\n` +
40+
`isSpaceBetween(binaryLeft, node): ${isSpaceBetween(binaryLeft, node)}\n` +
41+
`isSpaceBetweenTokens(binaryLeft, node): ${isSpaceBetweenTokens(binaryLeft, node)}`,
3442
node,
3543
});
3644
}
@@ -47,22 +55,44 @@ const testRule: Rule = {
4755
message:
4856
'\n' +
4957
`isSpaceBetween(leftExtended, right): ${isSpaceBetween(leftExtended, right)}\n` +
50-
`isSpaceBetween(right, leftExtended): ${isSpaceBetween(right, leftExtended)}`,
58+
`isSpaceBetweenTokens(leftExtended, right): ${isSpaceBetweenTokens(leftExtended, right)}\n` +
59+
`isSpaceBetween(right, leftExtended): ${isSpaceBetween(right, leftExtended)}\n` +
60+
`isSpaceBetweenTokens(right, leftExtended): ${isSpaceBetweenTokens(right, leftExtended)}`,
5161
node,
5262
});
5363
}
5464
},
5565

5666
SequenceExpression(node) {
57-
const { isSpaceBetween } = context.sourceCode,
67+
const { isSpaceBetween, isSpaceBetweenTokens } = context.sourceCode,
5868
[beforeString, , afterString] = node.expressions;
5969

6070
// We get this wrong. Should be `false`, but we get `true`.
6171
context.report({
6272
message:
6373
'\n' +
6474
`isSpaceBetween(beforeString, afterString): ${isSpaceBetween(beforeString, afterString)}\n` +
65-
`isSpaceBetween(afterString, beforeString): ${isSpaceBetween(afterString, beforeString)}`,
75+
`isSpaceBetweenTokens(beforeString, afterString): ${isSpaceBetweenTokens(beforeString, afterString)}\n` +
76+
`isSpaceBetween(afterString, beforeString): ${isSpaceBetween(afterString, beforeString)}\n` +
77+
`isSpaceBetweenTokens(afterString, beforeString): ${isSpaceBetweenTokens(afterString, beforeString)}`,
78+
node,
79+
});
80+
},
81+
82+
JSXElement(node) {
83+
const { isSpaceBetween, isSpaceBetweenTokens } = context.sourceCode,
84+
{ openingElement, closingElement } = node;
85+
86+
// We get this wrong.
87+
// `isSpaceBetween` should return `false` for last 2 `JSXElement`s, but we get `true`.
88+
// `isSpaceBetweenTokens` is correct for all cases.
89+
context.report({
90+
message:
91+
'\n' +
92+
`isSpaceBetween(openingElement, closingElement): ${isSpaceBetween(openingElement, closingElement)}\n` +
93+
`isSpaceBetweenTokens(openingElement, closingElement): ${isSpaceBetweenTokens(openingElement, closingElement)}\n` +
94+
`isSpaceBetween(closingElement, openingElement): ${isSpaceBetween(closingElement, openingElement)}\n` +
95+
`isSpaceBetweenTokens(closingElement, openingElement): ${isSpaceBetweenTokens(closingElement, openingElement)}`,
6696
node,
6797
});
6898
},

0 commit comments

Comments
 (0)