Commit Graph

62 Commits

Author SHA1 Message Date
Joe Savona f33e63838d Update copyrights to reference Meta instead of Facebook
Thanks @zpao!!! This was mostly his work, i just fixed up the last bit.
2024-04-03 08:43:36 -07:00
Sathya Gunasekaran cd3f56bb18 [eslint] Enforce generic array type syntax
Fix and enforce generic array type syntax
2024-04-02 15:31:25 +01:00
Mofei Zhang b35779c6a4 [logger] Add CompilerDiagnostic category, thread Logger into environment 2024-03-20 13:48:18 -04:00
Jordan Brown f5d99baf8e Add config option to ignore 'use no forget' directives
This will let us test without taking into account the existing 'use no forget' 
directives to better understand what validations we may need to build. 

There are two other files that look for this directive that do not seem to take 
compiler options: 

1. 
https://github.com/facebook/react-forget/blob/408617ec8a5caa815f61d4204cb3fec0775593ca/react/scripts/babel/transform-forget.js#L25 

2. 
https://github.com/facebook/react-forget/blob/408617ec8a5caa815f61d4204cb3fec0775593ca/packages/babel-plugin-react-forget/scripts/jest/makeTransform.ts#L119 

Do I need to do anything for those?
2024-03-04 19:00:48 -05:00
Sathya Gunasekaran ad8f19675c Infer type of React function
Infer if a function is a component or hook when we're deciding to compile a 
function and store that in the environment. 

This is used in passes like InferReferenceEffects rather than having to re-parse 
the name in each pass.
2024-02-29 14:47:51 -08:00
Mofei Zhang a06ded902a [other] Change instrumentation to use an optional gating identifier; record
filepath 

Internal rollout currently has a good number of test failures. 
`enableEmitInstrumentForget` can help developers understand which functions / 
files they should look at: 

``` 

// input 

function Foo() { 

userCode(); 

// ... 

} 

// output 

function Foo() { 

if (__DEV__ && inE2eTestMode) { 

logRender("Foo", "/path/to/filename.js"); 

} 

const $ = useMemoCache(...); 

userCode(); 

} 

```
2024-02-27 22:06:36 -08:00
Joe Savona a4234bcfbf infer mode only compiles component/hook decls
Forks the logic from Program.ts, letting us customize what
gets compiled.
2024-02-17 17:37:00 -08:00
Joe Savona ca3d16c5ef 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)".
2024-02-17 17:31:47 -08:00
Sathya Gunasekaran 770ca4ab3e [Babel] Add support for "use memo"
We want to start moving away from "Forget", so this PR adds support "use memo" 
and "use no memo" 

I've left "use forget" and "use no forget" directives unchanged for now, as we 
need to migrate existing users first and then come back and delete support for 
these directives.
2024-02-19 17:48:49 +00:00
Lauren Tan 5142affc26 Allow module level "use no forget"
Currently we only allow adding the directive to function bodies, but there may 
be cases where we want to always opt out an entire module from being compiled by 
Forget
2024-02-15 17:44:44 -05:00
Joe Savona ad5a272393 Skip functions using same rules as regular pipeline 2024-02-14 15:23:37 -08:00
Mike Vitousek 48024b76bb Fix lints 2024-02-09 14:50:06 -08:00
Mike Vitousek 19476aa5f6 Option to bail on Flow react-rule suppressions 2024-02-09 14:21:07 -08:00
Mofei Zhang 707d9643dd [logger] Log todo when encountering "use no forget"
--- 

This change simply logs on every function we encounter with a `use no forget` 
directive. A few nuances -- `compilationMode: "infer"` only compiles functions 
we infer to be 'react functions'. 

```js 

// `add` would not be compiled, as it has no jsx, no hook calls, 

// and is not named as a component or hook 

function add(a, b) { 

return a + b; 

} 

``` 

