Skip to content

Commit f27e6c8

Browse files
committed
feat(linter/plugins): implement SourceCode#getTokenBefore()
1 parent 15365c9 commit f27e6c8

File tree

1 file changed

+94
-12
lines changed

1 file changed

+94
-12
lines changed

apps/oxlint/test/tokens.test.ts

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import assert from 'node:assert';
22
import { describe, it, vi } from 'vitest';
3-
import { getTokens } from '../src-js/plugins/tokens.js';
3+
import { getTokens, getTokenBefore } from '../src-js/plugins/tokens.js';
44
import type { Node } from '../src-js/plugins/types.js';
55

6-
let sourceText = 'null;';
6+
let sourceText = '/*A*/var answer/*B*/=/*C*/a/*D*/* b/*E*///F\n call();\n/*Z*/';
77

88
vi.mock('../src-js/plugins/source_code.ts', () => {
99
return {
@@ -13,18 +13,16 @@ vi.mock('../src-js/plugins/source_code.ts', () => {
1313
};
1414
});
1515

16+
// TODO: We are lying about `Program`'s range here.
17+
// The range provided by `@typescript-eslint/typescript-estree` does not match the assertions for that of `espree`.
18+
// The deviation is being corrected in upcoming releases of ESLint and TS-ESLint.
19+
// https://eslint.org/blog/2025/10/whats-coming-in-eslint-10.0.0/#updates-to-program-ast-node-range-coverage
20+
// https://github.com/typescript-eslint/typescript-eslint/issues/11026#issuecomment-3421887632
21+
const Program = { range: [5, 55] } as Node;
22+
const BinaryExpression = { range: [26, 35] } as Node;
23+
1624
// https://github.com/eslint/eslint/blob/v9.39.1/tests/lib/languages/js/source-code/token-store.js#L62
1725
describe('when calling getTokens', () => {
18-
sourceText = '/*A*/var answer/*B*/=/*C*/a/*D*/* b/*E*///F\n call();\n/*Z*/';
19-
20-
// TODO: We are lying about `Program`'s range here.
21-
// The range provided by `@typescript-eslint/typescript-estree` does not match the assertions for that of `espree`.
22-
// The deviation is being corrected in upcoming releases of ESLint and TS-ESLint.
23-
// https://eslint.org/blog/2025/10/whats-coming-in-eslint-10.0.0/#updates-to-program-ast-node-range-coverage
24-
// https://github.com/typescript-eslint/typescript-eslint/issues/11026#issuecomment-3421887632
25-
const Program = { range: [5, 55] } as Node;
26-
const BinaryExpression = { range: [26, 35] } as Node;
27-
2826
it('should retrieve all tokens for root node', () => {
2927
assert.deepStrictEqual(
3028
getTokens(Program).map((token) => token.value),
@@ -104,3 +102,87 @@ describe('when calling getTokens', () => {
104102
);
105103
});
106104
});
105+
106+
describe('when calling getTokenBefore', () => {
107+
it('should retrieve one token before a node', () => {
108+
assert.strictEqual(getTokenBefore(BinaryExpression)!.value, '=');
109+
});
110+
111+
it('should skip a given number of tokens', () => {
112+
assert.strictEqual(getTokenBefore(BinaryExpression, 1)!.value, 'answer');
113+
assert.strictEqual(getTokenBefore(BinaryExpression, 2)!.value, 'var');
114+
});
115+
116+
it('should skip a given number of tokens with skip option', () => {
117+
assert.strictEqual(getTokenBefore(BinaryExpression, { skip: 1 })!.value, 'answer');
118+
assert.strictEqual(getTokenBefore(BinaryExpression, { skip: 2 })!.value, 'var');
119+
});
120+
121+
it('should retrieve matched token with filter option', () => {
122+
assert.strictEqual(getTokenBefore(BinaryExpression, (t) => t.value !== '=')!.value, 'answer');
123+
});
124+
125+
it('should retrieve matched token with skip and filter options', () => {
126+
assert.strictEqual(
127+
getTokenBefore(BinaryExpression, {
128+
skip: 1,
129+
filter: (t) => t.value !== '=',
130+
})!.value,
131+
'var',
132+
);
133+
});
134+
135+
it('should retrieve one token or comment before a node with includeComments option', () => {
136+
assert.strictEqual(
137+
getTokenBefore(BinaryExpression, {
138+
includeComments: true,
139+
})!.value,
140+
'C',
141+
);
142+
});
143+
144+
it('should retrieve one token or comment before a node with includeComments and skip options', () => {
145+
assert.strictEqual(
146+
getTokenBefore(BinaryExpression, {
147+
includeComments: true,
148+
skip: 1,
149+
})!.value,
150+
'=',
151+
);
152+
});
153+
154+
it('should retrieve one token or comment before a node with includeComments and skip and filter options', () => {
155+
assert.strictEqual(
156+
getTokenBefore(BinaryExpression, {
157+
includeComments: true,
158+
skip: 1,
159+
filter: (t) => t.type.startsWith('Block'),
160+
})!.value,
161+
'B',
162+
);
163+
});
164+
165+
it('should retrieve the previous node if the comment at the end of source code is specified.', () => {
166+
sourceText = 'a + b /*comment*/';
167+
// TODO: the manual range should be replaced with `ast.comments[0]`
168+
const token = getTokenBefore({ range: [6, 14] } as Node);
169+
170+
assert.strictEqual(token!.value, 'b');
171+
});
172+
173+
it('should retrieve the previous comment if the first token is specified.', () => {
174+
sourceText = '/*comment*/ a + b';
175+
// TODO: the manual range should be replaced with `ast.tokens[0]`
176+
const token = getTokenBefore({ range: [0, 11] } as Node, { includeComments: true });
177+
178+
assert.strictEqual(token!.value, 'comment');
179+
});
180+
181+
it('should retrieve null if the first comment is specified.', () => {
182+
sourceText = '/*comment*/ a + b';
183+
// TODO: the manual range should be replaced with `ast.comments[0]`
184+
const token = getTokenBefore({ range: [12, 13] } as Node, { includeComments: true });
185+
186+
assert.strictEqual(token, null);
187+
});
188+
});

0 commit comments

Comments
 (0)