Skip to content

Commit 484b6ca

Browse files
authored
feat: make TextSourceCodeBase a generic type (#182)
1 parent fd9b571 commit 484b6ca

File tree

3 files changed

+86
-12
lines changed

3 files changed

+86
-12
lines changed

packages/core/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ interface InlineConfigElement {
895895
/**
896896
* Generic options for the `SourceCodeBase` type.
897897
*/
898-
interface SourceCodeBaseTypeOptions {
898+
export interface SourceCodeBaseTypeOptions {
899899
LangOptions: LanguageOptions;
900900
RootNode: unknown;
901901
SyntaxElementWithLoc: unknown;

packages/plugin-kit/src/source-code.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@
1111

1212
/** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
1313
/** @typedef {import("@eslint/core").CallTraversalStep} CallTraversalStep */
14-
/** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
1514
/** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
1615
/** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
1716
/** @typedef {import("@eslint/core").SourceLocationWithOffset} SourceLocationWithOffset */
1817
/** @typedef {import("@eslint/core").SourceRange} SourceRange */
1918
/** @typedef {import("@eslint/core").Directive} IDirective */
2019
/** @typedef {import("@eslint/core").DirectiveType} DirectiveType */
20+
/** @typedef {import("@eslint/core").SourceCodeBaseTypeOptions} SourceCodeBaseTypeOptions */
21+
/**
22+
* @typedef {import("@eslint/core").TextSourceCode<Options>} TextSourceCode<Options>
23+
* @template {SourceCodeBaseTypeOptions} [Options=SourceCodeBaseTypeOptions]
24+
*/
2125

2226
//-----------------------------------------------------------------------------
2327
// Helpers
@@ -212,7 +216,8 @@ export class Directive {
212216

213217
/**
214218
* Source Code Base Object
215-
* @implements {TextSourceCode}
219+
* @template {SourceCodeBaseTypeOptions & {SyntaxElementWithLoc: object}} [Options=SourceCodeBaseTypeOptions & {SyntaxElementWithLoc: object}]
220+
* @implements {TextSourceCode<Options>}
216221
*/
217222
export class TextSourceCodeBase {
218223
/**
@@ -223,7 +228,7 @@ export class TextSourceCodeBase {
223228

224229
/**
225230
* The AST of the source code.
226-
* @type {object}
231+
* @type {Options['RootNode']}
227232
*/
228233
ast;
229234

@@ -237,7 +242,7 @@ export class TextSourceCodeBase {
237242
* Creates a new instance.
238243
* @param {Object} options The options for the instance.
239244
* @param {string} options.text The source code text.
240-
* @param {object} options.ast The root AST node.
245+
* @param {Options['RootNode']} options.ast The root AST node.
241246
* @param {RegExp} [options.lineEndingPattern] The pattern to match lineEndings in the source code.
242247
*/
243248
constructor({ text, ast, lineEndingPattern = /\r?\n/u }) {
@@ -248,7 +253,7 @@ export class TextSourceCodeBase {
248253

249254
/**
250255
* Returns the loc information for the given node or token.
251-
* @param {object} nodeOrToken The node or token to get the loc information for.
256+
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the loc information for.
252257
* @returns {SourceLocation} The loc information for the node or token.
253258
*/
254259
getLoc(nodeOrToken) {
@@ -267,7 +272,7 @@ export class TextSourceCodeBase {
267272

268273
/**
269274
* Returns the range information for the given node or token.
270-
* @param {object} nodeOrToken The node or token to get the range information for.
275+
* @param {Options['SyntaxElementWithLoc']} nodeOrToken The node or token to get the range information for.
271276
* @returns {SourceRange} The range information for the node or token.
272277
*/
273278
getRange(nodeOrToken) {
@@ -290,8 +295,8 @@ export class TextSourceCodeBase {
290295
/* eslint-disable no-unused-vars -- Required to complete interface. */
291296
/**
292297
* Returns the parent of the given node.
293-
* @param {object} node The node to get the parent of.
294-
* @returns {object|undefined} The parent of the node.
298+
* @param {Options['SyntaxElementWithLoc']} node The node to get the parent of.
299+
* @returns {Options['SyntaxElementWithLoc']|undefined} The parent of the node.
295300
*/
296301
getParent(node) {
297302
throw new Error("Not implemented.");
@@ -300,8 +305,8 @@ export class TextSourceCodeBase {
300305

301306
/**
302307
* Gets all the ancestors of a given node
303-
* @param {object} node The node
304-
* @returns {Array<object>} All the ancestor nodes in the AST, not including the provided node, starting
308+
* @param {Options['SyntaxElementWithLoc']} node The node
309+
* @returns {Array<Options['SyntaxElementWithLoc']>} All the ancestor nodes in the AST, not including the provided node, starting
305310
* from the root node at index 0 and going inwards to the parent node.
306311
* @throws {TypeError} When `node` is missing.
307312
*/
@@ -325,7 +330,7 @@ export class TextSourceCodeBase {
325330

326331
/**
327332
* Gets the source code for the given node.
328-
* @param {object} [node] The AST node to get the text for.
333+
* @param {Options['SyntaxElementWithLoc']} [node] The AST node to get the text for.
329334
* @param {number} [beforeCount] The number of characters before the node to retrieve.
330335
* @param {number} [afterCount] The number of characters after the node to retrieve.
331336
* @returns {string} The text representing the AST node.

packages/plugin-kit/tests/types/types.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,82 @@ const sourceCode = new TestTextSourceCode({
8383
ast: { foo: "ABC", bar: 123 },
8484
});
8585
sourceCode.ast satisfies { foo: string; bar: number };
86+
sourceCode.text satisfies string;
87+
sourceCode.lines satisfies string[];
8688
sourceCode.getAncestors({}) satisfies object[];
8789
sourceCode.getLoc({}) satisfies SourceLocation;
8890
sourceCode.getParent({}) satisfies object | undefined;
8991
sourceCode.getRange({}) satisfies SourceRange;
9092
sourceCode.getText() satisfies string;
9193
sourceCode.getText({}, 0, 1) satisfies string;
9294

95+
// TextSourceCodeBase (with options)
96+
interface CustomOptions {
97+
LangOptions: { option1: string; option2: boolean };
98+
RootNode: { type: string };
99+
SyntaxElementWithLoc: { value: string };
100+
ConfigNode: { config: string };
101+
}
102+
class TestTextSourceCodeWithOptions extends TextSourceCodeBase<CustomOptions> {
103+
declare ast: CustomOptions["RootNode"];
104+
105+
constructor({
106+
text,
107+
ast,
108+
}: {
109+
text: string;
110+
ast: CustomOptions["RootNode"];
111+
}) {
112+
super({ text, ast });
113+
}
114+
}
115+
116+
/* eslint-disable no-new -- Needed to test the constructor. */
117+
new TestTextSourceCodeWithOptions({
118+
// @ts-expect-error Wrong type should be caught
119+
text: 1,
120+
// @ts-expect-error Wrong type should be caught
121+
ast: { type: true },
122+
});
123+
new TestTextSourceCodeWithOptions({
124+
// @ts-expect-error Wrong type should be caught
125+
text: true,
126+
// @ts-expect-error Wrong type should be caught
127+
ast: { unknown: true },
128+
});
129+
/* eslint-enable no-new -- Constructor test ends here. */
130+
131+
const sourceCodeWithOptions = new TestTextSourceCodeWithOptions({
132+
text: "text",
133+
ast: { type: "customRootNode" },
134+
});
135+
sourceCodeWithOptions.ast satisfies {
136+
type: string;
137+
} satisfies CustomOptions["RootNode"];
138+
sourceCodeWithOptions.text satisfies string;
139+
sourceCodeWithOptions.lines satisfies string[];
140+
sourceCodeWithOptions.getAncestors({ value: "" }) satisfies {
141+
value: string;
142+
}[] satisfies CustomOptions["SyntaxElementWithLoc"][];
143+
sourceCodeWithOptions.getLoc({ value: "" }) satisfies SourceLocation;
144+
sourceCodeWithOptions.getParent({ value: "" }) satisfies
145+
| { value: string }
146+
| undefined satisfies CustomOptions["SyntaxElementWithLoc"] | undefined;
147+
sourceCodeWithOptions.getRange({ value: "" }) satisfies SourceRange;
148+
sourceCodeWithOptions.getText() satisfies string;
149+
sourceCodeWithOptions.getText({ value: "" }, 0, 1) satisfies string;
150+
151+
// @ts-expect-error Wrong type should be caught
152+
sourceCodeWithOptions.getAncestors({});
153+
// @ts-expect-error Wrong type should be caught
154+
sourceCodeWithOptions.getLoc({});
155+
// @ts-expect-error Wrong type should be caught
156+
sourceCodeWithOptions.getParent({});
157+
// @ts-expect-error Wrong type should be caught
158+
sourceCodeWithOptions.getRange({});
159+
// @ts-expect-error Wrong type should be caught
160+
sourceCodeWithOptions.getText({}, 0, 1);
161+
93162
// VisitNodeStep
94163
class TestVisitNodeStep extends VisitNodeStep {
95164
constructor({ target, phase }: { target: object; phase: 1 | 2 }) {

0 commit comments

Comments
 (0)