Skip to content

Commit d931832

Browse files
committed
[compiler] Surface unused opt out directives in eslint
This PR updates the eslint plugin to report unused opt out directives. One of the downsides of the opt out directive is that it opts the component/hook out of compilation forever, even if the underlying issue was fixed in product code or fixed in the compiler. ghstack-source-id: e288fff Pull Request resolved: #30721
1 parent fd4bb7c commit d931832

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ const tests: CompilerTestCases = {
104104
}
105105
`,
106106
},
107+
{
108+
name: "'use no forget' directive is respected when errors are present",
109+
code: normalizeIndent`
110+
let count = 0;
111+
function Component() {
112+
'use no forget';
113+
count++;
114+
return <div>Hello world {count}</div>
115+
}
116+
`,
117+
},
107118
],
108119
invalid: [
109120
{
@@ -199,6 +210,27 @@ const tests: CompilerTestCases = {
199210
},
200211
],
201212
},
213+
{
214+
name: "Unused 'use no forget' directive is reported when no errors are present",
215+
code: normalizeIndent`
216+
function Component() {
217+
'use no forget';
218+
return <div>Hello world</div>
219+
}
220+
`,
221+
errors: [
222+
{
223+
message: "Unused 'use no forget' directive",
224+
suggestions: [
225+
{
226+
output:
227+
// yuck
228+
'\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
229+
},
230+
],
231+
},
232+
],
233+
},
202234
],
203235
};
204236

compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ import BabelPluginReactCompiler, {
1515
ErrorSeverity,
1616
parsePluginOptions,
1717
validateEnvironmentConfig,
18+
OPT_OUT_DIRECTIVES,
1819
type PluginOptions,
1920
} from 'babel-plugin-react-compiler/src';
2021
import {Logger} from 'babel-plugin-react-compiler/src/Entrypoint';
2122
import type {Rule} from 'eslint';
23+
import {Statement} from 'estree';
2224
import * as HermesParser from 'hermes-parser';
2325

2426
type CompilerErrorDetailWithLoc = Omit<CompilerErrorDetailOptions, 'loc'> & {
@@ -146,6 +148,7 @@ const rule: Rule.RuleModule = {
146148
userOpts['__unstable_donotuse_reportAllBailouts'];
147149
}
148150

151+
let shouldReportUnusedOptOutDirective = true;
149152
const options: PluginOptions = {
150153
...parsePluginOptions(userOpts),
151154
...COMPILER_OPTIONS,
@@ -155,6 +158,7 @@ const rule: Rule.RuleModule = {
155158
logEvent: (filename, event): void => {
156159
userLogger?.logEvent(filename, event);
157160
if (event.kind === 'CompileError') {
161+
shouldReportUnusedOptOutDirective = false;
158162
const detail = event.detail;
159163
const suggest = makeSuggestions(detail);
160164
if (__unstable_donotuse_reportAllBailouts && event.fnLoc != null) {
@@ -269,7 +273,52 @@ const rule: Rule.RuleModule = {
269273
/* errors handled by injected logger */
270274
}
271275
}
272-
return {};
276+
277+
function reportUnusedOptOutDirective(stmt: Statement) {
278+
if (
279+
stmt.type === 'ExpressionStatement' &&
280+
stmt.expression.type === 'Literal' &&
281+
typeof stmt.expression.value === 'string' &&
282+
OPT_OUT_DIRECTIVES.has(stmt.expression.value) &&
283+
stmt.loc != null
284+
) {
285+
context.report({
286+
message: `Unused '${stmt.expression.value}' directive`,
287+
loc: stmt.loc,
288+
suggest: [
289+
{
290+
desc: 'Remove the directive',
291+
fix(fixer) {
292+
return fixer.remove(stmt);
293+
},
294+
},
295+
],
296+
});
297+
}
298+
}
299+
if (shouldReportUnusedOptOutDirective) {
300+
return {
301+
FunctionDeclaration(fnDecl) {
302+
for (const stmt of fnDecl.body.body) {
303+
reportUnusedOptOutDirective(stmt);
304+
}
305+
},
306+
ArrowFunctionExpression(fnExpr) {
307+
if (fnExpr.body.type === 'BlockStatement') {
308+
for (const stmt of fnExpr.body.body) {
309+
reportUnusedOptOutDirective(stmt);
310+
}
311+
}
312+
},
313+
FunctionExpression(fnExpr) {
314+
for (const stmt of fnExpr.body.body) {
315+
reportUnusedOptOutDirective(stmt);
316+
}
317+
},
318+
};
319+
} else {
320+
return {};
321+
}
273322
},
274323
};
275324

0 commit comments

Comments
 (0)