Skip to content

Commit ba93ffc

Browse files
committed
feat(linter/plugins): add SourceCode#tokensAndComments getter (#16503)
Add a getter `tokensAndComments` to `SourceCode`. ESLint has this property, but it's undocumented, but many of ESLint's built-in rules rely on it. Probably other ESLint plugins rely on it too.
1 parent 3a49158 commit ba93ffc

File tree

4 files changed

+72
-6
lines changed

4 files changed

+72
-6
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import {
1919
import { resetScopeManager, SCOPE_MANAGER } from "./scope.ts";
2020
import * as scopeMethods from "./scope.ts";
2121
import { resetTokens } from "./tokens.ts";
22+
import { tokens, tokensAndComments, initTokens, initTokensAndComments } from "./tokens.ts";
2223
import * as tokenMethods from "./tokens.ts";
2324
import { debugAssertIsNonNull } from "../utils/asserts.ts";
2425

2526
import type { Program } from "../generated/types.d.ts";
2627
import type { Ranged } from "./location.ts";
28+
import type { Token, CommentToken } from "./tokens.ts";
2729
import type { BufferWithArrays, Node } from "./types.ts";
2830
import type { ScopeManager } from "./scope.ts";
2931

@@ -178,6 +180,22 @@ export const SOURCE_CODE = Object.freeze({
178180
return lines;
179181
},
180182

183+
/**
184+
* Array of all tokens and comments in the file, in source order.
185+
*/
186+
// This property is present in ESLint's `SourceCode`, but is undocumented
187+
get tokensAndComments(): (Token | CommentToken)[] {
188+
if (tokensAndComments === null) {
189+
if (tokens === null) {
190+
if (sourceText === null) initSourceText();
191+
initTokens();
192+
}
193+
initTokensAndComments();
194+
}
195+
debugAssertIsNonNull(tokensAndComments);
196+
return tokensAndComments;
197+
},
198+
181199
/**
182200
* Get the source code for the given node.
183201
* @param node? - The AST node to get the text for.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ const INCLUDE_COMMENTS_SKIP_OPTIONS: SkipOptions = { includeComments: true, skip
135135
// Created lazily only when needed.
136136
export let tokens: Token[] | null = null;
137137
let comments: CommentToken[] | null = null;
138-
let tokensAndComments: (Token | CommentToken)[] | null = null;
138+
export let tokensAndComments: (Token | CommentToken)[] | null = null;
139139

140140
// TS-ESLint `parse` method.
141141
// Lazy-loaded only when needed, as it's a lot of code.
@@ -178,7 +178,7 @@ export function initTokens() {
178178
* Caller must ensure `tokens` and `comments` are initialized before calling this function,
179179
* by calling `initTokens()` if `tokens === null`.
180180
*/
181-
function initTokensAndComments() {
181+
export function initTokensAndComments() {
182182
debugAssertIsNonNull(tokens);
183183
debugAssertIsNonNull(comments);
184184

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,33 @@
2525
8 | `-> // Trailing comment
2626
`----
2727
28-
Found 0 warnings and 1 error.
28+
x tokens-plugin(tokens): Tokens and comments:
29+
| Line loc=1:0-1:18 range=0-18 " Leading comment"
30+
| Keyword loc=3:0-3:3 range=20-23 "let"
31+
| Identifier loc=3:4-3:5 range=24-25 "x"
32+
| Punctuator loc=3:6-3:7 range=26-27 "="
33+
| Block loc=3:8-3:28 range=28-48 " inline comment "
34+
| Numeric loc=3:29-3:30 range=49-50 "1"
35+
| Punctuator loc=3:30-3:31 range=50-51 ";"
36+
| Line loc=5:0-5:18 range=53-71 " Another comment"
37+
| Keyword loc=6:0-6:3 range=72-75 "let"
38+
| Identifier loc=6:4-6:5 range=76-77 "y"
39+
| Punctuator loc=6:6-6:7 range=78-79 "="
40+
| Numeric loc=6:8-6:9 range=80-81 "2"
41+
| Punctuator loc=6:9-6:10 range=81-82 ";"
42+
| Line loc=8:0-8:19 range=84-103 " Trailing comment"
43+
,-[files/index.js:1:1]
44+
1 | ,-> // Leading comment
45+
2 | |
46+
3 | | let x = /* inline comment */ 1;
47+
4 | |
48+
5 | | // Another comment
49+
6 | | let y = 2;
50+
7 | |
51+
8 | `-> // Trailing comment
52+
`----
53+
54+
Found 0 warnings and 2 errors.
2955
Finished in Xms on 1 file using X threads.
3056
```
3157

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import type { Plugin, Rule } from "#oxlint";
22

33
const rule: Rule = {
44
create(context) {
5-
const { sourceCode } = context,
6-
{ ast } = sourceCode;
5+
const { sourceCode } = context;
76

8-
// Note: Comments should not appear in `ast.tokens`
7+
// Get this first to check it works before `sourceText` or `ast` are accessed
8+
const { tokensAndComments } = sourceCode;
9+
10+
const { ast } = sourceCode;
11+
12+
// `ast.tokens` does not include comments
913
context.report({
1014
message:
1115
`Tokens:\n` +
@@ -23,6 +27,24 @@ const rule: Rule = {
2327
node: { range: [0, sourceCode.text.length] },
2428
});
2529

30+
// `sourceCode.tokensAndComments` does include comments
31+
context.report({
32+
message:
33+
`Tokens and comments:\n` +
34+
tokensAndComments
35+
.map(
36+
({ type, loc, range, value }) =>
37+
`${type.padEnd(17)} ` +
38+
`loc=${loc.start.line}:${loc.start.column}-${loc.end.line}:${loc.end.column} `.padEnd(
39+
16,
40+
) +
41+
`range=${range[0]}-${range[1]} `.padEnd(10) +
42+
`"${value}"`,
43+
)
44+
.join("\n"),
45+
node: { range: [0, sourceCode.text.length] },
46+
});
47+
2648
return {};
2749
},
2850
};

0 commit comments

Comments
 (0)