With this PR, we would report todos for functions that Forget wouldn't 
ordinarily try to compile. 

```js 

// Todo: Skipped due to "use no forget" directive. 

function add(a, b) { 

"use no forget"; 

return a + b; 

} 

``` 

This seems fine to me as (1) it's a bit nonsensical to have a `use no forget` 
direction on a non-react function, and (2) we're goalling on getting `use no 
forget`s down to 0.
2024-02-05 16:05:09 -05:00
Joe Savona d55420c430 Allow prefixed hooks for compiling bundled code
We're doing some internal benchmarking using a lightweight bundler that @pieterv 
wrote for experimentation purposes. It's designed to fully preserve Flow type 
annotations so we can experiment with type-driven compilation and test out what 
benefits we might get from "cross-module" compilation more easily (ie by just 
bundling together a few modules so we can see them all as one). 

However, the bundler renames local variables and imports, so that a reference to 
`useMemo()` might end up as `React$useMemo()` or similar. This PR adds a flag to 
tell the compiler that builtin hooks might be prefixed and resolve them 
appropriately.
2024-01-30 22:11:17 -05:00
Joe Savona bc145f6f1f Support customizable eslint suppressions
The compiler bails out of compiling code that contains suppressions of the 
official React ESLint rules. However, some apps may use additional rules that 
they want to trigger bailouts for, or use the official rules under a different 
name (we do this at Meta). This PR adds a compiler flag to specify a custom set 
of line rule names, suppression of which should trigger a bailout.
2024-01-29 16:58:49 -05:00
Mofei Zhang 34c89458f2 [entrypoint] Allow ref params to component functions 2024-01-19 14:59:52 -05:00
Joe Savona c3a947643f Update hoisting error message to allow error aggregation
Interpolating values into the `reason` field of an error breaks our error 
aggregation. This PR moves the offending function name into the `description` 
field which isn't used for aggregation.
2024-01-11 15:54:55 -08:00
Joe Savona 7ca3b004ae Early branch with new type inference foundation
It's starting to get complex just with a couple of extra
passes — we either need to substantially extend the HIR or (as i've done so far)
pass information from early passes to later ones. This PR changes things so that
very early in the babel plugin we fork into a separate mode. Forest has
its own `compileProgram()` equivalent, its own pipeline, its own codegen, etc.
2024-01-03 10:47:47 -08:00
Mofei Zhang 9e267714db [patch] Compile hooks with any number of args in infer mode 2023-12-07 16:14:25 -05:00
Mofei Zhang 41b164ed24 [validation] Runtime validation for hook calls
--- 

I modeled guards as try-finally blocks to be extremely explicit. An alternative 
implementation could flatten all nested hooks and only set / restore hook guards 
when entering / exiting a React function (i.e. hook or component) -- this 
alternative approach would be the easiest to represent as a separate pass 

```js 

// source 

function Foo() { 

const result = useHook(useContext(Context)); 

... 

} 

// current output 

function Foo() { 

try { 

pushHookGuard(); 

const result = (() => { 

try { 

pushEnableHook(); 

return useHook((() => { 

try { 

pushEnableHook(); 

return useContext(Context); 

} finally { 

popEnableHook(); 

} 

})()); 

} finally { 

popEnableHook(); 

}; 

})(); 

// ... 

} finally { 

popHookGuard(); 

} 

} 

// alternative output 

function Foo() { 

try { 

// check current is not lazyDispatcher; 

// save originalDispatcher, set lazyDispatcher 

pushHookGuard(); 

allowHook(); // always set originalDispatcher 

const t0 = useContext(Context); 

disallowHook(); // always set LazyDispatcher 

allowHook(); // always set originalDispatcher 

const result = useHook(t0); 

disallowHook(); // always set LazyDispatcher 

// ... 

} finally { 

popHookGuard(); // restore originalDispatcher 

} 

} 

