@samitouri / QOS-React / commits / ca3d16c5ef
Fork 0

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"