@samitouri / QOS-React / commits / 1b1283ade7

[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

Jordan Brown committed Dec 3, 2024 at 07:42 UTC 1b1283ade72dfdfa8981b08cf4cc9669bbf905f0
7 files changed +67 -4
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+9
@@ -660,6 +660,13 @@ const testComplexConfigDefaults: PartialEnvironmentConfig = {
660 },
661 numRequiredArgs: 2,
662 },
663 + {
664 + function: {
665 + source: 'useEffectWrapper',
666 + importSpecifierName: 'default',
667 + },
668 + numRequiredArgs: 1,
669 + },
670 ],
671 };
672
@@ -1147,3 +1154,5 @@ export function tryParseExternalFunction(
1154 suggestions: null,
1155 });
1156 }
1157 +
1158 +export const DEFAULT_EXPORT = 'default';
compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts
+12
@@ -16,6 +16,7 @@ import {
16 Place,
17 ReactiveScopeDependencies,
18 } from '../HIR';
19 +import {DEFAULT_EXPORT} from '../HIR/Environment';
20 import {
21 createTemporaryPlace,
22 fixScopeAndIdentifierRanges,
@@ -97,6 +98,17 @@ export function inferEffectDependencies(fn: HIRFunction): void {
98 autodepFnLoads.set(lvalue.identifier.id, numRequiredArgs);
99 }
100 }
101 + } else if (
102 + value.kind === 'LoadGlobal' &&
103 + value.binding.kind === 'ImportDefault'
104 + ) {
105 + const moduleTargets = autodepFnConfigs.get(value.binding.module);
106 + if (moduleTargets != null) {
107 + const numRequiredArgs = moduleTargets.get(DEFAULT_EXPORT);
108 + if (numRequiredArgs != null) {
109 + autodepFnLoads.set(lvalue.identifier.id, numRequiredArgs);
110 + }
111 + }
112 } else if (
113 /*
114 * TODO: Handle method calls
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies.expect.md
+18 -1
@@ -4,6 +4,7 @@
4 ```javascript
5 // @inferEffectDependencies
6 import {useEffect, useRef} from 'react';
7 +import useEffectWrapper from 'useEffectWrapper';
8
9 const moduleNonReactive = 0;
10
@@ -39,6 +40,10 @@ function Component({foo, bar}) {
40
41 // No inferred dep array, the argument is not a lambda
42 useEffect(f);
43 +
44 + useEffectWrapper(() => {
45 + console.log(foo);
46 + });
47 }
48
49 ```
@@ -48,11 +53,12 @@ function Component({foo, bar}) {
53 ```javascript
54 import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
55 import { useEffect, useRef } from "react";
56 +import useEffectWrapper from "useEffectWrapper";
57
58 const moduleNonReactive = 0;
59
60 function Component(t0) {
55 - const $ = _c(12);
61 + const $ = _c(14);
62 const { foo, bar } = t0;
63
64 const ref = useRef(0);
@@ -125,6 +131,17 @@ function Component(t0) {
131 const f = t5;
132
133 useEffect(f);
134 + let t6;
135 + if ($[12] !== foo) {
136 + t6 = () => {
137 + console.log(foo);
138 + };
139 + $[12] = foo;
140 + $[13] = t6;
141 + } else {
142 + t6 = $[13];
143 + }
144 + useEffectWrapper(t6, [foo]);
145 }
146
147 ```
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies.js
+5
@@ -1,5 +1,6 @@
1 // @inferEffectDependencies
2 import {useEffect, useRef} from 'react';
3 +import useEffectWrapper from 'useEffectWrapper';
4
5 const moduleNonReactive = 0;
6
@@ -35,4 +36,8 @@ function Component({foo, bar}) {
36
37 // No inferred dep array, the argument is not a lambda
38 useEffect(f);
39 +
40 + useEffectWrapper(() => {
41 + console.log(foo);
42 + });
43 }
compiler/packages/snap/src/compiler.ts
+2
@@ -297,6 +297,8 @@ function getEvaluatorPresets(
297 arg.value = './shared-runtime';
298 } else if (arg.value === 'ReactForgetFeatureFlag') {
299 arg.value = './ReactForgetFeatureFlag';
300 + } else if (arg.value === 'useEffectWrapper') {
301 + arg.value = './useEffectWrapper';
302 }
303 }
304 }
compiler/packages/snap/src/sprout/useEffectWrapper.ts new
+18
@@ -0,0 +1,18 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +/* This file is used to test the effect auto-deps configuration, which
9 + * allows you to specify functions that should have dependencies added to
10 + * callsites.
11 + */
12 +import {useEffect} from 'react';
13 +
14 +export default function useEffectWrapper(f: () => void | (() => void)): void {
15 + useEffect(() => {
16 + f();
17 + }, [f]);
18 +}
compiler/packages/snap/src/types.d.ts
+3 -3
@@ -6,14 +6,14 @@
6 */
7
8 // v0.17.1
9 -declare module "hermes-parser" {
9 +declare module 'hermes-parser' {
10 type HermesParserOptions = {
11 allowReturnOutsideFunction?: boolean;
12 babel?: boolean;
13 - flow?: "all" | "detect";
13 + flow?: 'all' | 'detect';
14 enableExperimentalComponentSyntax?: boolean;
15 sourceFilename?: string;
16 - sourceType?: "module" | "script" | "unambiguous";
16 + sourceType?: 'module' | 'script' | 'unambiguous';
17 tokens?: boolean;
18 };
19 export function parse(code: string, options: Partial<HermesParserOptions>);