From 443a9a3c26d0525e7f75165bdc0986d37bf70568 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:11 -0400 Subject: [PATCH 01/10] Update [ghstack-poisoned] --- packages/react/index.development.js | 1 + packages/react/index.experimental.development.js | 1 + packages/react/index.experimental.js | 1 + packages/react/index.fb.js | 1 + packages/react/index.js | 1 + packages/react/src/ReactClient.js | 2 ++ packages/react/src/ReactCompilerRuntime.js | 14 ++++++++++++++ 7 files changed, 21 insertions(+) create mode 100644 packages/react/src/ReactCompilerRuntime.js diff --git a/packages/react/index.development.js b/packages/react/index.development.js index c94c460b97416..bf08db7a58418 100644 --- a/packages/react/index.development.js +++ b/packages/react/index.development.js @@ -30,6 +30,7 @@ export type ChildrenArray<+T> = $ReadOnlyArray> | T; // We can't use export * from in Flow for some reason. export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, Children, Component, Fragment, diff --git a/packages/react/index.experimental.development.js b/packages/react/index.experimental.development.js index 4ffcdea0d242e..676b9eea4d9a9 100644 --- a/packages/react/index.experimental.development.js +++ b/packages/react/index.experimental.development.js @@ -9,6 +9,7 @@ export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, Children, Component, Fragment, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index 6f2ab835135c0..ae98e3b91f19c 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -9,6 +9,7 @@ export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, Children, Component, Fragment, diff --git a/packages/react/index.fb.js b/packages/react/index.fb.js index 1b87e4b2e582f..7999655f30e4b 100644 --- a/packages/react/index.fb.js +++ b/packages/react/index.fb.js @@ -9,6 +9,7 @@ export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, act, cache, Children, diff --git a/packages/react/index.js b/packages/react/index.js index 19f256fd73b5b..0c048c4e9c297 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -31,6 +31,7 @@ export type ChildrenArray<+T> = $ReadOnlyArray> | T; // We can't use export * from in Flow for some reason. export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, Children, Component, Fragment, diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index 318d8e648d9d5..ed064fe8031c0 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -63,6 +63,7 @@ import ReactSharedInternals from './ReactSharedInternalsClient'; import {startTransition} from './ReactStartTransition'; import {act} from './ReactAct'; import {captureOwnerStack} from './ReactOwnerStack'; +import ReactCompilerRuntime from './ReactCompilerRuntime'; const Children = { map, @@ -109,6 +110,7 @@ export { isValidElement, ReactVersion as version, ReactSharedInternals as __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + ReactCompilerRuntime as __COMPILER_RUNTIME, // Concurrent Mode useTransition, startTransition, diff --git a/packages/react/src/ReactCompilerRuntime.js b/packages/react/src/ReactCompilerRuntime.js new file mode 100644 index 0000000000000..a19d05596863e --- /dev/null +++ b/packages/react/src/ReactCompilerRuntime.js @@ -0,0 +1,14 @@ +/** + * 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. + * + * @flow + */ + +import {useMemoCache} from './ReactHooks'; + +export default { + c: useMemoCache, +}; From 0f22b5b2315a3e1e5a0f28b287b303db77b9f5a1 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:15 -0400 Subject: [PATCH 02/10] Update [ghstack-poisoned] --- .../react-compiler-runtime/src/index.ts | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/compiler/packages/react-compiler-runtime/src/index.ts b/compiler/packages/react-compiler-runtime/src/index.ts index 823a49ab8199f..7b8cbe36f52db 100644 --- a/compiler/packages/react-compiler-runtime/src/index.ts +++ b/compiler/packages/react-compiler-runtime/src/index.ts @@ -19,22 +19,27 @@ const ReactSecretInternals = type MemoCache = Array; const $empty = Symbol.for('react.memo_cache_sentinel'); -/** - * DANGER: this hook is NEVER meant to be called directly! - **/ -export function c(size: number) { - return React.useState(() => { - const $ = new Array(size); - for (let ii = 0; ii < size; ii++) { - $[ii] = $empty; - } - // This symbol is added to tell the react devtools that this array is from - // useMemoCache. - // @ts-ignore - $[$empty] = true; - return $; - })[0]; -} + +// Re-export React.c if present, otherwise fallback to the userspace polyfill for versions of React +// < 19. +export const c = + // @ts-expect-error + typeof React.__COMPILER_RUNTIME?.c === 'function' + ? // @ts-expect-error + React.__COMPILER_RUNTIME.c + : function c(size: number) { + return React.useMemo>(() => { + const $ = new Array(size); + for (let ii = 0; ii < size; ii++) { + $[ii] = $empty; + } + // This symbol is added to tell the react devtools that this array is from + // useMemoCache. + // @ts-ignore + $[$empty] = true; + return $; + }, []); + }; const LazyGuardDispatcher: {[key: string]: (...args: Array) => any} = {}; [ From 491db33c9713c3eb8e4ba74090b4665b9932000b Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:18 -0400 Subject: [PATCH 03/10] Update [ghstack-poisoned] --- .../fixtures/compiler/module-scoped-bindings.expect.md | 2 +- compiler/packages/snap/src/sprout/evaluator.ts | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/module-scoped-bindings.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/module-scoped-bindings.expect.md index 36eeaabc7b8b6..5c870b43aff1c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/module-scoped-bindings.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/module-scoped-bindings.expect.md @@ -101,4 +101,4 @@ export const FIXTURE_ENTRYPOINT = { ``` ### Eval output -(kind: ok) [{"Children":{"map":"[[ function params=3 ]]","forEach":"[[ function params=3 ]]","count":"[[ function params=1 ]]","toArray":"[[ function params=1 ]]","only":"[[ function params=1 ]]"},"Component":"[[ function params=3 ]]","PureComponent":"[[ function params=3 ]]","__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE":{"H":{"readContext":"[[ function params=1 ]]","use":"[[ function params=1 ]]","useCallback":"[[ function params=2 ]]","useContext":"[[ function params=1 ]]","useEffect":"[[ function params=2 ]]","useImperativeHandle":"[[ function params=3 ]]","useInsertionEffect":"[[ function params=2 ]]","useLayoutEffect":"[[ function params=2 ]]","useMemo":"[[ function params=2 ]]","useReducer":"[[ function params=3 ]]","useRef":"[[ function params=1 ]]","useState":"[[ function params=1 ]]","useDebugValue":"[[ function params=2 ]]","useDeferredValue":"[[ function params=2 ]]","useTransition":"[[ function params=0 ]]","useSyncExternalStore":"[[ function params=3 ]]","useId":"[[ function params=0 ]]","useCacheRefresh":"[[ function params=0 ]]","useMemoCache":"[[ function params=1 ]]","useHostTransitionStatus":"[[ function params=0 ]]","useFormState":"[[ function params=3 ]]","useActionState":"[[ function params=3 ]]","useOptimistic":"[[ function params=2 ]]"},"A":{"getCacheForType":"[[ function params=1 ]]","getOwner":"[[ function params=0 ]]"},"T":null,"actQueue":["[[ function params=0 ]]","[[ function params=1 ]]"],"isBatchingLegacy":false,"didScheduleLegacyUpdate":false,"didUsePromise":false,"thrownErrors":[],"setExtraStackFrame":"[[ function params=1 ]]","getCurrentStack":"[[ function params=0 ]]","getStackAddendum":"[[ function params=0 ]]"},"act":"[[ function params=1 ]]","cache":"[[ function params=1 ]]","cloneElement":"[[ function params=3 ]]","createContext":"[[ function params=1 ]]","createElement":"[[ function params=3 ]]","createRef":"[[ function params=0 ]]","forwardRef":"[[ function params=1 ]]","isValidElement":"[[ function params=1 ]]","lazy":"[[ function params=1 ]]","memo":"[[ function params=2 ]]","startTransition":"[[ function params=2 ]]","unstable_useCacheRefresh":"[[ function params=0 ]]","use":"[[ function params=1 ]]","useActionState":"[[ function params=3 ]]","useCallback":"[[ function params=2 ]]","useContext":"[[ function params=1 ]]","useDebugValue":"[[ function params=2 ]]","useDeferredValue":"[[ function params=2 ]]","useEffect":"[[ function params=2 ]]","useId":"[[ function params=0 ]]","useImperativeHandle":"[[ function params=3 ]]","useInsertionEffect":"[[ function params=2 ]]","useLayoutEffect":"[[ function params=2 ]]","useMemo":"[[ function params=2 ]]","useOptimistic":"[[ function params=2 ]]","useReducer":"[[ function params=3 ]]","useRef":"[[ function params=1 ]]","useState":"[[ function params=1 ]]","useSyncExternalStore":"[[ function params=3 ]]","useTransition":"[[ function params=0 ]]","version":"19.0.0-beta-b498834eab-20240506","c":"[[ function params=1 ]]"},"[[ cyclic ref *6 ]]",true,true,true,true,"[[ function params=0 ]]",true,"[[ function params=0 ]]"] \ No newline at end of file +(kind: ok) [{"Children":{"map":"[[ function params=3 ]]","forEach":"[[ function params=3 ]]","count":"[[ function params=1 ]]","toArray":"[[ function params=1 ]]","only":"[[ function params=1 ]]"},"Component":"[[ function params=3 ]]","PureComponent":"[[ function params=3 ]]","__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE":{"H":{"readContext":"[[ function params=1 ]]","use":"[[ function params=1 ]]","useCallback":"[[ function params=2 ]]","useContext":"[[ function params=1 ]]","useEffect":"[[ function params=2 ]]","useImperativeHandle":"[[ function params=3 ]]","useInsertionEffect":"[[ function params=2 ]]","useLayoutEffect":"[[ function params=2 ]]","useMemo":"[[ function params=2 ]]","useReducer":"[[ function params=3 ]]","useRef":"[[ function params=1 ]]","useState":"[[ function params=1 ]]","useDebugValue":"[[ function params=2 ]]","useDeferredValue":"[[ function params=2 ]]","useTransition":"[[ function params=0 ]]","useSyncExternalStore":"[[ function params=3 ]]","useId":"[[ function params=0 ]]","useCacheRefresh":"[[ function params=0 ]]","useMemoCache":"[[ function params=1 ]]","useHostTransitionStatus":"[[ function params=0 ]]","useFormState":"[[ function params=3 ]]","useActionState":"[[ function params=3 ]]","useOptimistic":"[[ function params=2 ]]"},"A":{"getCacheForType":"[[ function params=1 ]]","getOwner":"[[ function params=0 ]]"},"T":null,"actQueue":["[[ function params=0 ]]","[[ function params=1 ]]"],"isBatchingLegacy":false,"didScheduleLegacyUpdate":false,"didUsePromise":false,"thrownErrors":[],"setExtraStackFrame":"[[ function params=1 ]]","getCurrentStack":"[[ function params=0 ]]","getStackAddendum":"[[ function params=0 ]]"},"act":"[[ function params=1 ]]","cache":"[[ function params=1 ]]","cloneElement":"[[ function params=3 ]]","createContext":"[[ function params=1 ]]","createElement":"[[ function params=3 ]]","createRef":"[[ function params=0 ]]","forwardRef":"[[ function params=1 ]]","isValidElement":"[[ function params=1 ]]","lazy":"[[ function params=1 ]]","memo":"[[ function params=2 ]]","startTransition":"[[ function params=2 ]]","unstable_useCacheRefresh":"[[ function params=0 ]]","use":"[[ function params=1 ]]","useActionState":"[[ function params=3 ]]","useCallback":"[[ function params=2 ]]","useContext":"[[ function params=1 ]]","useDebugValue":"[[ function params=2 ]]","useDeferredValue":"[[ function params=2 ]]","useEffect":"[[ function params=2 ]]","useId":"[[ function params=0 ]]","useImperativeHandle":"[[ function params=3 ]]","useInsertionEffect":"[[ function params=2 ]]","useLayoutEffect":"[[ function params=2 ]]","useMemo":"[[ function params=2 ]]","useOptimistic":"[[ function params=2 ]]","useReducer":"[[ function params=3 ]]","useRef":"[[ function params=1 ]]","useState":"[[ function params=1 ]]","useSyncExternalStore":"[[ function params=3 ]]","useTransition":"[[ function params=0 ]]","version":"19.0.0-beta-b498834eab-20240506"},"[[ cyclic ref *6 ]]",true,true,true,true,"[[ function params=0 ]]",true,"[[ function params=0 ]]"] \ No newline at end of file diff --git a/compiler/packages/snap/src/sprout/evaluator.ts b/compiler/packages/snap/src/sprout/evaluator.ts index 77e8044fb5ed5..60da5dc53cec3 100644 --- a/compiler/packages/snap/src/sprout/evaluator.ts +++ b/compiler/packages/snap/src/sprout/evaluator.ts @@ -8,16 +8,11 @@ import {render} from '@testing-library/react'; import {JSDOM} from 'jsdom'; import React, {MutableRefObject} from 'react'; -// @ts-ignore -import {c as useMemoCache} from 'react/compiler-runtime'; import util from 'util'; import {z} from 'zod'; import {fromZodError} from 'zod-validation-error'; import {initFbt, toJSON} from './shared-runtime'; -// @ts-ignore -React.c = useMemoCache; - /** * Set up the global environment for JSDOM tests. * This is a hack to let us share code and setup between the test From 38f70e3165ccb67e0da4254373ab49e6f0c7a57d Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:21 -0400 Subject: [PATCH 04/10] Update [ghstack-poisoned] --- compiler/package.json | 1 - .../infer-component-props-non-null.expect.md | 31 ++++++++++++------- .../infer-component-props-non-null.tsx | 4 +-- compiler/packages/snap/package.json | 3 +- .../scripts/link-react-compiler-runtime.sh | 10 ++++++ .../packages/snap/src/sprout/evaluator.ts | 2 +- 6 files changed, 35 insertions(+), 16 deletions(-) create mode 100755 compiler/packages/snap/scripts/link-react-compiler-runtime.sh diff --git a/compiler/package.json b/compiler/package.json index 12c6de56dc0c6..b478c9f67a41c 100644 --- a/compiler/package.json +++ b/compiler/package.json @@ -19,7 +19,6 @@ "test": "yarn workspaces run test", "snap": "yarn workspace babel-plugin-react-compiler run snap", "snap:build": "yarn workspace snap run build", - "postinstall": "perl -p -i -e 's/react\\.element/react.transitional.element/' node_modules/fbt/lib/FbtReactUtil.js && perl -p -i -e 's/didWarnAboutUsingAct = false;/didWarnAboutUsingAct = true;/' node_modules/react-dom/cjs/react-dom-test-utils.development.js", "npm:publish": "node scripts/release/publish" }, "dependencies": { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.expect.md index da9ab95fb6b53..8079511850a84 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.expect.md @@ -12,7 +12,7 @@ function Foo(props) { * as it is arg[0] of a component function */ const arr = []; - if (cond) { + if (props.cond) { arr.push(identity(props.value)); } return ; @@ -20,7 +20,7 @@ function Foo(props) { export const FIXTURE_ENTRYPOINT = { fn: Foo, - params: [{value: 2}], + params: [{value: 2, cond: true}], }; ``` @@ -32,29 +32,38 @@ import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR import { identity, Stringify } from "shared-runtime"; function Foo(props) { - const $ = _c(2); + const $ = _c(5); let t0; - if ($[0] !== props.value) { + if ($[0] !== props.cond || $[1] !== props.value) { const arr = []; - if (cond) { - arr.push(identity(props.value)); + if (props.cond) { + let t1; + if ($[3] !== props.value) { + t1 = identity(props.value); + $[3] = props.value; + $[4] = t1; + } else { + t1 = $[4]; + } + arr.push(t1); } t0 = ; - $[0] = props.value; - $[1] = t0; + $[0] = props.cond; + $[1] = props.value; + $[2] = t0; } else { - t0 = $[1]; + t0 = $[2]; } return t0; } export const FIXTURE_ENTRYPOINT = { fn: Foo, - params: [{ value: 2 }], + params: [{ value: 2, cond: true }], }; ``` ### Eval output -(kind: exception) cond is not defined \ No newline at end of file +(kind: ok)
{"arr":[2]}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.tsx index d5aa3534d4dc3..2d88e5a790294 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.tsx +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/infer-component-props-non-null.tsx @@ -8,7 +8,7 @@ function Foo(props) { * as it is arg[0] of a component function */ const arr = []; - if (cond) { + if (props.cond) { arr.push(identity(props.value)); } return ; @@ -16,5 +16,5 @@ function Foo(props) { export const FIXTURE_ENTRYPOINT = { fn: Foo, - params: [{value: 2}], + params: [{value: 2, cond: true}], }; diff --git a/compiler/packages/snap/package.json b/compiler/packages/snap/package.json index ed0b2dd6438a1..3754260dffeed 100644 --- a/compiler/packages/snap/package.json +++ b/compiler/packages/snap/package.json @@ -9,7 +9,8 @@ "src" ], "scripts": { - "build": "rimraf dist && tsc --build", + "postinstall": "./scripts/link-react-compiler-runtime.sh && perl -p -i -e 's/react\\.element/react.transitional.element/' ../../node_modules/fbt/lib/FbtReactUtil.js && perl -p -i -e 's/didWarnAboutUsingAct = false;/didWarnAboutUsingAct = true;/' ../../node_modules/react-dom/cjs/react-dom-test-utils.development.js", + "build": "rimraf dist && concurrently -n snap,runtime \"tsc --build\" \"yarn --silent workspace react-compiler-runtime build --silent\"", "test": "echo 'no tests'", "prettier": "prettier --write 'src/**/*.ts'" }, diff --git a/compiler/packages/snap/scripts/link-react-compiler-runtime.sh b/compiler/packages/snap/scripts/link-react-compiler-runtime.sh new file mode 100755 index 0000000000000..83a30d41d7c8b --- /dev/null +++ b/compiler/packages/snap/scripts/link-react-compiler-runtime.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# 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. + +set -eo pipefail + +yarn --silent workspace react-compiler-runtime link +yarn --silent workspace snap link react-compiler-runtime diff --git a/compiler/packages/snap/src/sprout/evaluator.ts b/compiler/packages/snap/src/sprout/evaluator.ts index 60da5dc53cec3..ee998d6317e4f 100644 --- a/compiler/packages/snap/src/sprout/evaluator.ts +++ b/compiler/packages/snap/src/sprout/evaluator.ts @@ -282,7 +282,7 @@ export function doEval(source: string): EvaluatorResult { } else { return { kind: "exception", - value: e.message, + value: e.stack, }; } } From 871b2a38466ebe0884476deb800fbbfd500344de Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:25 -0400 Subject: [PATCH 05/10] Update [ghstack-poisoned] --- .../src/Entrypoint/Options.ts | 63 +++++++++++++++---- compiler/packages/snap/src/compiler.ts | 21 +++++-- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index e966497256511..b092800a2e12d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -14,6 +14,7 @@ import { parseEnvironmentConfig, } from '../HIR/Environment'; import {hasOwnProperty} from '../Utils/utils'; +import {fromZodError} from 'zod-validation-error'; const PanicThresholdOptionsSchema = z.enum([ /* @@ -121,8 +122,19 @@ export type PluginOptions = { * Set this flag (on by default) to automatically check for this library and activate the support. */ enableReanimatedCheck: boolean; + + /** + * The minimum major version of React that the compiler should emit code for. If the target is 19 + * or higher, the compiler emits direct imports of React runtime APIs needed by the compiler. On + * versions prior to 19, an extra runtime package react-compiler-runtime is necessary to provide + * a userspace approximation of runtime APIs. + */ + target: CompilerReactTarget; }; +const CompilerReactTargetSchema = z.enum(['17', '18', '19']); +export type CompilerReactTarget = z.infer; + const CompilationModeSchema = z.enum([ /* * Compiles functions annotated with "use forget" or component/hook-like functions. @@ -210,6 +222,7 @@ export const defaultOptions: PluginOptions = { return filename.indexOf('node_modules') === -1; }, enableReanimatedCheck: true, + target: '19', } as const; export function parsePluginOptions(obj: unknown): PluginOptions { @@ -222,25 +235,49 @@ export function parsePluginOptions(obj: unknown): PluginOptions { // normalize string configs to be case insensitive value = value.toLowerCase(); } - if (key === 'environment') { - const environmentResult = parseEnvironmentConfig(value); - if (environmentResult.isErr()) { - CompilerError.throwInvalidConfig({ - reason: - 'Error in validating environment config. This is an advanced setting and not meant to be used directly', - description: environmentResult.unwrapErr().toString(), - suggestions: null, - loc: null, - }); + if (isCompilerFlag(key)) { + switch (key) { + case 'environment': { + const environmentResult = parseEnvironmentConfig(value); + if (environmentResult.isErr()) { + CompilerError.throwInvalidConfig({ + reason: + 'Error in validating environment config. This is an advanced setting and not meant to be used directly', + description: environmentResult.unwrapErr().toString(), + suggestions: null, + loc: null, + }); + } + parsedOptions[key] = environmentResult.unwrap(); + break; + } + case 'target': { + parsedOptions[key] = parseTargetConfig(value); + break; + } + default: { + parsedOptions[key] = value; + } } - parsedOptions[key] = environmentResult.unwrap(); - } else if (isCompilerFlag(key)) { - parsedOptions[key] = value; } } return {...defaultOptions, ...parsedOptions}; } +export function parseTargetConfig(value: unknown): CompilerReactTarget { + const parsed = CompilerReactTargetSchema.safeParse(value); + if (parsed.success) { + return parsed.data; + } else { + CompilerError.throwInvalidConfig({ + reason: 'Not a valid target', + description: `${fromZodError(parsed.error)}`, + suggestions: null, + loc: null, + }); + } +} + function isCompilerFlag(s: string): s is keyof PluginOptions { return hasOwnProperty(defaultOptions, s); } diff --git a/compiler/packages/snap/src/compiler.ts b/compiler/packages/snap/src/compiler.ts index bbb6aeded750c..f38c6f5fc2fba 100644 --- a/compiler/packages/snap/src/compiler.ts +++ b/compiler/packages/snap/src/compiler.ts @@ -12,12 +12,12 @@ import * as BabelParser from '@babel/parser'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; import assert from 'assert'; -import type { - CompilationMode, - Logger, - LoggerEvent, - PanicThresholdOptions, - PluginOptions, +import { + type CompilationMode, + type Logger, + type LoggerEvent, + type PanicThresholdOptions, + type PluginOptions, } from 'babel-plugin-react-compiler/src/Entrypoint'; import type {Effect, ValueKind} from 'babel-plugin-react-compiler/src/HIR'; import type { @@ -56,6 +56,7 @@ function makePluginOptions( let enableChangeDetectionForDebugging = null; let customMacros: null | Array = null; let validateBlocklistedImports = null; + let target = '19' as const; if (firstLine.indexOf('@compilationMode(annotation)') !== -1) { assert( @@ -107,6 +108,13 @@ function makePluginOptions( if (runtimeModuleMatch) { runtimeModule = runtimeModuleMatch[1]; } + + const targetMatch = /@target="([^"]+)"/.exec(firstLine); + if (targetMatch) { + // @ts-ignore + target = targetMatch[1]; + } + if (firstLine.includes('@panicThreshold(none)')) { panicThreshold = 'none'; } @@ -248,6 +256,7 @@ function makePluginOptions( flowSuppressions, ignoreUseNoForget, enableReanimatedCheck: false, + target, }; return [options, logs]; } From 18277731cf8f4cfc6562971f7205ae1d0a1b919b Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:29 -0400 Subject: [PATCH 06/10] Update [ghstack-poisoned] --- .../babel-plugin-react-compiler/package.json | 1 + .../scripts/link-react-compiler-runtime.sh | 10 +++++ .../src/Entrypoint/Program.ts | 39 ++++++++++++++-- .../fixtures/compiler/target-flag.expect.md | 45 +++++++++++++++++++ .../fixtures/compiler/target-flag.js | 11 +++++ .../userspace-use-memo-cache.expect.md | 20 ++++++--- .../compiler/userspace-use-memo-cache.js | 10 +++-- 7 files changed, 123 insertions(+), 13 deletions(-) create mode 100755 compiler/packages/babel-plugin-react-compiler/scripts/link-react-compiler-runtime.sh create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.js diff --git a/compiler/packages/babel-plugin-react-compiler/package.json b/compiler/packages/babel-plugin-react-compiler/package.json index fa8d5c06cf4d7..d9bfe9de7320c 100644 --- a/compiler/packages/babel-plugin-react-compiler/package.json +++ b/compiler/packages/babel-plugin-react-compiler/package.json @@ -8,6 +8,7 @@ "dist" ], "scripts": { + "postinstall": "./scripts/link-react-compiler-runtime.sh", "build": "rimraf dist && rollup --config --bundleConfigAsCjs", "test": "yarn snap:ci", "jest": "yarn build && ts-node node_modules/.bin/jest", diff --git a/compiler/packages/babel-plugin-react-compiler/scripts/link-react-compiler-runtime.sh b/compiler/packages/babel-plugin-react-compiler/scripts/link-react-compiler-runtime.sh new file mode 100755 index 0000000000000..4d1bcf1ec35d0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/scripts/link-react-compiler-runtime.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# 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. + +set -eo pipefail + +yarn --silent workspace react-compiler-runtime link +yarn --silent workspace babel-plugin-react-compiler link react-compiler-runtime diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index c2c7d8d640846..d60f86e7fa4e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -298,7 +298,6 @@ export function compileProgram( return; } const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c'); - const moduleName = pass.opts.runtimeModule ?? 'react/compiler-runtime'; /* * Record lint errors and critical errors as depending on Forget's config, @@ -605,7 +604,7 @@ export function compileProgram( if (needsMemoCacheFunctionImport) { updateMemoCacheFunctionImport( program, - moduleName, + getReactCompilerRuntimeModule(pass.opts), useMemoCacheIdentifier.name, ); } @@ -638,8 +637,12 @@ function shouldSkipCompilation( } } - const moduleName = pass.opts.runtimeModule ?? 'react/compiler-runtime'; - if (hasMemoCacheFunctionImport(program, moduleName)) { + if ( + hasMemoCacheFunctionImport( + program, + getReactCompilerRuntimeModule(pass.opts), + ) + ) { return true; } return false; @@ -1126,3 +1129,31 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel( return errors.details.length > 0 ? errors : null; } + +type ReactCompilerRuntimeModule = + | 'react/compiler-runtime' // from react namespace + | 'react-compiler-runtime'; // npm package +function getReactCompilerRuntimeModule( + opts: PluginOptions, +): ReactCompilerRuntimeModule { + let moduleName: ReactCompilerRuntimeModule | null = null; + switch (opts.target) { + case '17': + case '18': { + moduleName = 'react-compiler-runtime'; + break; + } + case '19': { + moduleName = 'react/compiler-runtime'; + break; + } + default: + CompilerError.invariant(moduleName != null, { + reason: 'Expected target to already be validated', + description: null, + loc: null, + suggestions: null, + }); + } + return moduleName; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.expect.md new file mode 100644 index 0000000000000..b4cf41e369e79 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.expect.md @@ -0,0 +1,45 @@ + +## Input + +```javascript +// @target="18" + +function Component() { + return
Hello world
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + isComponent: true, +}; + +``` + +## Code + +```javascript +import { c as _c } from "react-compiler-runtime"; // @target="18" + +function Component() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
Hello world
; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + isComponent: true, +}; + +``` + +### Eval output +(kind: ok)
Hello world
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.js new file mode 100644 index 0000000000000..5319d28f0ad59 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/target-flag.js @@ -0,0 +1,11 @@ +// @target="18" + +function Component() { + return
Hello world
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [], + isComponent: true, +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md index 386d6afbb23e0..972d1069072bb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md @@ -2,7 +2,9 @@ ## Input ```javascript -// @runtimeModule="react-forget-runtime" +// @runtimeModule="react-compiler-runtime" +import {useState} from 'react'; + function Component(props) { const [x, setX] = useState(1); let y; @@ -10,10 +12,12 @@ function Component(props) { y = x * 2; } return ( - + }}> + Click me + ); } @@ -28,7 +32,9 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react-forget-runtime"; // @runtimeModule="react-forget-runtime" +import { c as _c } from "react/compiler-runtime"; // @runtimeModule="react-compiler-runtime" +import { useState } from "react"; + function Component(props) { const $ = _c(5); const [x, setX] = useState(1); @@ -48,11 +54,13 @@ function Component(props) { let t1; if ($[3] !== t0) { t1 = ( - ); $[3] = t0; $[4] = t1; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js index 013a1be33081e..1e6d5f435dd32 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js @@ -1,4 +1,6 @@ -// @runtimeModule="react-forget-runtime" +// @runtimeModule="react-compiler-runtime" +import {useState} from 'react'; + function Component(props) { const [x, setX] = useState(1); let y; @@ -6,10 +8,12 @@ function Component(props) { y = x * 2; } return ( - + }}> + Click me + ); } From 7d312db1c2d57c4fbbf76435838ad0594f1970d1 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:18:32 -0400 Subject: [PATCH 07/10] Update [ghstack-poisoned] --- compiler/apps/playground/babel.config.js | 9 +-- .../src/Entrypoint/Options.ts | 12 --- .../userspace-use-memo-cache.expect.md | 80 ------------------- .../compiler/userspace-use-memo-cache.js | 24 ------ compiler/packages/snap/src/compiler.ts | 6 -- .../packages/snap/src/sprout/evaluator.ts | 2 +- 6 files changed, 2 insertions(+), 131 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js diff --git a/compiler/apps/playground/babel.config.js b/compiler/apps/playground/babel.config.js index 0edf2e9b72d70..954638caaff9c 100644 --- a/compiler/apps/playground/babel.config.js +++ b/compiler/apps/playground/babel.config.js @@ -9,13 +9,6 @@ module.exports = function (api) { api.cache(true); return { presets: ['next/babel'], - plugins: [ - [ - 'babel-plugin-react-compiler', - { - runtimeModule: 'react-compiler-runtime', - }, - ], - ], + plugins: [['babel-plugin-react-compiler']], }; }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index b092800a2e12d..10bcebe44e976 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -87,17 +87,6 @@ export type PluginOptions = { */ compilationMode: CompilationMode; - /* - * If enabled, Forget will import `useMemoCache` from the given module - * instead of `react/compiler-runtime`. - * - * ``` - * // If set to "react-compiler-runtime" - * import {c as useMemoCache} from 'react-compiler-runtime'; - * ``` - */ - runtimeModule?: string | null | undefined; - /** * By default React Compiler will skip compilation of code that suppresses the default * React ESLint rules, since this is a strong indication that the code may be breaking React rules @@ -214,7 +203,6 @@ export const defaultOptions: PluginOptions = { logger: null, gating: null, noEmit: false, - runtimeModule: null, eslintSuppressionRules: null, flowSuppressions: true, ignoreUseNoForget: false, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md deleted file mode 100644 index 972d1069072bb..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.expect.md +++ /dev/null @@ -1,80 +0,0 @@ - -## Input - -```javascript -// @runtimeModule="react-compiler-runtime" -import {useState} from 'react'; - -function Component(props) { - const [x, setX] = useState(1); - let y; - if (props.cond) { - y = x * 2; - } - return ( - - ); -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [true], - isComponent: true, -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @runtimeModule="react-compiler-runtime" -import { useState } from "react"; - -function Component(props) { - const $ = _c(5); - const [x, setX] = useState(1); - let y; - if ($[0] !== props.cond || $[1] !== x) { - if (props.cond) { - y = x * 2; - } - $[0] = props.cond; - $[1] = x; - $[2] = y; - } else { - y = $[2]; - } - - const t0 = y; - let t1; - if ($[3] !== t0) { - t1 = ( - - ); - $[3] = t0; - $[4] = t1; - } else { - t1 = $[4]; - } - return t1; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [true], - isComponent: true, -}; - -``` - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js deleted file mode 100644 index 1e6d5f435dd32..0000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/userspace-use-memo-cache.js +++ /dev/null @@ -1,24 +0,0 @@ -// @runtimeModule="react-compiler-runtime" -import {useState} from 'react'; - -function Component(props) { - const [x, setX] = useState(1); - let y; - if (props.cond) { - y = x * 2; - } - return ( - - ); -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [true], - isComponent: true, -}; diff --git a/compiler/packages/snap/src/compiler.ts b/compiler/packages/snap/src/compiler.ts index f38c6f5fc2fba..d4db9af2008a4 100644 --- a/compiler/packages/snap/src/compiler.ts +++ b/compiler/packages/snap/src/compiler.ts @@ -48,7 +48,6 @@ function makePluginOptions( let enableEmitFreeze = null; let enableEmitHookGuards = null; let compilationMode: CompilationMode = 'all'; - let runtimeModule = null; let panicThreshold: PanicThresholdOptions = 'all_errors'; let hookPattern: string | null = null; // TODO(@mofeiZ) rewrite snap fixtures to @validatePreserveExistingMemo:false @@ -104,10 +103,6 @@ function makePluginOptions( importSpecifierName: '$dispatcherGuard', }; } - const runtimeModuleMatch = /@runtimeModule="([^"]+)"/.exec(firstLine); - if (runtimeModuleMatch) { - runtimeModule = runtimeModuleMatch[1]; - } const targetMatch = /@target="([^"]+)"/.exec(firstLine); if (targetMatch) { @@ -251,7 +246,6 @@ function makePluginOptions( gating, panicThreshold, noEmit: false, - runtimeModule, eslintSuppressionRules, flowSuppressions, ignoreUseNoForget, diff --git a/compiler/packages/snap/src/sprout/evaluator.ts b/compiler/packages/snap/src/sprout/evaluator.ts index ee998d6317e4f..60da5dc53cec3 100644 --- a/compiler/packages/snap/src/sprout/evaluator.ts +++ b/compiler/packages/snap/src/sprout/evaluator.ts @@ -282,7 +282,7 @@ export function doEval(source: string): EvaluatorResult { } else { return { kind: "exception", - value: e.stack, + value: e.message, }; } } From 5e7d542b72c50001cff42d80a64add92dc35f7f6 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:20:25 -0400 Subject: [PATCH 08/10] Update [ghstack-poisoned] --- compiler/packages/snap/src/sprout/evaluator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/packages/snap/src/sprout/evaluator.ts b/compiler/packages/snap/src/sprout/evaluator.ts index ee998d6317e4f..60da5dc53cec3 100644 --- a/compiler/packages/snap/src/sprout/evaluator.ts +++ b/compiler/packages/snap/src/sprout/evaluator.ts @@ -282,7 +282,7 @@ export function doEval(source: string): EvaluatorResult { } else { return { kind: "exception", - value: e.stack, + value: e.message, }; } } From 51489ab8fde8ea3e92eac8b3142fbc5a227de79d Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:22:21 -0400 Subject: [PATCH 09/10] Update [ghstack-poisoned] --- compiler/packages/snap/src/compiler.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/packages/snap/src/compiler.ts b/compiler/packages/snap/src/compiler.ts index f38c6f5fc2fba..72212ea0e94d7 100644 --- a/compiler/packages/snap/src/compiler.ts +++ b/compiler/packages/snap/src/compiler.ts @@ -12,12 +12,12 @@ import * as BabelParser from '@babel/parser'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; import assert from 'assert'; -import { - type CompilationMode, - type Logger, - type LoggerEvent, - type PanicThresholdOptions, - type PluginOptions, +import type { + CompilationMode, + Logger, + LoggerEvent, + PanicThresholdOptions, + PluginOptions, } from 'babel-plugin-react-compiler/src/Entrypoint'; import type {Effect, ValueKind} from 'babel-plugin-react-compiler/src/HIR'; import type { From 7d79cf6e9aa92fcb25627f7e2f70a853dc9d0eaf Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Mon, 7 Oct 2024 15:30:40 -0400 Subject: [PATCH 10/10] Update [ghstack-poisoned] --- compiler/apps/playground/babel.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/apps/playground/babel.config.js b/compiler/apps/playground/babel.config.js index 0edf2e9b72d70..f65e0a2a79046 100644 --- a/compiler/apps/playground/babel.config.js +++ b/compiler/apps/playground/babel.config.js @@ -13,7 +13,7 @@ module.exports = function (api) { [ 'babel-plugin-react-compiler', { - runtimeModule: 'react-compiler-runtime', + target: '18', }, ], ],