Add config option to ignore 'use no forget' directives

This will let us test without taking into account the existing 'use no forget' 
directives to better understand what validations we may need to build. 

There are two other files that look for this directive that do not seem to take 
compiler options: 

1. 
https://github.com/facebook/react-forget/blob/408617ec8a5caa815f61d4204cb3fec0775593ca/react/scripts/babel/transform-forget.js#L25 

2. 
https://github.com/facebook/react-forget/blob/408617ec8a5caa815f61d4204cb3fec0775593ca/packages/babel-plugin-react-forget/scripts/jest/makeTransform.ts#L119 

Do I need to do anything for those?
This commit is contained in:
Jordan Brown
2024-03-04 19:00:48 -05:00
parent 7f994241b0
commit f5d99baf8e
5 changed files with 99 additions and 18 deletions
@@ -104,6 +104,10 @@ export type PluginOptions = {
eslintSuppressionRules?: Array<string> | null | undefined;
flowSuppressions: boolean;
/*
* Ignore 'use no forget' annotations. Helpful during testing but should not be used in production.
*/
ignoreUseNoForget: boolean;
};
const CompilationModeSchema = z.enum([
@@ -172,6 +176,7 @@ export const defaultOptions: PluginOptions = {
enableUseMemoCachePolyfill: false,
eslintSuppressionRules: null,
flowSuppressions: false,
ignoreUseNoForget: false,
} as const;
export function parsePluginOptions(obj: unknown): PluginOptions {
@@ -51,13 +51,15 @@ function findDirectiveEnablingMemoization(
}
function findDirectiveDisablingMemoization(
directives: t.Directive[]
directives: t.Directive[],
options: PluginOptions
): t.Directive | null {
for (const directive of directives) {
const directiveValue = directive.value.value;
if (
directiveValue === "use no forget" ||
directiveValue === "use no memo"
(directiveValue === "use no forget" ||
directiveValue === "use no memo") &&
!options.ignoreUseNoForget
) {
return directive;
}
@@ -197,12 +199,15 @@ export function compileProgram(
program: NodePath<t.Program>,
pass: CompilerPass
): void {
const options = parsePluginOptions(pass.opts);
// Top level "use no forget", skip this file entirely
if (findDirectiveDisablingMemoization(program.node.directives) != null) {
if (
findDirectiveDisablingMemoization(program.node.directives, options) != null
) {
return;
}
const options = parsePluginOptions(pass.opts);
const environment = parseEnvironmentConfig(pass.opts.environment ?? {});
/*
@@ -400,7 +405,8 @@ function getReactFunctionType(
if (fn.node.body.type === "BlockStatement") {
// Opt-outs disable compilation regardless of mode
const useNoForget = findDirectiveDisablingMemoization(
fn.node.body.directives
fn.node.body.directives,
pass.opts
);
if (useNoForget != null) {
pass.opts.logger?.logEvent(pass.filename, {
@@ -0,0 +1,53 @@
## Input
```javascript
// @ignoreUseNoForget
function Component(prop) {
"use no forget";
const result = prop.x.toFixed();
return <div>{result}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ x: 1 }],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react"; // @ignoreUseNoForget
function Component(prop) {
const $ = useMemoCache(4);
let t0;
if ($[0] !== prop.x) {
t0 = prop.x.toFixed();
$[0] = prop.x;
$[1] = t0;
} else {
t0 = $[1];
}
const result = t0;
let t1;
if ($[2] !== result) {
t1 = <div>{result}</div>;
$[2] = result;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ x: 1 }],
};
```
### Eval output
(kind: ok) <div>1</div>
@@ -0,0 +1,11 @@
// @ignoreUseNoForget
function Component(prop) {
"use no forget";
const result = prop.x.toFixed();
return <div>{result}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ x: 1 }],
};
+18 -12
View File
@@ -108,6 +108,11 @@ function makePluginOptions(
flowSuppressions = true;
}
let ignoreUseNoForget: boolean = false;
if (firstLine.includes("@ignoreUseNoForget")) {
ignoreUseNoForget = true;
}
const hookPatternMatch = /@hookPattern:"([^"]+)"/.exec(firstLine);
if (
hookPatternMatch &&
@@ -168,6 +173,7 @@ function makePluginOptions(
enableUseMemoCachePolyfill,
eslintSuppressionRules,
flowSuppressions,
ignoreUseNoForget
};
}
@@ -205,18 +211,18 @@ function getEvaluatorPresets(
presets.push(
language === "typescript"
? [
"@babel/preset-typescript",
{
/**
* onlyRemoveTypeImports needs to be set as fbt imports
* would otherwise be removed by this pass.
* https://github.com/facebook/fbt/issues/49
* https://github.com/facebook/sfbt/issues/72
* https://dev.to/retyui/how-to-add-support-typescript-for-fbt-an-internationalization-framework-3lo0
*/
onlyRemoveTypeImports: true,
},
]
"@babel/preset-typescript",
{
/**
* onlyRemoveTypeImports needs to be set as fbt imports
* would otherwise be removed by this pass.
* https://github.com/facebook/fbt/issues/49
* https://github.com/facebook/sfbt/issues/72
* https://dev.to/retyui/how-to-add-support-typescript-for-fbt-an-internationalization-framework-3lo0
*/
onlyRemoveTypeImports: true,
},
]
: "@babel/preset-flow"
);