The library for web and native user interfaces.
Enable hook syntax support
Updates the compiler to understand Flow hook syntax. Like component syntax, in infer mode hooks are compiled by default unless opted out. Looking ahead, i can imagine splitting up our compilation modes as follows: * Annotations: opt-in explicitly * Declarations: annotations + component/hook declarations * Infer: annotations, component/hook declarations, + component/hook-like functions This also suggest an alternative annotation strategy: "use react" (or "use component" / "use hook") as a general way to tell the compiler that a function is intended for React. Then opting out of memoization could do "use react(nomemo)".
Joe Savona committed
Feb 17, 2024 at 17:31 UTC
ca3d16c5eff02672fb818639716f7dd7c3f961c9
8 files changed
+75
-14
compiler/packages/babel-plugin-react-forget/package.json
+1
-1
@@ -55,7 +55,7 @@
55
"babel-plugin-syntax-hermes-parser": "^0.15.1",
56
"eslint": "8.27.0",
57
"glob": "^7.1.6",
58
- "hermes-parser": "^0.18.2",
58
+ "hermes-parser": "^0.19.1",
59
"jest": "^29.0.3",
60
"jest-environment-jsdom": "^29.0.3",
61
"prettier": "2.8.8",
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts
+4
-2
@@ -19,6 +19,7 @@ import {
19
} from "../HIR/Environment";
20
import { CodegenFunction } from "../ReactiveScopes";
21
import { isComponentDeclaration } from "../Utils/ComponentDeclaration";
22
+import { isHookDeclaration } from "../Utils/HookDeclaration";
23
import { assertExhaustive } from "../Utils/utils";
24
import { insertGatedFunctionDeclaration } from "./Gating";
25
import { addImportsToProgram, updateUseMemoCacheImport } from "./Imports";
@@ -408,8 +409,9 @@ export function shouldVisitNode(fn: BabelFn, pass: CompilerPass): boolean {
409
case "infer": {
410
const hookPattern = pass.opts.environment?.hookPattern ?? null;
411
return (
411
- // Component declarations are known components
412
- (fn.isFunctionDeclaration() && isComponentDeclaration(fn.node)) ||
412
+ // Component and hook declarations are known components/hooks
413
+ (fn.isFunctionDeclaration() &&
414
+ (isComponentDeclaration(fn.node) || isHookDeclaration(fn.node))) ||
415
// Otherwise check if this is a component or hook-like function
416
isComponentOrHookLike(fn, hookPattern)
417
);
compiler/packages/babel-plugin-react-forget/src/Utils/HookDeclaration.ts
new
+24
@@ -0,0 +1,24 @@
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
+import * as t from "@babel/types";
9
+
10
+export type HookDeclaration = t.FunctionDeclaration & {
11
+ __hookDeclaration: boolean;
12
+};
13
+
14
+export function isHookDeclaration(
15
+ node: t.FunctionDeclaration
16
+): node is HookDeclaration {
17
+ return Object.prototype.hasOwnProperty.call(node, "__hookDeclaration");
18
+}
19
+
20
+export function parseHookDeclaration(
21
+ node: t.FunctionDeclaration
22
+): HookDeclaration | null {
23
+ return isHookDeclaration(node) ? node : null;
24
+}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/hook-declaration-basic.flow.expect.md
new
+30
@@ -0,0 +1,30 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @flow @compilationMode(infer)
6
+export default hook useFoo(bar: number) {
7
+ return [bar];
8
+}
9
+
10
+```
11
+
12
+## Code
13
+
14
+```javascript
15
+import { unstable_useMemoCache as useMemoCache } from "react";
16
+function useFoo(bar) {
17
+ const $ = useMemoCache(2);
18
+ let t0;
19
+ if ($[0] !== bar) {
20
+ t0 = [bar];
21
+ $[0] = bar;
22
+ $[1] = t0;
23
+ } else {
24
+ t0 = $[1];
25
+ }
26
+ return t0;
27
+}
28
+
29
+```
30
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/hook-declaration-basic.flow.js
new
+4
@@ -0,0 +1,4 @@
1
+// @flow @compilationMode(infer)
2
+export default hook useFoo(bar: number) {
3
+ return [bar];
4
+}
compiler/packages/eslint-plugin-react-compiler/package.json
+1
-1
@@ -10,7 +10,7 @@
10
"@babel/core": "^7.19.1",
11
"@babel/plugin-proposal-private-methods": "^7.18.6",
12
"babel-plugin-react-forget": "*",
13
- "hermes-parser": "^0.17.1"
13
+ "hermes-parser": "^0.19.1"
14
},
15
"devDependencies": {
16
"@babel/preset-env": "^7.22.4",
compiler/packages/sprout/src/SproutTodoFilter.ts
+1
@@ -420,6 +420,7 @@ const skipFilter = new Set([
420
421
// TODO: we should be able to support these
422
"component-declaration-basic.flow",
423
+ "hook-declaration-basic.flow",
424
"nested-function-with-param-as-captured-dep",
425
"readonly-object-method-calls",
426
"readonly-object-method-calls-mutable-lambda",
compiler/yarn.lock
+10
-10
@@ -6410,10 +6410,10 @@ hermes-estree@0.17.1:
6410
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.17.1.tgz#902806a900c185720424ffcf958027821d23c051"
6411
integrity sha512-EdUJms+eRE40OQxysFlPr1mPpvUbbMi7uDAKlScBw8o3tQY22BZ5yx56OYyp1bVaBm+7Cjc3NQz24sJEFXkPxg==
6412
6413
-hermes-estree@0.18.2:
6414
- version "0.18.2"
6415
- resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.18.2.tgz#fd450fa1659cf074ceaa2ddeeb21674f3b2342f3"
6416
- integrity sha512-KoLsoWXJ5o81nit1wSyEZnWUGy9cBna9iYMZBR7skKh7okYAYKqQ9/OczwpMHn/cH0hKDyblulGsJ7FknlfVxQ==
6413
+hermes-estree@0.19.1:
6414
+ version "0.19.1"
6415
+ resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.19.1.tgz#d5924f5fac2bf0532547ae9f506d6db8f3c96392"
6416
+ integrity sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==
6417
6418
hermes-parser@0.14.0:
6419
version "0.14.0"
@@ -6429,19 +6429,19 @@ hermes-parser@0.15.1:
6429
dependencies:
6430
hermes-estree "0.15.1"
6431
6432
-hermes-parser@0.17.1, hermes-parser@^0.17.1:
6432
+hermes-parser@0.17.1:
6433
version "0.17.1"
6434
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.17.1.tgz#8b5cbaff235fed28487812ad718f9c7182d0db0f"
6435
integrity sha512-yErtFLMEL6490fFJPurNn23OI2ciGAtaUfKUg9VPdcde9CmItCjOVQkJt1Xzawv5kuRzeIx0RE2E2Q9TbIgdzA==
6436
dependencies:
6437
hermes-estree "0.17.1"
6438
6439
-hermes-parser@^0.18.2:
6440
- version "0.18.2"
6441
- resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.18.2.tgz#50f15e2fcd559a48c68cd7af259d4292298bd14d"
6442
- integrity sha512-1eQfvib+VPpgBZ2zYKQhpuOjw1tH+Emuib6QmjkJWJMhyjM8xnXMvA+76o9LhF0zOAJDZgPfQhg43cyXEyl5Ew==
6439
+hermes-parser@^0.19.1:
6440
+ version "0.19.1"
6441
+ resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.19.1.tgz#1044348097165b7c93dc198a80b04ed5130d6b1a"
6442
+ integrity sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A==
6443
dependencies:
6444
- hermes-estree "0.18.2"
6444
+ hermes-estree "0.19.1"
6445
6446
hmac-drbg@^1.0.1:
6447
version "1.0.1"