From c79696618342688b8849afd649d4a3f42f008b63 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:26:24 +0000 Subject: [PATCH] feat(linter/plugins): add `meta` property to rules (#14089) Add `meta` property to `Rule`. Currently it doesn't do much, except make the `Rule` type compatible with ESLint. But need `rule.meta` for supporting fixes (PR to come). --- apps/oxlint/src-js/plugins/context.ts | 7 ++++++- apps/oxlint/src-js/plugins/load.ts | 22 ++++++++++++++++++++-- apps/oxlint/src-js/plugins/types.ts | 6 ++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/oxlint/src-js/plugins/context.ts b/apps/oxlint/src-js/plugins/context.ts index 3d57dfc75f581..b65392fff86c7 100644 --- a/apps/oxlint/src-js/plugins/context.ts +++ b/apps/oxlint/src-js/plugins/context.ts @@ -1,3 +1,5 @@ +import type { RuleMeta } from './types.ts'; + // Diagnostic in form passed by user to `Context#report()` interface Diagnostic { message: string; @@ -64,6 +66,8 @@ interface InternalContext { filePath: string; // Options options: unknown[]; + // Rule metadata + meta: RuleMeta; } /** @@ -80,12 +84,13 @@ export class Context { * @class * @param fullRuleName - Rule name, in form `/` */ - constructor(fullRuleName: string) { + constructor(fullRuleName: string, meta: RuleMeta) { this.#internal = { id: fullRuleName, filePath: '', ruleIndex: -1, options: [], + meta, }; } diff --git a/apps/oxlint/src-js/plugins/load.ts b/apps/oxlint/src-js/plugins/load.ts index 0e4840b006ad8..175c312357aec 100644 --- a/apps/oxlint/src-js/plugins/load.ts +++ b/apps/oxlint/src-js/plugins/load.ts @@ -1,7 +1,7 @@ import { Context } from './context.js'; import { getErrorMessage } from './utils.js'; -import type { AfterHook, BeforeHook, Visitor, VisitorWithHooks } from './types.ts'; +import type { AfterHook, BeforeHook, RuleMeta, Visitor, VisitorWithHooks } from './types.ts'; const ObjectKeys = Object.keys; @@ -21,10 +21,12 @@ export interface Plugin { export type Rule = CreateRule | CreateOnceRule; interface CreateRule { + meta?: RuleMeta; create: (context: Context) => Visitor; } export interface CreateOnceRule { + meta?: RuleMeta; create?: (context: Context) => Visitor; createOnce: (context: Context) => VisitorWithHooks; } @@ -66,6 +68,9 @@ interface PluginDetails { ruleNames: string[]; } +// Default rule metadata, used if `rule.meta` property is empty. +const emptyRuleMeta: RuleMeta = {}; + /** * Load a plugin. * @@ -113,7 +118,20 @@ async function loadPluginImpl(path: string): Promise { const ruleName = ruleNames[i], rule = rules[ruleName]; - const context = new Context(`${pluginName}/${ruleName}`); + // Validate `rule.meta` and convert to object with standardized shape + // (all properties defined with default values if not supplied) + let ruleMeta = rule.meta; + if (ruleMeta == null) { + ruleMeta = emptyRuleMeta; + } else { + if (typeof ruleMeta !== 'object') throw new TypeError('Invalid `meta`'); + // TODO: Validate and conform individual properties of `meta` once they're supported + ruleMeta = emptyRuleMeta; + } + + // Create `Context` object for rule. This will be re-used for every file. + // It's updated with file-specific data before linting each file with `setupContextForFile`. + const context = new Context(`${pluginName}/${ruleName}`, ruleMeta); let ruleAndContext; if ('createOnce' in rule) { diff --git a/apps/oxlint/src-js/plugins/types.ts b/apps/oxlint/src-js/plugins/types.ts index d9e34df585579..b752c27490f73 100644 --- a/apps/oxlint/src-js/plugins/types.ts +++ b/apps/oxlint/src-js/plugins/types.ts @@ -39,3 +39,9 @@ export interface EnterExit { enter: VisitFn | null; exit: VisitFn | null; } + +// Rule metadata. +// TODO: Fill in all properties. +export interface RuleMeta { + [key: string]: unknown; +}