From 6957fb9a39cd6583d17d1d637740e56e55f465dd Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sat, 8 Nov 2025 14:46:46 +0000 Subject: [PATCH] fix(linter/plugins): do not allow access to `Context#id` in `createOnce` (#15489) Revert #15447. It's easy to allow `Context#id` to be accessed in `createOnce` when running rule in Oxlint, but not possible to do in ESLint compat mode with current API, because `id` isn't a property of the rule, so `defineRule` doesn't know it. We could support it by making `definePlugin` compulsory for ESLint interop, and get it to set `id` property on `Context` objects properly. But that's a bit tricky, so just disable access for now. It's probably not very useful anyway - a rule should know what its own name is! --- apps/oxlint/src-js/index.ts | 2 -- apps/oxlint/src-js/plugins/context.ts | 7 ++++++- apps/oxlint/test/fixtures/createOnce/output.snap.md | 4 ++-- apps/oxlint/test/fixtures/createOnce/plugin.ts | 5 ++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/oxlint/src-js/index.ts b/apps/oxlint/src-js/index.ts index 1ec77f1185de4..1f600360a7068 100644 --- a/apps/oxlint/src-js/index.ts +++ b/apps/oxlint/src-js/index.ts @@ -230,8 +230,6 @@ function createContextAndVisitor(rule: CreateOnceRule): { // But any such bugs should have been caught when testing the rule in Oxlint, so should be OK to take this shortcut. // `FILE_CONTEXT` prototype provides `cwd` property and `extends` method, which are available in `createOnce`. const context = ObjectCreate(FILE_CONTEXT, { - // TODO: Need to set `id` to full rule name - it's available in `createOnce`. - // Or make it inaccessible in `createOnce`. id: { value: '', enumerable: true, configurable: true }, options: { value: null, enumerable: true, configurable: true }, report: { value: null, enumerable: true, configurable: true }, diff --git a/apps/oxlint/src-js/plugins/context.ts b/apps/oxlint/src-js/plugins/context.ts index 1fd1ae69ec093..acc03e256b8a2 100644 --- a/apps/oxlint/src-js/plugins/context.ts +++ b/apps/oxlint/src-js/plugins/context.ts @@ -352,7 +352,12 @@ export function createContext(fullRuleName: string, ruleDetails: RuleDetails): R // Inherit from `FILE_CONTEXT`, which provides getters for file-specific properties __proto__: FILE_CONTEXT, // Rule ID, in form `/` - id: fullRuleName, + get id(): string { + // It's not possible to allow access to `id` in `createOnce` in ESLint compatibility mode, so we don't + // allow it here either. It's probably not very useful anyway - a rule should know what its own name is! + if (filePath === null) throw new Error('Cannot access `context.id` in `createOnce`'); + return fullRuleName; + }, // Getter for rule options for this rule on this file get options(): Readonly { if (filePath === null) throw new Error('Cannot access `context.options` in `createOnce`'); diff --git a/apps/oxlint/test/fixtures/createOnce/output.snap.md b/apps/oxlint/test/fixtures/createOnce/output.snap.md index 0303cd972c1be..0892bdafa3daa 100644 --- a/apps/oxlint/test/fixtures/createOnce/output.snap.md +++ b/apps/oxlint/test/fixtures/createOnce/output.snap.md @@ -39,7 +39,7 @@ : ^ `---- - x create-once-plugin(always-run): createOnce: id: create-once-plugin/always-run + x create-once-plugin(always-run): createOnce: id error: Cannot access `context.id` in `createOnce` ,-[files/1.js:1:1] 1 | let x; : ^ @@ -177,7 +177,7 @@ : ^ `---- - x create-once-plugin(always-run): createOnce: id: create-once-plugin/always-run + x create-once-plugin(always-run): createOnce: id error: Cannot access `context.id` in `createOnce` ,-[files/2.js:1:1] 1 | let y; : ^ diff --git a/apps/oxlint/test/fixtures/createOnce/plugin.ts b/apps/oxlint/test/fixtures/createOnce/plugin.ts index c7955617426d8..75ae8fd528a2e 100644 --- a/apps/oxlint/test/fixtures/createOnce/plugin.ts +++ b/apps/oxlint/test/fixtures/createOnce/plugin.ts @@ -22,9 +22,8 @@ const alwaysRunRule: Rule = { // oxlint-disable-next-line typescript-eslint/no-this-alias const topLevelThis = this; - const { id } = context; - // Check that these APIs throw here + const idError = tryCatch(() => context.id); const filenameError = tryCatch(() => context.filename); const physicalFilenameError = tryCatch(() => context.physicalFilename); const optionsError = tryCatch(() => context.options); @@ -36,7 +35,7 @@ const alwaysRunRule: Rule = { before() { context.report({ message: `createOnce: call count: ${createOnceCallCount}`, node: SPAN }); context.report({ message: `createOnce: this === rule: ${topLevelThis === alwaysRunRule}`, node: SPAN }); - context.report({ message: `createOnce: id: ${id}`, node: SPAN }); + context.report({ message: `createOnce: id error: ${idError?.message}`, node: SPAN }); context.report({ message: `createOnce: filename error: ${filenameError?.message}`, node: SPAN }); context.report({ message: `createOnce: physicalFilename error: ${physicalFilenameError?.message}`,