[compiler] Support default imports for autodep config (#31657)

## Summary

Allows us to add deps for things like `import useWrapperEffect from
'useWrapperEffect'`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31657).
* __->__ #31657
* #31652
This commit is contained in:
Jordan Brown
2024-12-03 07:42:53 -05:00
committed by GitHub
parent 2ab471c8d2
commit 1b1283ade7
7 changed files with 67 additions and 4 deletions
@@ -660,6 +660,13 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = {
},
numRequiredArgs: 2,
},
{
function: {
source: 'useEffectWrapper',
importSpecifierName: 'default',
},
numRequiredArgs: 1,
},
],
};
@@ -1147,3 +1154,5 @@ export function tryParseExternalFunction(
suggestions: null,
});
}
export const DEFAULT_EXPORT = 'default';
@@ -16,6 +16,7 @@ import {
Place,
ReactiveScopeDependencies,
} from '../HIR';
import {DEFAULT_EXPORT} from '../HIR/Environment';
import {
createTemporaryPlace,
fixScopeAndIdentifierRanges,
@@ -97,6 +98,17 @@ export function inferEffectDependencies(fn: HIRFunction): void {
autodepFnLoads.set(lvalue.identifier.id, numRequiredArgs);
}
}
} else if (
value.kind === 'LoadGlobal' &&
value.binding.kind === 'ImportDefault'
) {
const moduleTargets = autodepFnConfigs.get(value.binding.module);
if (moduleTargets != null) {
const numRequiredArgs = moduleTargets.get(DEFAULT_EXPORT);
if (numRequiredArgs != null) {
autodepFnLoads.set(lvalue.identifier.id, numRequiredArgs);
}
}
} else if (
/*
* TODO: Handle method calls
@@ -4,6 +4,7 @@
```javascript
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import useEffectWrapper from 'useEffectWrapper';
const moduleNonReactive = 0;
@@ -39,6 +40,10 @@ function Component({foo, bar}) {
// No inferred dep array, the argument is not a lambda
useEffect(f);
useEffectWrapper(() => {
console.log(foo);
});
}
```
@@ -48,11 +53,12 @@ function Component({foo, bar}) {
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { useEffect, useRef } from "react";
import useEffectWrapper from "useEffectWrapper";
const moduleNonReactive = 0;
function Component(t0) {
const $ = _c(12);
const $ = _c(14);
const { foo, bar } = t0;
const ref = useRef(0);
@@ -125,6 +131,17 @@ function Component(t0) {
const f = t5;
useEffect(f);
let t6;
if ($[12] !== foo) {
t6 = () => {
console.log(foo);
};
$[12] = foo;
$[13] = t6;
} else {
t6 = $[13];
}
useEffectWrapper(t6, [foo]);
}
```
@@ -1,5 +1,6 @@
// @inferEffectDependencies
import {useEffect, useRef} from 'react';
import useEffectWrapper from 'useEffectWrapper';
const moduleNonReactive = 0;
@@ -35,4 +36,8 @@ function Component({foo, bar}) {
// No inferred dep array, the argument is not a lambda
useEffect(f);
useEffectWrapper(() => {
console.log(foo);
});
}
+2
View File
@@ -297,6 +297,8 @@ function getEvaluatorPresets(
arg.value = './shared-runtime';
} else if (arg.value === 'ReactForgetFeatureFlag') {
arg.value = './ReactForgetFeatureFlag';
} else if (arg.value === 'useEffectWrapper') {
arg.value = './useEffectWrapper';
}
}
}
@@ -0,0 +1,18 @@
/**
* 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.
*/
/* This file is used to test the effect auto-deps configuration, which
* allows you to specify functions that should have dependencies added to
* callsites.
*/
import {useEffect} from 'react';
export default function useEffectWrapper(f: () => void | (() => void)): void {
useEffect(() => {
f();
}, [f]);
}
+3 -3
View File
@@ -6,14 +6,14 @@
*/
// v0.17.1
declare module "hermes-parser" {
declare module 'hermes-parser' {
type HermesParserOptions = {
allowReturnOutsideFunction?: boolean;
babel?: boolean;
flow?: "all" | "detect";
flow?: 'all' | 'detect';
enableExperimentalComponentSyntax?: boolean;
sourceFilename?: string;
sourceType?: "module" | "script" | "unambiguous";
sourceType?: 'module' | 'script' | 'unambiguous';
tokens?: boolean;
};
export function parse(code: string, options: Partial<HermesParserOptions>);