Skip to content

Commit 0118104

Browse files
committed
[autodeps] Support namespaces
Summary: Summary: Correctly supports React.useEffect when React is imported as `import * as React from 'react'` (as well as other namespaces as specified in the config). Test Plan:
1 parent 9eabb37 commit 0118104

File tree

5 files changed

+103
-18
lines changed

5 files changed

+103
-18
lines changed

compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function inferEffectDependencies(fn: HIRFunction): void {
4949
);
5050
}
5151
const autodepFnLoads = new Map<IdentifierId, number>();
52+
const autodepModuleLoads = new Map<IdentifierId, Map<string, number>>();
5253

5354
const scopeInfos = new Map<
5455
ScopeId,
@@ -89,9 +90,32 @@ export function inferEffectDependencies(fn: HIRFunction): void {
8990
lvalue.identifier.id,
9091
instr as TInstruction<FunctionExpression>,
9192
);
93+
} else if (
94+
value.kind === 'PropertyLoad' &&
95+
autodepModuleLoads.has(value.object.identifier.id)
96+
) {
97+
const moduleTargets = autodepModuleLoads.get(
98+
value.object.identifier.id,
99+
)!;
100+
const propertyName = value.property;
101+
const numRequiredArgs = moduleTargets.get(propertyName);
102+
if (numRequiredArgs != null) {
103+
autodepFnLoads.set(lvalue.identifier.id, numRequiredArgs);
104+
}
92105
} else if (value.kind === 'LoadGlobal') {
93106
loadGlobals.add(lvalue.identifier.id);
94107

108+
/*
109+
* TODO: Handle properties on default exports, like
110+
* import React from 'react';
111+
* React.useEffect(...);
112+
*/
113+
if (value.binding.kind === 'ImportNamespace') {
114+
const moduleTargets = autodepFnConfigs.get(value.binding.module);
115+
if (moduleTargets != null) {
116+
autodepModuleLoads.set(lvalue.identifier.id, moduleTargets);
117+
}
118+
}
95119
if (
96120
value.binding.kind === 'ImportSpecifier' ||
97121
value.binding.kind === 'ImportDefault'
@@ -109,12 +133,14 @@ export function inferEffectDependencies(fn: HIRFunction): void {
109133
}
110134
}
111135
} else if (
112-
/*
113-
* TODO: Handle method calls
114-
*/
115-
value.kind === 'CallExpression' &&
116-
autodepFnLoads.get(value.callee.identifier.id) === value.args.length &&
117-
value.args[0].kind === 'Identifier'
136+
(value.kind === 'CallExpression' &&
137+
autodepFnLoads.get(value.callee.identifier.id) ===
138+
value.args.length &&
139+
value.args[0].kind === 'Identifier') ||
140+
(value.kind === 'MethodCall' &&
141+
autodepFnLoads.get(value.property.identifier.id) ===
142+
value.args.length &&
143+
value.args[0].kind === 'Identifier')
118144
) {
119145
const effectDeps: Array<Place> = [];
120146
const newInstructions: Array<Instruction> = [];
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @inferEffectDependencies
6+
import * as React from 'react';
7+
import * as SharedRuntime from 'shared-runtime';
8+
9+
function NonReactiveDepInEffect() {
10+
const obj = makeObject_Primitives();
11+
React.useEffect(() => print(obj));
12+
SharedRuntime.useSpecialEffect(() => print(obj), [obj]);
13+
}
14+
15+
```
16+
17+
## Code
18+
19+
```javascript
20+
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
21+
import * as React from "react";
22+
import * as SharedRuntime from "shared-runtime";
23+
24+
function NonReactiveDepInEffect() {
25+
const $ = _c(4);
26+
let t0;
27+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
28+
t0 = makeObject_Primitives();
29+
$[0] = t0;
30+
} else {
31+
t0 = $[0];
32+
}
33+
const obj = t0;
34+
let t1;
35+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
36+
t1 = () => print(obj);
37+
$[1] = t1;
38+
} else {
39+
t1 = $[1];
40+
}
41+
React.useEffect(t1, [obj]);
42+
let t2;
43+
let t3;
44+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
45+
t2 = () => print(obj);
46+
t3 = [obj];
47+
$[2] = t2;
48+
$[3] = t3;
49+
} else {
50+
t2 = $[2];
51+
t3 = $[3];
52+
}
53+
SharedRuntime.useSpecialEffect(t2, t3, [obj]);
54+
}
55+
56+
```
57+
58+
### Eval output
59+
(kind: exception) Fixture not implemented
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @inferEffectDependencies
2+
import * as React from 'react';
3+
import * as SharedRuntime from 'shared-runtime';
4+
5+
function NonReactiveDepInEffect() {
6+
const obj = makeObject_Primitives();
7+
React.useEffect(() => print(obj));
8+
SharedRuntime.useSpecialEffect(() => print(obj), [obj]);
9+
}

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.expect.md renamed to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.expect.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33

44
```javascript
55
// @inferEffectDependencies
6-
import * as React from 'react';
6+
import React from 'react';
77

8-
/**
9-
* TODO: recognize import namespace
10-
*/
118
function NonReactiveDepInEffect() {
129
const obj = makeObject_Primitives();
1310
React.useEffect(() => print(obj));
@@ -19,11 +16,8 @@ function NonReactiveDepInEffect() {
1916

2017
```javascript
2118
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
22-
import * as React from "react";
19+
import React from "react";
2320

24-
/**
25-
* TODO: recognize import namespace
26-
*/
2721
function NonReactiveDepInEffect() {
2822
const $ = _c(2);
2923
let t0;

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo-import-namespace-useEffect.js renamed to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/todo.import-default-property-useEffect.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
// @inferEffectDependencies
2-
import * as React from 'react';
2+
import React from 'react';
33

4-
/**
5-
* TODO: recognize import namespace
6-
*/
74
function NonReactiveDepInEffect() {
85
const obj = makeObject_Primitives();
96
React.useEffect(() => print(obj));

0 commit comments

Comments
 (0)