From 19e7a990109efad36f300e6dbbd32d490d1cd177 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Tue, 20 Aug 2024 14:19:29 -0400 Subject: [PATCH 01/12] feat: Add rule types --- packages/core/package.json | 1 + packages/core/src/types.ts | 366 ++++++++++++++++++++++++++++++++++++- 2 files changed, 366 insertions(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index fa7223d..cdf02e5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,6 +36,7 @@ }, "homepage": "https://github.com/eslint/rewrite#readme", "devDependencies": { + "json-schema": "^0.4.0", "typescript": "^5.4.5" }, "engines": { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 45a1116..3784cef 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -2,6 +2,12 @@ * @fileoverview Shared types for ESLint Core. */ +//------------------------------------------------------------------------------ +// Imports +//------------------------------------------------------------------------------ + +import { JSONSchema4 } from "json-schema"; + //------------------------------------------------------------------------------ // Helper Types //------------------------------------------------------------------------------ @@ -75,6 +81,365 @@ export interface PositionWithOffset extends Position { */ export type SourceRange = [number, number]; +//------------------------------------------------------------------------------ +// Rules +//------------------------------------------------------------------------------ + +/** + * What the rule is responsible for finding: + * - `problem` means the rule has noticed a potential error. + * - `suggestion` means the rule suggests an alternate or better approach. + * - `layout` means the rule is looking at spacing, indentation, etc. + */ +export type RuleType = "problem" | "suggestion" | "layout"; + +/** + * The type of fix the rule can provide: + * - `code` means the rule can fix syntax. + * - `whitespace` means the rule can fix spacing and indentation. + */ +export type RuleFixType = "code" | "whitespace"; + +/** + * An object containing visitor information for a rule. Each method is either the + * name of a node type or a selector, or is a method that will be called at specific + * times during the traversal. + */ +export interface RuleVisitor { + /** + * Called for each node in the AST or at specific times during the traversal. + */ + [key: string]: + | ((node: unknown, parent?: unknown) => void) + | ((...unknown: unknown[]) => void); +} + +/** + * Rule meta information used for documentation. + */ +export interface RulesMetaDocs { + /** + * A short description of the rule. + */ + description?: string | undefined; + + /** + * The URL to the documentation for the rule. + */ + url?: string | undefined; + + /** + * The category the rule falls under. + * @deprecated No longer used. + */ + category?: string | undefined; + + /** + * Indicates if the rule is generally recommended for all users. + */ + recommended?: boolean | undefined; +} + +/** + * Meta information about a rule. + */ +export interface RulesMeta { + /** + * Properties that are used when documenting the rule. + */ + docs?: RulesMetaDocs | undefined; + + /** + * The type of rule. + */ + type?: RuleType | undefined; + + /** + * The schema for the rule options. Required if the rule has options. + */ + schema?: JSONSchema4 | JSONSchema4[] | false | undefined; + + /** + * The messages that the rule can report. + */ + messages: Record; + + /** + * The visitor keys for the rule. + */ + visitorKeys?: Record; + + /** + * The deprecated rules for the rule. + */ + deprecated?: boolean | undefined; + + /** + * When a rule is deprecated, indicates the rule ID(s) that should be used instead. + */ + replacedBy?: string[] | undefined; + + /** + * Indicates if the rule is fixable, and if so, what type of fix it provides. + */ + fixable?: RuleFixType | undefined; + + /** + * Indicates if the rule may provide suggestions. + */ + hasSuggestions?: boolean | undefined; +} + +/** + * Represents the context object that is passed to a rule. This object contains + * information about the current state of the linting process and is the rule's + * view into the outside world. + */ +export interface RuleContext { + /** + * The current working directory for the session. + */ + cwd: string; + + /** + * Returns the current working directory for the session. + * @deprecated Use `cwd` instead. + */ + getCwd(): string; + + /** + * The filename of the file being linted. + */ + filename: string; + + /** + * Returns the filename of the file being linted. + * @deprecated Use `filename` instead. + */ + getFilename(): string; + + /** + * The physical filename of the file being linted. + */ + physicalFilename: string; + + /** + * Returns the physical filename of the file being linted. + * @deprecated Use `physicalFilename` instead. + */ + getPhysicalFilename(): string; + + /** + * The source code object that the rule is running on. + */ + sourceCode: SourceCode; + + /** + * Returns the source code object that the rule is running on. + * @deprecated Use `sourceCode` instead. + */ + getSourceCode(): SourceCode; + + /** + * Shared settings for the configuration. + */ + settings: Record; + + /** + * Parser-specific options for the configuration. + * @deprecated Use `languageOptions.parserOptions` instead. + */ + parserOptions: Record; + + /** + * The language options for the configuration. + */ + languageOptions: LanguageOptions; + + /** + * The CommonJS path to the parser used while parsing this file. + * @deprecated No longer used. + */ + parserPath: string; + + /** + * The rule ID. + */ + id: string; + + /** + * The rule's configured options. + */ + options: unknown[]; + + /** + * The report function that the rule should use to report problems. + * @param violation The violation to report. + */ + report(violation: ViolationReport): void; +} + +// #region Rule Fixing + +/** + * Manager of text edits for a rule fix. + */ +export interface RuleTextEditor { + /** + * Inserts text after the specified node or token. + * @param nodeOrToken The node or token to insert after. + * @param text The edit to insert after the node or token. + */ + insertTextAfter(nodeOrToken: object, text: string): RuleTextEdit; + + /** + * Inserts text after the specified range. + * @param range The range to insert after. + * @param text The edit to insert after the range. + */ + insertTextAfterRange(range: SourceRange, text: string): RuleTextEdit; + + /** + * Inserts text before the specified node or token. + * @param nodeOrToken The node or token to insert before. + * @param text The edit to insert before the node or token. + */ + insertTextBefore(nodeOrToken: object, text: string): RuleTextEdit; + + /** + * Inserts text before the specified range. + * @param range The range to insert before. + * @param text The edit to insert before the range. + */ + insertTextBeforeRange(range: SourceRange, text: string): RuleTextEdit; + + /** + * Removes the specified node or token. + * @param nodeOrToken The node or token to remove. + * @returns The edit to remove the node or token. + */ + remove(nodeOrToken: object): RuleTextEdit; + + /** + * Removes the specified range. + * @param range The range to remove. + * @returns The edit to remove the range. + */ + removeRange(range: SourceRange): RuleTextEdit; + + /** + * Replaces the specified node or token with the given text. + * @param nodeOrToken The node or token to replace. + * @param text The text to replace the node or token with. + * @returns The edit to replace the node or token. + */ + replaceText(nodeOrToken: object, text: string): RuleTextEdit; + + /** + * Replaces the specified range with the given text. + * @param range The range to replace. + * @param text The text to replace the range with. + * @returns The edit to replace the range. + */ + replaceTextRange(range: SourceRange, text: string): RuleTextEdit; +} + +/** + * Represents a fix for a rule violation implemented as a text edit. + */ +export interface RuleTextEdit { + /** + * The range to replace. + */ + range: SourceRange; + + /** + * The text to insert. + */ + text: string; +} + +// #endregion + +interface ViolationReportBase { + /** + * The type of node that the violation is for. + * @deprecated May be removed in the future. + */ + nodeType?: string | undefined; + + /** + * The data to insert into the message. + */ + data?: Record | undefined; + + /** + * The fix to be applied for the violation. + * @param fixer The text editor to apply the fix. + * @returns The fix(es) for the violation. + */ + fix?( + fixer: RuleTextEditor, + ): RuleTextEdit | RuleTextEdit[] | IterableIterator | null; + + /** + * An array of suggested fixes for the problem. These fixes may change the + * behavior of the code, so they are not applied automatically. + */ + suggest?: Array; +} + +type ViolationMessage = { message: string } | { messageId: string }; +type ViolationLocation = { loc: SourceLocation } | { node: object }; + +export type ViolationReport = ViolationReportBase & + ViolationMessage & + ViolationLocation; + +// #region Suggestions + +interface SuggestedEditBase { + /** + * The data to insert into the message. + */ + data?: Record | undefined; + + /** + * The fix to be applied for the suggestion. + * @param fixer The text editor to apply the fix. + * @returns The fix for the suggestion. + */ + fix?( + fixer: RuleTextEditor, + ): RuleTextEdit | RuleTextEdit[] | IterableIterator | null; +} + +type SuggestionMessage = { desc: string } | { messageId: string }; + +/** + * A suggested edit for a rule violation. + */ +export type SuggestedEdit = SuggestedEditBase & SuggestionMessage; + +// #endregion + +/** + * The definition of an ESLint rule. + */ +export interface RuleDefinition { + /** + * The meta information for the rule. + */ + meta?: RulesMeta; + + /** + * Creates the visitor that ESLint uses to apply the rule during traversal. + * @param context The rule context. + * @returns The rule visitor. + */ + create(context: RuleContext): T; +} + //------------------------------------------------------------------------------ // Config //------------------------------------------------------------------------------ @@ -90,7 +455,6 @@ export type SeverityName = "off" | "warn" | "error"; * - `0` means off. * - `1` means warn. * - `2` means error. - * */ export type SeverityLevel = 0 | 1 | 2; From 3519eb46ca93f85438abf73beed6173200127eb1 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Thu, 22 Aug 2024 16:34:43 -0400 Subject: [PATCH 02/12] Remove typescript-eslint stylistic rules --- eslint.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index e9ef578..cf73ac9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -58,7 +58,7 @@ export default [ // TypeScript ...tseslint.config({ files: ["**/*.ts"], - extends: [...tseslint.configs.strict, ...tseslint.configs.stylistic], + extends: [...tseslint.configs.strict], rules: { "no-use-before-define": "off", }, From c31fbe064dce1ac57980e242708c8a9eb9fc144f Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 25 Sep 2024 10:57:57 -0400 Subject: [PATCH 03/12] Re-enable tseslint stylistic rules --- eslint.config.js | 2 +- packages/core/src/types.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index cf73ac9..e9ef578 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -58,7 +58,7 @@ export default [ // TypeScript ...tseslint.config({ files: ["**/*.ts"], - extends: [...tseslint.configs.strict], + extends: [...tseslint.configs.strict, ...tseslint.configs.stylistic], rules: { "no-use-before-define": "off", }, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 3784cef..101e0ed 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -100,6 +100,7 @@ export type RuleType = "problem" | "suggestion" | "layout"; */ export type RuleFixType = "code" | "whitespace"; +/* eslint-disable @typescript-eslint/consistent-indexed-object-style -- Needs to be interface so people can extend it. */ /** * An object containing visitor information for a rule. Each method is either the * name of a node type or a selector, or is a method that will be called at specific @@ -113,6 +114,7 @@ export interface RuleVisitor { | ((node: unknown, parent?: unknown) => void) | ((...unknown: unknown[]) => void); } +/* eslint-enable @typescript-eslint/consistent-indexed-object-style -- Needs to be interface so people can extend it. */ /** * Rule meta information used for documentation. @@ -386,7 +388,7 @@ interface ViolationReportBase { * An array of suggested fixes for the problem. These fixes may change the * behavior of the code, so they are not applied automatically. */ - suggest?: Array; + suggest?: SuggestedEdit[]; } type ViolationMessage = { message: string } | { messageId: string }; From aa8acb2e1c153a144ae75084cc7b9216b0196e2d Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 25 Sep 2024 11:13:00 -0400 Subject: [PATCH 04/12] Remove extra visitorKeys --- packages/core/src/types.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 101e0ed..3995ecf 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -166,11 +166,6 @@ export interface RulesMeta { */ messages: Record; - /** - * The visitor keys for the rule. - */ - visitorKeys?: Record; - /** * The deprecated rules for the rule. */ From 6ab35d5160dac3cfc542ae71831b73aa5cea1bbc Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 25 Sep 2024 11:18:44 -0400 Subject: [PATCH 05/12] Update RuleTextEditor --- packages/core/src/types.ts | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 3995ecf..4ca8ec2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -36,14 +36,6 @@ export interface FileProblem { // ASTs //------------------------------------------------------------------------------ -/** - * Represents an AST node or token with location information in ESLint format. - */ -export interface SyntaxElement { - loc: SourceLocation; - range: SourceRange; -} - /** * Represents the start and end coordinates of a node inside the source. */ @@ -281,13 +273,16 @@ export interface RuleContext { /** * Manager of text edits for a rule fix. */ -export interface RuleTextEditor { +export interface RuleTextEditor { /** * Inserts text after the specified node or token. - * @param nodeOrToken The node or token to insert after. + * @param syntaxElement The node or token to insert after. * @param text The edit to insert after the node or token. */ - insertTextAfter(nodeOrToken: object, text: string): RuleTextEdit; + insertTextAfter( + syntaxElement: EditableSyntaxElement, + text: string, + ): RuleTextEdit; /** * Inserts text after the specified range. @@ -298,10 +293,13 @@ export interface RuleTextEditor { /** * Inserts text before the specified node or token. - * @param nodeOrToken The node or token to insert before. + * @param syntaxElement A syntax element with location information to insert before. * @param text The edit to insert before the node or token. */ - insertTextBefore(nodeOrToken: object, text: string): RuleTextEdit; + insertTextBefore( + syntaxElement: EditableSyntaxElement, + text: string, + ): RuleTextEdit; /** * Inserts text before the specified range. @@ -312,10 +310,10 @@ export interface RuleTextEditor { /** * Removes the specified node or token. - * @param nodeOrToken The node or token to remove. + * @param syntaxElement A syntax element with location information to remove. * @returns The edit to remove the node or token. */ - remove(nodeOrToken: object): RuleTextEdit; + remove(syntaxElement: EditableSyntaxElement): RuleTextEdit; /** * Removes the specified range. @@ -326,11 +324,14 @@ export interface RuleTextEditor { /** * Replaces the specified node or token with the given text. - * @param nodeOrToken The node or token to replace. + * @param syntaxElement A syntax element with location information to replace. * @param text The text to replace the node or token with. * @returns The edit to replace the node or token. */ - replaceText(nodeOrToken: object, text: string): RuleTextEdit; + replaceText( + syntaxElement: EditableSyntaxElement, + text: string, + ): RuleTextEdit; /** * Replaces the specified range with the given text. From cc0f4532e3fe555a14bdfc91afc76abc17448125 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 25 Sep 2024 13:49:38 -0400 Subject: [PATCH 06/12] Apply feedback --- packages/core/src/types.ts | 95 +++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 4ca8ec2..fd7d04d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -184,7 +184,10 @@ export interface RulesMeta { * information about the current state of the linting process and is the rule's * view into the outside world. */ -export interface RuleContext { +export interface RuleContext< + Code extends SourceCode = SourceCode, + LangOptions = LanguageOptions, +> { /** * The current working directory for the session. */ @@ -221,13 +224,13 @@ export interface RuleContext { /** * The source code object that the rule is running on. */ - sourceCode: SourceCode; + sourceCode: Code; /** * Returns the source code object that the rule is running on. * @deprecated Use `sourceCode` instead. */ - getSourceCode(): SourceCode; + getSourceCode(): Code; /** * Shared settings for the configuration. @@ -243,7 +246,7 @@ export interface RuleContext { /** * The language options for the configuration. */ - languageOptions: LanguageOptions; + languageOptions: LangOptions; /** * The CommonJS path to the parser used while parsing this file. @@ -424,7 +427,11 @@ export type SuggestedEdit = SuggestedEditBase & SuggestionMessage; /** * The definition of an ESLint rule. */ -export interface RuleDefinition { +export interface RuleDefinition< + Visitor extends RuleVisitor = RuleVisitor, + Code extends SourceCode = SourceCode, + LangOptions = LanguageOptions, +> { /** * The meta information for the rule. */ @@ -435,7 +442,7 @@ export interface RuleDefinition { * @param context The rule context. * @returns The rule visitor. */ - create(context: RuleContext): T; + create(context: RuleContext): Visitor; } //------------------------------------------------------------------------------ @@ -498,7 +505,11 @@ export type RulesConfig = Record; /** * Represents a plugin language. */ -export interface Language { +export interface Language< + LangOptions = LanguageOptions, + Code extends SourceCode = SourceCode, + RootNode = unknown, +> { /** * Indicates how ESLint should read the file. */ @@ -527,7 +538,7 @@ export interface Language { /** * Validates languageOptions for this language. */ - validateLanguageOptions(languageOptions: LanguageOptions): void; + validateLanguageOptions(languageOptions: LangOptions): void; /** * Helper for esquery that allows languages to match nodes against @@ -546,16 +557,19 @@ export interface Language { * throws errors for parsing errors but rather should return any parsing * errors as parse of the ParseResult object. */ - parse(file: File, context: LanguageContext): ParseResult; // Future: | Promise; + parse( + file: File, + context: LanguageContext, + ): ParseResult; // Future: | Promise; /** * Creates SourceCode object that ESLint uses to work with a file. */ createSourceCode( file: File, - input: OkParseResult, - context: LanguageContext, - ): SourceCode; // Future: | Promise; + input: OkParseResult, + context: LanguageContext, + ): Code; // Future: | Promise; } /** @@ -566,8 +580,8 @@ export type LanguageOptions = Record; /** * The context object that is passed to the language plugin methods. */ -export interface LanguageContext { - languageOptions: LanguageOptions; +export interface LanguageContext { + languageOptions: LangOptions; } /** @@ -602,7 +616,7 @@ export interface File { /** * Represents the successful result of parsing a file. */ -export interface OkParseResult { +export interface OkParseResult { /** * Indicates if the parse was successful. If true, the parse was successful * and ESLint should continue on to create a SourceCode object and run rules; @@ -614,7 +628,7 @@ export interface OkParseResult { /** * The abstract syntax tree created by the parser. (only when ok: true) */ - ast: T; + ast: RootNode; /** * Any additional data that the parser wants to provide. @@ -647,8 +661,8 @@ export interface NotOkParseResult { [key: string]: any; } -export type ParseResult = - | OkParseResult +export type ParseResult = + | OkParseResult | NotOkParseResult; /** @@ -671,11 +685,16 @@ interface InlineConfigElement { /** * Represents the basic interface for a source code object. */ -interface SourceCodeBase { +interface SourceCodeBase< + LangOptions = LanguageOptions, + RootNode = unknown, + SyntaxElementWithLoc = unknown, + ConfigNode = unknown, +> { /** * Root of the AST. */ - ast: object; + ast: RootNode; /** * The traversal path that tools should take when evaluating the AST. @@ -686,13 +705,17 @@ interface SourceCodeBase { /** * Retrieves the equivalent of `loc` for a given node or token. + * @param syntaxElement The node or token to get the location for. + * @returns The location of the node or token. */ - getLoc(nodeOrToken: object): SourceLocation; + getLoc(syntaxElement: SyntaxElementWithLoc): SourceLocation; /** * Retrieves the equivalent of `range` for a given node or token. + * @param syntaxElement The node or token to get the range for. + * @returns The range of the node or token. */ - getRange(nodeOrToken: object): SourceRange; + getRange(syntaxElement: SyntaxElementWithLoc): SourceRange; /** * Traversal of AST. @@ -702,7 +725,7 @@ interface SourceCodeBase { /** * Applies language options passed in from the ESLint core. */ - applyLanguageOptions?(languageOptions: LanguageOptions): void; + applyLanguageOptions?(languageOptions: LangOptions): void; /** * Return all of the inline areas where ESLint should be disabled/enabled @@ -717,7 +740,7 @@ interface SourceCodeBase { * Returns an array of all inline configuration nodes found in the * source code. */ - getInlineConfigNodes?(): object[]; + getInlineConfigNodes?(): ConfigNode[]; /** * Applies configuration found inside of the source code. This method is only @@ -740,7 +763,17 @@ interface SourceCodeBase { /** * Represents the source of a text file being linted. */ -export interface TextSourceCode extends SourceCodeBase { +export interface TextSourceCode< + LangOptions = LanguageOptions, + RootNode = unknown, + SyntaxElementWithLoc = unknown, + ConfigNode = unknown, +> extends SourceCodeBase< + LangOptions, + RootNode, + SyntaxElementWithLoc, + ConfigNode + > { /** * The body of the file that you'd like rule developers to access. */ @@ -750,7 +783,17 @@ export interface TextSourceCode extends SourceCodeBase { /** * Represents the source of a binary file being linted. */ -export interface BinarySourceCode extends SourceCodeBase { +export interface BinarySourceCode< + LangOptions = LanguageOptions, + RootNode = unknown, + SyntaxElementWithLoc = unknown, + ConfigNode = unknown, +> extends SourceCodeBase< + LangOptions, + RootNode, + SyntaxElementWithLoc, + ConfigNode + > { /** * The body of the file that you'd like rule developers to access. */ From 766986bb1467465572cdc7f1f3bbddc993734ec7 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 25 Sep 2024 13:55:33 -0400 Subject: [PATCH 07/12] Make more generic --- packages/core/src/types.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index fd7d04d..d188a0a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -185,8 +185,9 @@ export interface RulesMeta { * view into the outside world. */ export interface RuleContext< - Code extends SourceCode = SourceCode, LangOptions = LanguageOptions, + Code extends SourceCode = SourceCode, + RuleOptions = unknown[], > { /** * The current working directory for the session. @@ -262,7 +263,7 @@ export interface RuleContext< /** * The rule's configured options. */ - options: unknown[]; + options: RuleOptions; /** * The report function that the rule should use to report problems. @@ -428,9 +429,10 @@ export type SuggestedEdit = SuggestedEditBase & SuggestionMessage; * The definition of an ESLint rule. */ export interface RuleDefinition< - Visitor extends RuleVisitor = RuleVisitor, - Code extends SourceCode = SourceCode, LangOptions = LanguageOptions, + Code extends SourceCode = SourceCode, + RuleOptions = unknown[], + Visitor extends RuleVisitor = RuleVisitor, > { /** * The meta information for the rule. @@ -442,7 +444,7 @@ export interface RuleDefinition< * @param context The rule context. * @returns The rule visitor. */ - create(context: RuleContext): Visitor; + create(context: RuleContext): Visitor; } //------------------------------------------------------------------------------ From 44a566058eb0c78739cb20acfb38c87c4627e816 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 25 Sep 2024 13:57:47 -0400 Subject: [PATCH 08/12] Make ViolationReport generic --- packages/core/src/types.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index d188a0a..3c68157 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -188,6 +188,7 @@ export interface RuleContext< LangOptions = LanguageOptions, Code extends SourceCode = SourceCode, RuleOptions = unknown[], + Node = unknown, > { /** * The current working directory for the session. @@ -269,7 +270,7 @@ export interface RuleContext< * The report function that the rule should use to report problems. * @param violation The violation to report. */ - report(violation: ViolationReport): void; + report(violation: ViolationReport): void; } // #region Rule Fixing @@ -392,11 +393,11 @@ interface ViolationReportBase { } type ViolationMessage = { message: string } | { messageId: string }; -type ViolationLocation = { loc: SourceLocation } | { node: object }; +type ViolationLocation = { loc: SourceLocation } | { node: Node }; -export type ViolationReport = ViolationReportBase & +export type ViolationReport = ViolationReportBase & ViolationMessage & - ViolationLocation; + ViolationLocation; // #region Suggestions From c5a743bdf2ddd0c64b74c2877bf436e24b764cfd Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Thu, 26 Sep 2024 13:48:45 -0400 Subject: [PATCH 09/12] Pass Node to RuleContext --- packages/core/src/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 3c68157..09184f5 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -434,6 +434,7 @@ export interface RuleDefinition< Code extends SourceCode = SourceCode, RuleOptions = unknown[], Visitor extends RuleVisitor = RuleVisitor, + Node = unknown, > { /** * The meta information for the rule. @@ -445,7 +446,7 @@ export interface RuleDefinition< * @param context The rule context. * @returns The rule visitor. */ - create(context: RuleContext): Visitor; + create(context: RuleContext): Visitor; } //------------------------------------------------------------------------------ From c10d276d5bfb06b5cdb920a98ebc624cf905f7be Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 2 Oct 2024 11:51:23 -0400 Subject: [PATCH 10/12] Clean up complicated generic types --- packages/core/src/types.ts | 167 +++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 63 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 09184f5..53020a1 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -137,11 +137,14 @@ export interface RulesMetaDocs { /** * Meta information about a rule. */ -export interface RulesMeta { +export interface RulesMeta< + MessageIds extends string = string, + ExtRuleDocs = unknown, +> { /** * Properties that are used when documenting the rule. */ - docs?: RulesMetaDocs | undefined; + docs?: (RulesMetaDocs & ExtRuleDocs) | undefined; /** * The type of rule. @@ -156,7 +159,7 @@ export interface RulesMeta { /** * The messages that the rule can report. */ - messages: Record; + messages: Record; /** * The deprecated rules for the rule. @@ -179,16 +182,28 @@ export interface RulesMeta { hasSuggestions?: boolean | undefined; } +/** + * Generic type for `RuleContext`. + */ +export interface RuleContextTypeOptions { + LangOptions?: LanguageOptions; + Code?: SourceCode; + RuleOptions?: unknown[]; + Node?: unknown; +} + /** * Represents the context object that is passed to a rule. This object contains * information about the current state of the linting process and is the rule's * view into the outside world. */ export interface RuleContext< - LangOptions = LanguageOptions, - Code extends SourceCode = SourceCode, - RuleOptions = unknown[], - Node = unknown, + Options extends RuleContextTypeOptions = { + LangOptions: LanguageOptions; + Code: SourceCode; + RuleOptions: unknown[]; + Node: unknown; + }, > { /** * The current working directory for the session. @@ -226,13 +241,13 @@ export interface RuleContext< /** * The source code object that the rule is running on. */ - sourceCode: Code; + sourceCode: Options["Code"]; /** * Returns the source code object that the rule is running on. * @deprecated Use `sourceCode` instead. */ - getSourceCode(): Code; + getSourceCode(): Options["Code"]; /** * Shared settings for the configuration. @@ -248,7 +263,7 @@ export interface RuleContext< /** * The language options for the configuration. */ - languageOptions: LangOptions; + languageOptions: Options["LangOptions"]; /** * The CommonJS path to the parser used while parsing this file. @@ -264,13 +279,13 @@ export interface RuleContext< /** * The rule's configured options. */ - options: RuleOptions; + options: Options["RuleOptions"]; /** * The report function that the rule should use to report problems. * @param violation The violation to report. */ - report(violation: ViolationReport): void; + report(violation: ViolationReport): void; } // #region Rule Fixing @@ -426,27 +441,41 @@ export type SuggestedEdit = SuggestedEditBase & SuggestionMessage; // #endregion +/** + * Generic options for the `RuleDefinition` type. + */ +export interface RuleDefinitionTypeOptions { + LangOptions?: LanguageOptions; + Code?: SourceCode; + RuleOptions?: unknown[]; + Visitor?: RuleVisitor; + Node?: unknown; + MessageIds?: string; + ExtRuleDocs?: unknown; +} + /** * The definition of an ESLint rule. */ -export interface RuleDefinition< - LangOptions = LanguageOptions, - Code extends SourceCode = SourceCode, - RuleOptions = unknown[], - Visitor extends RuleVisitor = RuleVisitor, - Node = unknown, -> { +export interface RuleDefinition { /** * The meta information for the rule. */ - meta?: RulesMeta; + meta?: RulesMeta; /** * Creates the visitor that ESLint uses to apply the rule during traversal. * @param context The rule context. * @returns The rule visitor. */ - create(context: RuleContext): Visitor; + create( + context: RuleContext<{ + LangOptions: Options["LangOptions"]; + Code: Options["Code"]; + RuleOptions: Options["RuleOptions"]; + Node: Options["Node"]; + }>, + ): Options["Visitor"]; } //------------------------------------------------------------------------------ @@ -506,14 +535,20 @@ export type RulesConfig = Record; // Languages //------------------------------------------------------------------------------ +/** + * Generic options for the `Language` type. + */ +export interface LanguageTypeOptions { + LangOptions?: LanguageOptions; + Code?: SourceCode; + RootNode?: unknown; + Node: unknown; +} + /** * Represents a plugin language. */ -export interface Language< - LangOptions = LanguageOptions, - Code extends SourceCode = SourceCode, - RootNode = unknown, -> { +export interface Language { /** * Indicates how ESLint should read the file. */ @@ -542,7 +577,7 @@ export interface Language< /** * Validates languageOptions for this language. */ - validateLanguageOptions(languageOptions: LangOptions): void; + validateLanguageOptions(languageOptions: Options["LangOptions"]): void; /** * Helper for esquery that allows languages to match nodes against @@ -552,8 +587,8 @@ export interface Language< */ matchesSelectorClass?( className: string, - node: object, - ancestry: object[], + node: Options["Node"], + ancestry: Options["Node"][], ): boolean; /** @@ -563,17 +598,17 @@ export interface Language< */ parse( file: File, - context: LanguageContext, - ): ParseResult; // Future: | Promise; + context: LanguageContext, + ): ParseResult; // Future: | Promise; /** * Creates SourceCode object that ESLint uses to work with a file. */ createSourceCode( file: File, - input: OkParseResult, - context: LanguageContext, - ): Code; // Future: | Promise; + input: OkParseResult, + context: LanguageContext, + ): Options["Code"]; // Future: | Promise; } /** @@ -686,19 +721,31 @@ interface InlineConfigElement { }; } +/** + * Generic options for the `SourceCodeBase` type. + */ +interface SourceCodeBaseTypeOptions { + LangOptions?: LanguageOptions; + RootNode?: unknown; + SyntaxElementWithLoc?: unknown; + ConfigNode?: unknown; +} + /** * Represents the basic interface for a source code object. */ interface SourceCodeBase< - LangOptions = LanguageOptions, - RootNode = unknown, - SyntaxElementWithLoc = unknown, - ConfigNode = unknown, + Options extends SourceCodeBaseTypeOptions = { + LangOptions: LanguageOptions; + RootNode: unknown; + SyntaxElementWithLoc: unknown; + ConfigNode: unknown; + }, > { /** * Root of the AST. */ - ast: RootNode; + ast: Options["RootNode"]; /** * The traversal path that tools should take when evaluating the AST. @@ -712,14 +759,14 @@ interface SourceCodeBase< * @param syntaxElement The node or token to get the location for. * @returns The location of the node or token. */ - getLoc(syntaxElement: SyntaxElementWithLoc): SourceLocation; + getLoc(syntaxElement: Options["SyntaxElementWithLoc"]): SourceLocation; /** * Retrieves the equivalent of `range` for a given node or token. * @param syntaxElement The node or token to get the range for. * @returns The range of the node or token. */ - getRange(syntaxElement: SyntaxElementWithLoc): SourceRange; + getRange(syntaxElement: Options["SyntaxElementWithLoc"]): SourceRange; /** * Traversal of AST. @@ -729,7 +776,7 @@ interface SourceCodeBase< /** * Applies language options passed in from the ESLint core. */ - applyLanguageOptions?(languageOptions: LangOptions): void; + applyLanguageOptions?(languageOptions: Options["LangOptions"]): void; /** * Return all of the inline areas where ESLint should be disabled/enabled @@ -744,7 +791,7 @@ interface SourceCodeBase< * Returns an array of all inline configuration nodes found in the * source code. */ - getInlineConfigNodes?(): ConfigNode[]; + getInlineConfigNodes?(): Options["ConfigNode"][]; /** * Applies configuration found inside of the source code. This method is only @@ -768,16 +815,13 @@ interface SourceCodeBase< * Represents the source of a text file being linted. */ export interface TextSourceCode< - LangOptions = LanguageOptions, - RootNode = unknown, - SyntaxElementWithLoc = unknown, - ConfigNode = unknown, -> extends SourceCodeBase< - LangOptions, - RootNode, - SyntaxElementWithLoc, - ConfigNode - > { + Options extends SourceCodeBaseTypeOptions = { + LangOptions: LanguageOptions; + RootNode: unknown; + SyntaxElementWithLoc: unknown; + ConfigNode: unknown; + }, +> extends SourceCodeBase { /** * The body of the file that you'd like rule developers to access. */ @@ -788,16 +832,13 @@ export interface TextSourceCode< * Represents the source of a binary file being linted. */ export interface BinarySourceCode< - LangOptions = LanguageOptions, - RootNode = unknown, - SyntaxElementWithLoc = unknown, - ConfigNode = unknown, -> extends SourceCodeBase< - LangOptions, - RootNode, - SyntaxElementWithLoc, - ConfigNode - > { + Options extends SourceCodeBaseTypeOptions = { + LangOptions: LanguageOptions; + RootNode: unknown; + SyntaxElementWithLoc: unknown; + ConfigNode: unknown; + }, +> extends SourceCodeBase { /** * The body of the file that you'd like rule developers to access. */ From 136f991e6adfe720d0ca30a06ff2f380ad23323f Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 2 Oct 2024 11:57:48 -0400 Subject: [PATCH 11/12] Update RuleVisitor interface --- packages/core/src/types.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 53020a1..45a86f2 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -102,9 +102,7 @@ export interface RuleVisitor { /** * Called for each node in the AST or at specific times during the traversal. */ - [key: string]: - | ((node: unknown, parent?: unknown) => void) - | ((...unknown: unknown[]) => void); + [key: string]: (...args: unknown[]) => void; } /* eslint-enable @typescript-eslint/consistent-indexed-object-style -- Needs to be interface so people can extend it. */ @@ -845,7 +843,14 @@ export interface BinarySourceCode< body: Uint8Array; } -export type SourceCode = TextSourceCode | BinarySourceCode; +export type SourceCode< + Options extends SourceCodeBaseTypeOptions = { + LangOptions: LanguageOptions; + RootNode: unknown; + SyntaxElementWithLoc: unknown; + ConfigNode: unknown; + }, +> = TextSourceCode | BinarySourceCode; /** * Represents a traversal step visiting the AST. From 9430d28cb49193644582fe5bba32aecfab75ebfc Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 2 Oct 2024 14:10:41 -0400 Subject: [PATCH 12/12] Make Language arguments optional --- packages/core/src/types.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 45a86f2..7e94e19 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -546,7 +546,14 @@ export interface LanguageTypeOptions { /** * Represents a plugin language. */ -export interface Language { +export interface Language< + Options extends LanguageTypeOptions = { + LangOptions: LanguageOptions; + Code: SourceCode; + RootNode: unknown; + Node: unknown; + }, +> { /** * Indicates how ESLint should read the file. */