``` 

Checked that IG Web works as expected 

Unless I add a sneaky useState: 

<img width="705" alt="Screenshot 2023-12-05 at 6 44 59 PM" 
src="https://github.com/facebook/react-forget/assets/34200447/3790bd76-7d71-44b5-a62e-f53256fb5736">
2023-12-05 15:02:34 -05:00
Mofei Zhang 37e1975050 [be] Move codegen logic Program.ts -> Codegen
--- 

Prior to this PR, we were mutating functions after CodegenReactiveFunction 
completes (in `Entrypoint/Program.ts`). 

The reasoning for this separation was that we wanted to keep non-compiler logic 
out of the core Pipeline. However, it made our code difficult to read and reason 
about. 

Open to other alternatives, like adding a pass after Codegen.
2023-12-05 15:02:33 -05:00
Joe Savona 9fb3a26174 Add logging for number of memo *blocks* (in addition to slots)
Useful for understanding and comparing how much memoization Forget applies vs 
how much developers previously memoized by hand.
2023-11-30 15:20:01 -08:00
Sathya Gunasekaran 18d4913406 [babel] Don't use source location for hoisting check
Instead of using the source location to check for hoisting, just stop checking 
for a given component after we reach it's declaration. 

By definition all references to it before are (potential) hoisting errors. 

Note that there could be false positives but that's ok.
2023-11-28 14:18:24 +00:00
Lauren Tan a1e3891189 [rfc][babel] InvalidConfig always throws
When you have your panic threshold set to "NONE" as we recommend, it's easy to 
miss that your config is wrong (which makes everything not compile) because 
those errors were being silenced. This made debugging FluentUI and the 
forget-feedback testapp pretty difficult to figure out at first, and defeats the 
purpose of having config validation in the first place. 

This pr makes it so InvalidConfig errors always throw, regardless of the panic 
threshold set. In general our plugin should never throw at build time due to 
component bailouts, but because an InvalidConfig would bailout everything from 
being compiled at all, it seems reasonable to throw here
2023-11-17 12:01:20 -05:00
Lauren Tan 6d101435d4 [Babel] Fix up eslint suppression logic
Babel doesn't attach Comment nodes to anything, so they dangle off of 

the Program node while only specifying a range. This meant that 

previously we first had to traverse all of the Program's comments to 

find an eslint suppression of the rules of React, then during traversal 

of the individual functions, we would check if there were any *global* 

eslint suppressions, then bailout all components. 

This PR updates our logic to determine if individual functions are 

affected by an eslint suppression range: 

- If an eslint suppression range falls within its body; or 

- If an eslint suppression wraps the function
2023-11-17 10:22:46 -05:00
Sathya Gunasekaran 58947d9075 [babel] Rename isReactFunction to isReactAPI
Disambiguates better between this and the existing `isReactFunctionLike` 
function.
2023-11-15 09:36:31 +00:00
Mofei Zhang 9965db70bd Revert "[babel] Remove unused PipelineError"
This reverts commit 10d129a8406e9d226abdb6943bf8512e34ce91db 

--- 

Reverts #2311 due to undocumented assumptions being broken. I also added some 
comments to `LoggerEvents` to explain each event type. 

In `Program.ts`, we have something like the following code. `compile` could 
produce any number of errors (not just expected errors / instances of 
`CompilerError`). As an example, we sometimes error in `Codegen` due to babel 
version incompatibilities (`Error: ObjectMethod: Too many arguments passed. 
Received 7 but can receive no more than 5`). 

```js 

try { 

// any error could be thrown here 

compile(input); 

} catch (e) { 

// unknown type for e 

handleError(e, ...); 

} 

