Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b8b8c6e1f | ||
|
|
90131ceca5 | ||
|
|
64b555c5fb |
@@ -41,10 +41,6 @@ const DynamicGatingOptionsSchema = z.object({
|
||||
source: z.string(),
|
||||
});
|
||||
export type DynamicGatingOptions = z.infer<typeof DynamicGatingOptionsSchema>;
|
||||
const CustomOptOutDirectiveSchema = z
|
||||
.nullable(z.array(z.string()))
|
||||
.default(null);
|
||||
type CustomOptOutDirective = z.infer<typeof CustomOptOutDirectiveSchema>;
|
||||
|
||||
export type PluginOptions = {
|
||||
environment: EnvironmentConfig;
|
||||
@@ -136,11 +132,6 @@ export type PluginOptions = {
|
||||
*/
|
||||
ignoreUseNoForget: boolean;
|
||||
|
||||
/**
|
||||
* Unstable / do not use
|
||||
*/
|
||||
customOptOutDirectives: CustomOptOutDirective;
|
||||
|
||||
sources: Array<string> | ((filename: string) => boolean) | null;
|
||||
|
||||
/**
|
||||
@@ -287,7 +278,6 @@ export const defaultOptions: PluginOptions = {
|
||||
return filename.indexOf('node_modules') === -1;
|
||||
},
|
||||
enableReanimatedCheck: true,
|
||||
customOptOutDirectives: null,
|
||||
target: '19',
|
||||
} as const;
|
||||
|
||||
@@ -348,21 +338,6 @@ export function parsePluginOptions(obj: unknown): PluginOptions {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'customOptOutDirectives': {
|
||||
const result = CustomOptOutDirectiveSchema.safeParse(value);
|
||||
if (result.success) {
|
||||
parsedOptions[key] = result.data;
|
||||
} else {
|
||||
CompilerError.throwInvalidConfig({
|
||||
reason:
|
||||
'Could not parse custom opt out directives. Update React Compiler config to fix the error',
|
||||
description: `${fromZodError(result.error)}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parsedOptions[key] = value;
|
||||
}
|
||||
|
||||
@@ -63,16 +63,7 @@ export function tryFindDirectiveEnablingMemoization(
|
||||
|
||||
export function findDirectiveDisablingMemoization(
|
||||
directives: Array<t.Directive>,
|
||||
{customOptOutDirectives}: PluginOptions,
|
||||
): t.Directive | null {
|
||||
if (customOptOutDirectives != null) {
|
||||
return (
|
||||
directives.find(
|
||||
directive =>
|
||||
customOptOutDirectives.indexOf(directive.value.value) !== -1,
|
||||
) ?? null
|
||||
);
|
||||
}
|
||||
return (
|
||||
directives.find(directive =>
|
||||
OPT_OUT_DIRECTIVES.has(directive.value.value),
|
||||
@@ -403,8 +394,7 @@ export function compileProgram(
|
||||
code: pass.code,
|
||||
suppressions,
|
||||
hasModuleScopeOptOut:
|
||||
findDirectiveDisablingMemoization(program.node.directives, pass.opts) !=
|
||||
null,
|
||||
findDirectiveDisablingMemoization(program.node.directives) != null,
|
||||
});
|
||||
|
||||
const queue: Array<CompileSource> = findFunctionsToCompile(
|
||||
@@ -581,10 +571,7 @@ function processFn(
|
||||
}
|
||||
directives = {
|
||||
optIn: optIn.unwrapOr(null),
|
||||
optOut: findDirectiveDisablingMemoization(
|
||||
fn.node.body.directives,
|
||||
programContext.opts,
|
||||
),
|
||||
optOut: findDirectiveDisablingMemoization(fn.node.body.directives),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -93,21 +93,6 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = {
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function* splitPragma(
|
||||
pragma: string,
|
||||
): Generator<{key: string; value: string | null}> {
|
||||
for (const entry of pragma.split('@')) {
|
||||
const keyVal = entry.trim();
|
||||
const valIdx = keyVal.indexOf(':');
|
||||
if (valIdx === -1) {
|
||||
yield {key: keyVal.split(' ', 1)[0], value: null};
|
||||
} else {
|
||||
yield {key: keyVal.slice(0, valIdx), value: keyVal.slice(valIdx + 1)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For snap test fixtures and playground only.
|
||||
*/
|
||||
@@ -116,11 +101,19 @@ function parseConfigPragmaEnvironmentForTest(
|
||||
): EnvironmentConfig {
|
||||
const maybeConfig: Partial<Record<keyof EnvironmentConfig, unknown>> = {};
|
||||
|
||||
for (const {key, value: val} of splitPragma(pragma)) {
|
||||
for (const token of pragma.split(' ')) {
|
||||
if (!token.startsWith('@')) {
|
||||
continue;
|
||||
}
|
||||
const keyVal = token.slice(1);
|
||||
const valIdx = keyVal.indexOf(':');
|
||||
const key = valIdx === -1 ? keyVal : keyVal.slice(0, valIdx);
|
||||
const val = valIdx === -1 ? undefined : keyVal.slice(valIdx + 1);
|
||||
const isSet = val === undefined || val === 'true';
|
||||
if (!hasOwnProperty(EnvironmentConfigSchema.shape, key)) {
|
||||
continue;
|
||||
}
|
||||
const isSet = val == null || val === 'true';
|
||||
|
||||
if (isSet && key in testComplexConfigDefaults) {
|
||||
maybeConfig[key] = testComplexConfigDefaults[key];
|
||||
} else if (isSet) {
|
||||
@@ -183,11 +176,18 @@ export function parseConfigPragmaForTests(
|
||||
compilationMode: defaults.compilationMode,
|
||||
environment,
|
||||
};
|
||||
for (const {key, value: val} of splitPragma(pragma)) {
|
||||
for (const token of pragma.split(' ')) {
|
||||
if (!token.startsWith('@')) {
|
||||
continue;
|
||||
}
|
||||
const keyVal = token.slice(1);
|
||||
const idx = keyVal.indexOf(':');
|
||||
const key = idx === -1 ? keyVal : keyVal.slice(0, idx);
|
||||
const val = idx === -1 ? undefined : keyVal.slice(idx + 1);
|
||||
if (!hasOwnProperty(defaultOptions, key)) {
|
||||
continue;
|
||||
}
|
||||
const isSet = val == null || val === 'true';
|
||||
const isSet = val === undefined || val === 'true';
|
||||
if (isSet && key in testComplexPluginOptionDefaults) {
|
||||
options[key] = testComplexPluginOptionDefaults[key];
|
||||
} else if (isSet) {
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @customOptOutDirectives:["use todo memo"]
|
||||
function Component() {
|
||||
'use todo memo';
|
||||
return <div>hello world!</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @customOptOutDirectives:["use todo memo"]
|
||||
function Component() {
|
||||
"use todo memo";
|
||||
return <div>hello world!</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>hello world!</div>
|
||||
@@ -1,10 +0,0 @@
|
||||
// @customOptOutDirectives:["use todo memo"]
|
||||
function Component() {
|
||||
'use todo memo';
|
||||
return <div>hello world!</div>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [],
|
||||
};
|
||||
@@ -8344,23 +8344,6 @@ const testsTypescript = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function MyComponent(props) {
|
||||
useEffect(() => {
|
||||
console.log(props.foo);
|
||||
});
|
||||
}
|
||||
`,
|
||||
options: [{requireExplicitEffectDeps: true}],
|
||||
errors: [
|
||||
{
|
||||
message:
|
||||
'React Hook useEffect always requires dependencies. Please add a dependency array or an explicit `undefined`',
|
||||
suggestions: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -67,9 +67,6 @@ const rule = {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
requireExplicitEffectDeps: {
|
||||
type: 'boolean',
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -93,13 +90,10 @@ const rule = {
|
||||
? rawOptions.experimental_autoDependenciesHooks
|
||||
: [];
|
||||
|
||||
const requireExplicitEffectDeps: boolean = rawOptions && rawOptions.requireExplicitEffectDeps || false;
|
||||
|
||||
const options = {
|
||||
additionalHooks,
|
||||
experimental_autoDependenciesHooks,
|
||||
enableDangerousAutofixThisMayCauseInfiniteLoops,
|
||||
requireExplicitEffectDeps,
|
||||
};
|
||||
|
||||
function reportProblem(problem: Rule.ReportDescriptor) {
|
||||
@@ -1346,15 +1340,6 @@ const rule = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!maybeNode && isEffect && options.requireExplicitEffectDeps) {
|
||||
reportProblem({
|
||||
node: reactiveHook,
|
||||
message:
|
||||
`React Hook ${reactiveHookName} always requires dependencies. ` +
|
||||
`Please add a dependency array or an explicit \`undefined\``
|
||||
});
|
||||
}
|
||||
|
||||
const isAutoDepsHook =
|
||||
options.experimental_autoDependenciesHooks.includes(reactiveHookName);
|
||||
|
||||
|
||||
@@ -573,7 +573,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
assertConsoleErrorDev([
|
||||
"Can't perform a React state update on a component that hasn't mounted yet. " +
|
||||
'This indicates that you have a side-effect in your render function that ' +
|
||||
'asynchronously tries to update the component. ' +
|
||||
'asynchronously later calls tries to update the component. ' +
|
||||
'Move this work to useEffect instead.\n' +
|
||||
' in B (at **)',
|
||||
]);
|
||||
|
||||
@@ -1922,7 +1922,7 @@ describe('ReactDOMServerPartialHydration', () => {
|
||||
assertConsoleErrorDev([
|
||||
"Can't perform a React state update on a component that hasn't mounted yet. " +
|
||||
'This indicates that you have a side-effect in your render function that ' +
|
||||
'asynchronously tries to update the component. Move this work to useEffect instead.\n' +
|
||||
'asynchronously later calls tries to update the component. Move this work to useEffect instead.\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
|
||||
|
||||
@@ -1883,7 +1883,7 @@ describe('ReactDOMServerPartialHydrationActivity', () => {
|
||||
assertConsoleErrorDev([
|
||||
"Can't perform a React state update on a component that hasn't mounted yet. " +
|
||||
'This indicates that you have a side-effect in your render function that ' +
|
||||
'asynchronously tries to update the component. Move this work to useEffect instead.\n' +
|
||||
'asynchronously later calls tries to update the component. Move this work to useEffect instead.\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
|
||||
|
||||
@@ -908,7 +908,7 @@ export function scheduleUpdateOnFiber(
|
||||
markRootUpdated(root, lane);
|
||||
|
||||
if (
|
||||
(executionContext & RenderContext) !== NoContext &&
|
||||
(executionContext & RenderContext) !== NoLanes &&
|
||||
root === workInProgressRoot
|
||||
) {
|
||||
// This update was dispatched during the render phase. This is a mistake
|
||||
@@ -4802,7 +4802,7 @@ export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) {
|
||||
console.error(
|
||||
"Can't perform a React state update on a component that hasn't mounted yet. " +
|
||||
'This indicates that you have a side-effect in your render function that ' +
|
||||
'asynchronously tries to update the component. Move this work to ' +
|
||||
'asynchronously later calls tries to update the component. Move this work to ' +
|
||||
'useEffect instead.',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -764,7 +764,7 @@ describe('Activity', () => {
|
||||
assertConsoleErrorDev([
|
||||
"Can't perform a React state update on a component that hasn't mounted yet. " +
|
||||
'This indicates that you have a side-effect in your render function that ' +
|
||||
'asynchronously tries to update the component. ' +
|
||||
'asynchronously later calls tries to update the component. ' +
|
||||
'Move this work to useEffect instead.\n' +
|
||||
' in Child (at **)',
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user