11import assert from 'node:assert' ;
22import { describe , it , vi } from 'vitest' ;
3- import { getTokens } from '../src-js/plugins/tokens.js' ;
3+ import { getTokens , getTokenBefore } from '../src-js/plugins/tokens.js' ;
44import 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
88vi . 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
1725describe ( '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