```
2023-11-10 17:09:29 -05:00
Sathya Gunasekaran f13594280b [ez] Use BabelFn type 2023-11-09 08:38:00 +00:00
Lauren Tan bb92520ba5 Some existing InvalidConfig errors should be invariants
Now that we have validation of the compiler config, these old errors weren't 
categorized correctly. Readjusted them to be invariants instead.
2023-11-08 14:10:18 -05:00
Sathya Gunasekaran 7aca04a0d6 [babel] Refactor handleError to pass the error first
Pass the most important and required argument as the first parameter.
2023-11-08 16:09:19 +00:00
Sathya Gunasekaran 4e7d437880 [babel] Remove unused PipelineError
Most of the use cases are already handled with ErrorSeverity.InvalidConfig
2023-11-08 16:09:18 +00:00
Sathya Gunasekaran 3f7b9e23e3 Don't allow null to be passed to validateEnvironmentConfig
zod will throw an error if we pass null, so let's not do this. 

Adding a temporary workaround until we start validating PluginOptions.
2023-11-08 16:09:14 +00:00
Sathya Gunasekaran 6405c980eb Use starred-block for multi line comments 2023-11-08 08:27:41 +00:00
Joe Savona 702aadd82b Fix to only add imports if we compiled something
We should only add imports if we actually compiled anything, this is what caused 
the internal issue despite the file in question not having any functions 
opted-in to compilation.
2023-11-07 16:22:03 -08:00
Sathya Gunasekaran 00cb557b12 Add runtime validation for EnvironmentConfig 2023-11-07 11:04:37 +00:00
Sathya Gunasekaran 1c53385db5 Don't plumb PartialEnvironmentConfig
Let's do validation at the API level and pass around the fully parsed config 
inside the plugin.
2023-11-07 11:04:37 +00:00
Sathya Gunasekaran 58a10c0ac1 [babel] Outline insertNewFunctionDeclaration
This lets us use the parsed and validated `gating` and `instrumentForget` 
options.
2023-11-07 11:04:36 +00:00
Sathya Gunasekaran d8734b5136 Remove hasForgetMutatedOriginalSource
Derive it from compiledFns rather than duplicating state
2023-11-07 11:04:35 +00:00
Sathya Gunasekaran bccd06a632 [babel] Simplify noEmit and hasCriticalError check 2023-11-07 11:04:35 +00:00
Sathya Gunasekaran 688305a978 Check if critical error before throwing 2023-11-07 11:04:34 +00:00
Sathya Gunasekaran cf70ef899e Validate options before replacing with compiled function 2023-11-07 11:04:34 +00:00
Sathya Gunasekaran d6ff65ecc7 Add runtime validation for ExternalFunction
Use zod to do runtime validation and throw if incorrect. 

This PR only adds validation for ExternalFunction, will validate other options 
in follow on PRs.
2023-11-07 11:04:33 +00:00
Sathya Gunasekaran 60af6671d7 [ez] Check for use-before-decl only when gating is on 2023-11-02 12:32:40 -07:00
Sathya Gunasekaran 4afee45440 [ez] Add missing ts type 2023-11-02 11:10:33 -07:00
Sathya Gunasekaran adadc21021 [babel] Check if the gated component is used before decl 2023-11-02 11:10:30 -07:00
Lauren Tan 0a7bb7ac7a [babel] Clean up import logic a bit
Addressing feedback from #2218
2023-10-31 12:16:34 -04:00
Lauren Tan ac170f4f0d [babel] Allow userspace useMemoCache
Not every consumer of Forget will be able to run an experimental version of 
React. In the meantime before useMemoCache is stable, provide a way for OSS to 
pass in a userspace impl.
2023-10-31 12:16:31 -04:00
Joe Savona b02f5a5e0e Dont compile code w useMemoCache
Skips compilation of code that has a reference to `useMemoCache()`, as a 
last-resort to avoid double-compilation of code. This is meant as a quick way to 
unblock since we're still seeing some double compilation issues when syncing 
internally.
2023-09-18 09:46:04 -07:00
Joe Savona 14630c0618 Dont compile functions w multiple args (infer mode) 2023-09-14 16:22:06 -07:00