mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 00:20:04 +01:00
[eprh] Remove NoUnusedOptOutDirectives (#34703)
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 --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34703). * __->__ #34703 * #34700
This commit is contained in:
parent
26b177bc5e
commit
19f65ff179
|
|
@ -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 <div>Hello world</div>
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: "Unused 'use no forget' directive",
|
||||
suggestions: [
|
||||
{
|
||||
output:
|
||||
// yuck
|
||||
'\nfunction Component() {\n \n return <div>Hello world</div>\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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -161,69 +161,21 @@ 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};
|
||||
};
|
||||
|
||||
export const allRules: RulesConfig = LintRules.reduce(
|
||||
(acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
'no-unused-directives': {
|
||||
rule: NoUnusedDirectivesRule,
|
||||
severity: ErrorSeverity.Error,
|
||||
},
|
||||
} as RulesConfig,
|
||||
);
|
||||
export const allRules: RulesConfig = LintRules.reduce((acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
}, {} as RulesConfig);
|
||||
|
||||
export const recommendedRules: RulesConfig = LintRules.filter(
|
||||
rule => rule.recommended,
|
||||
).reduce(
|
||||
(acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
'no-unused-directives': {
|
||||
rule: NoUnusedDirectivesRule,
|
||||
severity: ErrorSeverity.Error,
|
||||
},
|
||||
} as RulesConfig,
|
||||
);
|
||||
).reduce((acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
}, {} as RulesConfig);
|
||||
|
||||
export function mapErrorSeverityToESlint(
|
||||
severity: ErrorSeverity,
|
||||
|
|
|
|||
|
|
@ -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<UnusedOptOutDirective>;
|
||||
events: Array<LoggerEvent>;
|
||||
};
|
||||
|
||||
|
|
@ -87,25 +79,6 @@ function getFlowSuppressions(
|
|||
return results;
|
||||
}
|
||||
|
||||
function filterUnusedOptOutDirectives(
|
||||
directives: ReadonlyArray<Directive>,
|
||||
): Array<UnusedOptOutDirective> {
|
||||
const results: Array<UnusedOptOutDirective> = [];
|
||||
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,
|
||||
filename,
|
||||
|
|
@ -125,7 +98,6 @@ function runReactCompilerImpl({
|
|||
filename,
|
||||
userOpts,
|
||||
flowSuppressions: [],
|
||||
unusedOptOutDirectives: [],
|
||||
events: [],
|
||||
};
|
||||
const userLogger: Logger | null = options.logger;
|
||||
|
|
@ -181,29 +153,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 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<UnusedOptOutDirective>;
|
||||
events: Array<LoggerEvent>;
|
||||
};
|
||||
|
||||
|
|
@ -88,24 +80,6 @@ function getFlowSuppressions(
|
|||
return results;
|
||||
}
|
||||
|
||||
function filterUnusedOptOutDirectives(
|
||||
directives: ReadonlyArray<Directive>,
|
||||
): Array<UnusedOptOutDirective> {
|
||||
const results: Array<UnusedOptOutDirective> = [];
|
||||
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 */
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user