From df454e68d97e01529c68dfdb2433fa65288d021e Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Thu, 2 Oct 2025 18:12:36 -0400 Subject: [PATCH] [eprh] Remove NoUnusedOptOutDirectives This rule was a leftover from a while ago and doesn't actually lint anything useful. Specifically, you get a lint error if you try to opt out a component that isn't already bailing out. If there's a bailout the compiler already safely skips over it, so adding `'use no memo'` there is unnecessary. Fixes #31407 --- .../__tests__/NoUnusedDirectivesRule-test.ts | 58 ------------------- .../src/rules/ReactCompilerRule.ts | 46 +-------------- .../src/shared/RunReactCompiler.ts | 54 +---------------- .../src/shared/ReactCompiler.ts | 46 +-------------- .../src/shared/RunReactCompiler.ts | 53 +---------------- 5 files changed, 8 insertions(+), 249 deletions(-) delete mode 100644 compiler/packages/eslint-plugin-react-compiler/__tests__/NoUnusedDirectivesRule-test.ts diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/NoUnusedDirectivesRule-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/NoUnusedDirectivesRule-test.ts deleted file mode 100644 index 77f6dd93fb..0000000000 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/NoUnusedDirectivesRule-test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {NoUnusedDirectivesRule} from '../src/rules/ReactCompilerRule'; -import {normalizeIndent, testRule} from './shared-utils'; - -testRule('no unused directives rule', NoUnusedDirectivesRule, { - valid: [], - invalid: [ - { - name: "Unused 'use no forget' directive is reported when no errors are present on components", - code: normalizeIndent` - function Component() { - 'use no forget'; - return
Hello world
- } - `, - errors: [ - { - message: "Unused 'use no forget' directive", - suggestions: [ - { - output: - // yuck - '\nfunction Component() {\n \n return
Hello world
\n}\n', - }, - ], - }, - ], - }, - - { - name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks", - code: normalizeIndent` - function notacomponent() { - 'use no forget'; - return 1 + 1; - } - `, - errors: [ - { - message: "Unused 'use no forget' directive", - suggestions: [ - { - output: - // yuck - '\nfunction notacomponent() {\n \n return 1 + 1;\n}\n', - }, - ], - }, - ], - }, - ], -}); diff --git a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts index 3eb6dfa0c7..5990155248 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts @@ -161,38 +161,6 @@ function makeRule(rule: LintRule): Rule.RuleModule { }; } -export const NoUnusedDirectivesRule: Rule.RuleModule = { - meta: { - type: 'suggestion', - docs: { - recommended: true, - }, - fixable: 'code', - hasSuggestions: true, - // validation is done at runtime with zod - schema: [{type: 'object', additionalProperties: true}], - }, - create(context: Rule.RuleContext): Rule.RuleListener { - const results = getReactCompilerResult(context); - - for (const directive of results.unusedOptOutDirectives) { - context.report({ - message: `Unused '${directive.directive}' directive`, - loc: directive.loc, - suggest: [ - { - desc: 'Remove the directive', - fix(fixer): Rule.Fix { - return fixer.removeRange(directive.range); - }, - }, - ], - }); - } - return {}; - }, -}; - type RulesConfig = { [name: string]: {rule: Rule.RuleModule; severity: ErrorSeverity}; }; @@ -202,12 +170,7 @@ export const allRules: RulesConfig = LintRules.reduce( acc[rule.name] = {rule: makeRule(rule), severity: rule.severity}; return acc; }, - { - 'no-unused-directives': { - rule: NoUnusedDirectivesRule, - severity: ErrorSeverity.Error, - }, - } as RulesConfig, + {} as RulesConfig, ); export const recommendedRules: RulesConfig = LintRules.filter( @@ -217,12 +180,7 @@ export const recommendedRules: RulesConfig = LintRules.filter( acc[rule.name] = {rule: makeRule(rule), severity: rule.severity}; return acc; }, - { - 'no-unused-directives': { - rule: NoUnusedDirectivesRule, - severity: ErrorSeverity.Error, - }, - } as RulesConfig, + {} as RulesConfig, ); export function mapErrorSeverityToESlint( diff --git a/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts b/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts index f11d0b910d..1216eb7f41 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/shared/RunReactCompiler.ts @@ -5,20 +5,18 @@ * LICENSE file in the root directory of this source tree. */ -import {transformFromAstSync, traverse} from '@babel/core'; +import {transformFromAstSync} from '@babel/core'; import {parse as babelParse} from '@babel/parser'; -import {Directive, File} from '@babel/types'; +import {File} from '@babel/types'; // @ts-expect-error: no types available import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods'; import BabelPluginReactCompiler, { parsePluginOptions, validateEnvironmentConfig, - OPT_OUT_DIRECTIVES, type PluginOptions, } from 'babel-plugin-react-compiler/src'; import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint'; import type {SourceCode} from 'eslint'; -import {SourceLocation} from 'estree'; // @ts-expect-error: no types available import * as HermesParser from 'hermes-parser'; import {isDeepStrictEqual} from 'util'; @@ -45,17 +43,11 @@ const COMPILER_OPTIONS: PluginOptions = { }), }; -export type UnusedOptOutDirective = { - loc: SourceLocation; - range: [number, number]; - directive: string; -}; export type RunCacheEntry = { sourceCode: string; filename: string; userOpts: PluginOptions; flowSuppressions: Array<{line: number; code: string}>; - unusedOptOutDirectives: Array; events: Array; }; @@ -87,24 +79,6 @@ function getFlowSuppressions( return results; } -function filterUnusedOptOutDirectives( - directives: ReadonlyArray, -): Array { - const results: Array = []; - for (const directive of directives) { - if ( - OPT_OUT_DIRECTIVES.has(directive.value.value) && - directive.loc != null - ) { - results.push({ - loc: directive.loc, - directive: directive.value.value, - range: [directive.start!, directive.end!], - }); - } - } - return results; -} function runReactCompilerImpl({ sourceCode, @@ -125,7 +99,6 @@ function runReactCompilerImpl({ filename, userOpts, flowSuppressions: [], - unusedOptOutDirectives: [], events: [], }; const userLogger: Logger | null = options.logger; @@ -181,29 +154,6 @@ function runReactCompilerImpl({ configFile: false, babelrc: false, }); - - if (results.events.filter(e => e.kind === 'CompileError').length === 0) { - traverse(babelAST, { - FunctionDeclaration(path) { - path.node; - results.unusedOptOutDirectives.push( - ...filterUnusedOptOutDirectives(path.node.body.directives), - ); - }, - ArrowFunctionExpression(path) { - if (path.node.body.type === 'BlockStatement') { - results.unusedOptOutDirectives.push( - ...filterUnusedOptOutDirectives(path.node.body.directives), - ); - } - }, - FunctionExpression(path) { - results.unusedOptOutDirectives.push( - ...filterUnusedOptOutDirectives(path.node.body.directives), - ); - }, - }); - } } catch (err) { /* errors handled by injected logger */ } diff --git a/packages/eslint-plugin-react-hooks/src/shared/ReactCompiler.ts b/packages/eslint-plugin-react-hooks/src/shared/ReactCompiler.ts index dec46457cc..cdb3af3848 100644 --- a/packages/eslint-plugin-react-hooks/src/shared/ReactCompiler.ts +++ b/packages/eslint-plugin-react-hooks/src/shared/ReactCompiler.ts @@ -160,38 +160,6 @@ function makeRule(rule: LintRule): Rule.RuleModule { }; } -export const NoUnusedDirectivesRule: Rule.RuleModule = { - meta: { - type: 'suggestion', - docs: { - recommended: true, - }, - fixable: 'code', - hasSuggestions: true, - // validation is done at runtime with zod - schema: [{type: 'object', additionalProperties: true}], - }, - create(context: Rule.RuleContext): Rule.RuleListener { - const results = getReactCompilerResult(context); - - for (const directive of results.unusedOptOutDirectives) { - context.report({ - message: `Unused '${directive.directive}' directive`, - loc: directive.loc, - suggest: [ - { - desc: 'Remove the directive', - fix(fixer): Rule.Fix { - return fixer.removeRange(directive.range); - }, - }, - ], - }); - } - return {}; - }, -}; - type RulesConfig = { [name: string]: {rule: Rule.RuleModule; severity: ErrorSeverity}; }; @@ -201,12 +169,7 @@ export const allRules: RulesConfig = LintRules.reduce( acc[rule.name] = {rule: makeRule(rule), severity: rule.severity}; return acc; }, - { - 'no-unused-directives': { - rule: NoUnusedDirectivesRule, - severity: ErrorSeverity.Error, - }, - } as RulesConfig, + {} as RulesConfig, ); export const recommendedRules: RulesConfig = LintRules.filter( @@ -216,12 +179,7 @@ export const recommendedRules: RulesConfig = LintRules.filter( acc[rule.name] = {rule: makeRule(rule), severity: rule.severity}; return acc; }, - { - 'no-unused-directives': { - rule: NoUnusedDirectivesRule, - severity: ErrorSeverity.Error, - }, - } as RulesConfig, + {} as RulesConfig, ); export function mapErrorSeverityToESlint( diff --git a/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts b/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts index f46528cd60..fc9067d274 100644 --- a/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts +++ b/packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts @@ -6,21 +6,19 @@ */ /* eslint-disable no-for-of-loops/no-for-of-loops */ -import {transformFromAstSync, traverse} from '@babel/core'; +import {transformFromAstSync} from '@babel/core'; import {parse as babelParse} from '@babel/parser'; -import {Directive, File} from '@babel/types'; +import {File} from '@babel/types'; // @ts-expect-error: no types available import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods'; import BabelPluginReactCompiler, { parsePluginOptions, validateEnvironmentConfig, - OPT_OUT_DIRECTIVES, type PluginOptions, Logger, LoggerEvent, } from 'babel-plugin-react-compiler'; import type {SourceCode} from 'eslint'; -import {SourceLocation} from 'estree'; import * as HermesParser from 'hermes-parser'; import {isDeepStrictEqual} from 'util'; import type {ParseResult} from '@babel/parser'; @@ -46,17 +44,11 @@ const COMPILER_OPTIONS: PluginOptions = { }, }; -export type UnusedOptOutDirective = { - loc: SourceLocation; - range: [number, number]; - directive: string; -}; export type RunCacheEntry = { sourceCode: string; filename: string; userOpts: PluginOptions; flowSuppressions: Array<{line: number; code: string}>; - unusedOptOutDirectives: Array; events: Array; }; @@ -88,24 +80,6 @@ function getFlowSuppressions( return results; } -function filterUnusedOptOutDirectives( - directives: ReadonlyArray, -): Array { - const results: Array = []; - for (const directive of directives) { - if ( - OPT_OUT_DIRECTIVES.has(directive.value.value) && - directive.loc != null - ) { - results.push({ - loc: directive.loc, - directive: directive.value.value, - range: [directive.start!, directive.end!], - }); - } - } - return results; -} function runReactCompilerImpl({ sourceCode, @@ -126,7 +100,6 @@ function runReactCompilerImpl({ filename, userOpts, flowSuppressions: [], - unusedOptOutDirectives: [], events: [], }; const userLogger: Logger | null = options.logger; @@ -182,28 +155,6 @@ function runReactCompilerImpl({ configFile: false, babelrc: false, }); - - if (results.events.filter(e => e.kind === 'CompileError').length === 0) { - traverse(babelAST, { - FunctionDeclaration(path) { - results.unusedOptOutDirectives.push( - ...filterUnusedOptOutDirectives(path.node.body.directives), - ); - }, - ArrowFunctionExpression(path) { - if (path.node.body.type === 'BlockStatement') { - results.unusedOptOutDirectives.push( - ...filterUnusedOptOutDirectives(path.node.body.directives), - ); - } - }, - FunctionExpression(path) { - results.unusedOptOutDirectives.push( - ...filterUnusedOptOutDirectives(path.node.body.directives), - ); - }, - }); - } } catch (err) { /* errors handled by injected logger */ }