Commit Graph

1397 Commits

Author SHA1 Message Date
Sathya Gunasekaran 69eb9ccccc [snap] Add option to set panicThreshold 2023-11-09 12:00:34 +00:00
Joe Savona f92a058ca5 Codegen comments to explain output 2023-11-10 11:45:29 -08:00
Joe Savona 956c1ee7e2 Repro for bug with incorrectly using context variables when name overlaps 2023-11-01 17:42:14 -07:00
Joe Savona ab0f698585 More tests for fbt whitespace handling
I experimented with more variations but prettier collapses most of them: any run 
of consecutive whitespace within a jsxtext node will get collapsed into a single 
space, for example. So the main difference in practice is whether a jsxtext node 
(preceding a value) has a trailing space/newline or not: 

``` 

Text <fbt:param /> Text 

// vs 

Text<fbt:param />Text 

``` 

which we now have tests for.
2023-11-10 11:21:10 -08:00
Joe Savona 127b5df7ca [be] Group fbt fixtures in directory 2023-11-10 11:21:07 -08:00
Joe Savona 6f2fe1e7e9 Fix FBT whitespace handling (again (again))
Nice find, @mofeiZ! I really tried to thoroughly test all the examples I could 
think of for FBT whitespace but i missed the newline case. Initially Mofei found 
[this code potentially related to whitespace 
handling](https://github.com/facebook/fbt/blob/main/packages/babel-plugin-fbt/src/fbt-nodes/FbtImplicitParamNode.js#L230-L233) 
but Babel never seems to produce consecutive JsxText nodes — this looks like 
maybe a leftover from older babel versions. 

I noticed that code wasn't actually trimming the whitspace but clearly it was 
happening somewhere, so i grepped for 'trim' and found that [this 
code](https://github.com/facebook/fbt/blob/0b4e0d13c30bffd0daa2a75715d606e3587b4e40/packages/babel-plugin-fbt/src/babel-processors/JSXFbtProcessor.js#L143) 
calls a 
[normalizeSpaces](https://github.com/facebook/fbt/blob/0b4e0d13c30bffd0daa2a75715d606e3587b4e40/packages/babel-plugin-fbt/src/FbtUtil.js#L86C10-L86C45) 
helper. Updating our logic to handle whitespace similarly just for children of 
fbt nodes produces the expected result.
2023-11-10 10:21:10 -08:00
Lauren Tan 82ed13b8b4 [eslint] Don't inline everything
Turns out this will cause an OOM in node.js when running eslint as part of the 
IDE 

``` Before: 16M   packages/eslint-plugin-react-forget/dist/index.js After:  2.2M 
 packages/eslint-plugin-react-forget/dist/index.js ```
2023-11-10 13:49:30 -05:00
Joe Savona 2927792dca Extra tests for #2339 2023-11-10 09:25:40 -08:00
Joe Savona 4742d27f9e [be] Clarify naming in scope merging pass
Anytime we have a nested `.scope` in code my brain hurts. For example 
`scope.scope.dependencies`. This PR updates the scope merging pass to use the 
name `scopeBlock` for a ReactiveScopeBlock and `scope` only for ReactiveScope 
values, to make things a bit more clear.
2023-11-09 16:33:41 -08:00
Joe Savona 691cad6b51 Optimization for scope merging when no deps
Addresses T168684688 (#2242). MergeReactiveScopesThatInvalidateTogether does not 
merge scopes if their output is not guaranteed to change when their inputs do. 
So for example a case such as `{session_id: bar(props.bar)}` will not merge the 
scopes for `t0 = bar(props.bar)` and `t1 = {session_id: t0}`, because t0 isn't 
guaranteed to change when `props.bar` does, and we want to avoid recreating the 
t1 object unless it semantically changes. 

But there's a special case: if a scope has no dependencies, then we'll never 
execute it again anyway. So it doesn't matter what kind of value it produces and 
it's safe to merge with subsequent scopes: 

```javascript 

return {session_id: bar()} 

``` 

Without the reactive input, `bar()` will always return the same value since 
we'll only ever call it once anyway. So it's safe to then merge with the scope 
for the outer object literal.
2023-11-09 16:21:00 -08:00
Sathya Gunasekaran e35df45eaf Test component name access in gated setup
Jesse flagged this as a potential bug internally but this looks correct to me. 

Adding a test case so that we don't regress in the future.
2023-11-09 10:30:54 +00:00
Sathya Gunasekaran cdf39e7956 [sprout] Add ReactForgetFeatureFlag 2023-11-09 10:30:52 +00:00
Mofei Zhang 6c327b0f3e [repro] Repro for fbt preserve whitespace bug
Current sprout output (if you remove the line in `SproutTodoFilter`): 

``` 

Failures: 

FAIL: bug-fbt-preserve-whitespace 

Difference in forget and non-forget results. 

Expected result: { 

"kind": "ok", 

"value": "Before text hello world", 

"logs": [] 

} 

Found: { 

"kind": "ok", 

"value": "Before texthello world", 

"logs": [] 

} 

``` 

`fbt` transforms run before jsx ones, so our lowering should also account for 
[fbt's whitespace 
rules](https://github.com/facebook/fbt/blob/main/packages/babel-plugin-fbt/src/fbt-nodes/FbtImplicitParamNode.js#L230-L233) 
in `BuildHIR:trimJsxText`.
2023-11-09 18:55:06 -05:00
Mofei Zhang 59ddf537ed [tests][fixtures] Enable sprout on basic existing fbt tests
--- 

Output: 

``` 

$ yarn sprout:build && yarn sprout --verbose 

... 

PASS  fbt-call-complex-param-value 

ok <div>Hello, Sathya!</div> 

PASS  fbt-call 

ok <div>2 items</div> 

PASS  fbt-template-string-same-scope 

ok <div>{"children":"for 3 experiences"}</div> 

... 

```
2023-11-09 18:55:05 -05:00
Mofei Zhang 372696c541 [sprout] Add support for fbt
Fbt + typescript [seems](https://github.com/facebook/fbt/issues/49) [to 
be](https://github.com/facebook/sfbt/issues/72) a non-blessed workflow. We do 
want to allow for both flow and typescript tests, so I followed some 
instructions [from a 
guide](https://dev.to/retyui/how-to-add-support-typescript-for-fbt-an-internationalization-framework-3lo0) 
to add support. 

- fbt tags desugar to.. "fbt" strings. By default, typescript removes unused 
imports at parse step (and on autoformat steps). We pass special 
babel-typescript configs and change vscode settings to mitigate this. 

- I tried adding `fbt` to the global scope, but the fbt transform asserts that 
`fbt` is actually imported in the source program. 

- Other hacks are available, like saying we'll only allow for fbt in flow files, 
or always patching the source code to have an "fbt" import. This seemed the most 
reasonable and easiest to debug / follow when writing tests
2023-11-09 18:55:05 -05:00
Mofei Zhang b25c14feb1 [tests][be] Clean up fixture selection logic
--- 

Refactor selection logic to be easier to read; add support for .jsx test files 
(feels a bit weird adding a `.jsx` fixture and not seeing it get run)
2023-11-09 16:50:00 -05:00
Lauren Tan 2726484823 Add license to rolledup output 2023-11-09 13:00:13 -05:00
Lauren Tan a6e492d8d0 [feedback] Add script to build packages for testapp
In order to make changes to the testapp's dependencies, we need to update the 
lockfile which means modules need to actually exist. This adds a new script to 
just run a build of the packages we sync so that module resolution in the 
testapp will work
2023-11-09 11:31:33 -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
Lauren Tan 973ec414bd [eslint] Try inlining everything in build 2023-11-08 15:19:07 -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 0beeb1e7ad Remove unncessary nullcheck 2023-11-08 16:09:18 +00:00
Sathya Gunasekaran 521f056875 Always clone DEFAULT_HOOKS
Reusing DEFAULT_HOOKS instance is a bit scary in case we mutate it by mistake 
and this gets reflected across all compiles. 

This isn't a concern now as we can't change the config during compilation. But 
this PR keeps us safe in case we change this behavior in the future. 

The tradeoff is a bit of perf which I think is the right tradeoff here.
2023-11-08 16:09:17 +00:00
Sathya Gunasekaran ec089d5454 [test] Add test to make sure stringy enums are parsed correct
Meta internal config is going to be stringy, so let's add tests.
2023-11-08 16:09:16 +00:00
Sathya Gunasekaran 6d2fce8eb5 Make CustomHooks optional and add default value
Don't propogate nullability into the plugin.
2023-11-08 16:09:16 +00:00
Sathya Gunasekaran 8b12f179d3 Add tests to make sure our config validation error message doesn't regress 2023-11-08 16:09:15 +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 42497b60f5 [error] Prefix side-effecting function names with throw
I did a double take when I thought we didn't handle returning the 

error when reading the code and when I edited the code, typescript told 

me that there's no need to return as creating the error will throw. 

This PR makes it clear from the name of the function that we will throw.
2023-11-08 16:09:13 +00:00
Sathya Gunasekaran 33ffde4836 [playground] turn of ppr 2023-11-08 15:46:43 +00:00
Sathya Gunasekaran 6405c980eb Use starred-block for multi line comments 2023-11-08 08:27:41 +00:00
Sathya Gunasekaran 3d6d81052d [be] Upgrade Eslint to 8.27.0 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
Joe Savona 64dffe9e78 repro for gating error despite no compiled functions 2023-11-07 16:09:29 -08:00
Lauren Tan 9d5ff320ba [babel] Fix default value for environment option
When an `environment` isn't explicitly provided by the user's config, we used to 
default this to `null` in `parsePluginOptions` which is called right at the 
start of the Babel plugin. These parsed options were then being passed to zod, 
which was expecting an object type, not null. 

Zod can reify a default config based on the schema, if an empty object is passed 
in. So by changing our default value to `{}` this should fix the compiler 
bailing out incorrectly on valid compiler options 

Test plan: tested this manually on the external repo by modifying node_modules
2023-11-07 18:58:21 -05: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
Joe Savona 337c17a66f More React API coverage
Redo of #2248 in its own stack
2023-11-06 16:27:11 -08:00
Joe Savona 72c27b6893 Feature flag for transitively freezing values
This PR adds a feature flag to model a potential new-in-practice rule in React: 
that freezing a function expression also freezes its closed-over values, 
transitively. For example, in the following code `data` is frozen when the 
lambda that captures it is is passed to useEffect: 

```javascript 

const data = []; 

// useEffect freezes its argument (the function expr), which transitively 
freezes its captured value data 

useEffect(() => { 

foo(data); 

}, [data]); 

data.push(true); // ERROR: mutating a frozen value 

mutate(data); // we conservatively assume this doesn't mutate but could be wrong 

``` 

Note that this rule has never been written down or enforced. It is theoretically 
equivalent to the rule (already implemented in Forget) that values captured by 
JSX are frozen: 

```javascript 

const style = {...}; 

<div style={style}>...</div> 

style.width = 10; // ERROR: mutating a frozen value 

mutate(style); // we conservatively assume this doesn't mutate but could be 
wrong 

``` 

However, JSX is typically constructed toward the very end of a render function. 
Thus in practice there isn't much subsequent code that could even modify such a 
captured value. But for the useEffect case (and other hooks that take closures 
as arguments), they tend to occur much earlier in a render function. There's 
more code that can run later and still modify the captured values, without 
causing issues in practice. The _practical_ rule today is that you can't modify 
values captured by frozen lambdas _after the component returns_: it's fine in 
practice to modify captured values between calling eg useEffect and returning 
from render. 

Thus this feature flag is fairly likely to break some percent of real product 
code. I'm adding this so that we can experiment and see how unsafe it actually 
is.
2023-11-06 08:33:32 -08:00
Joe Savona c424cbbfa7 Add validation against instructions not part of their scope
Adds an internal compiler assertion pass which checks that all the instructions 
which are necessary for constructing a given scope correctly end up within the 
corresponding ReactiveScopeBlock. All known cases where this can occur are fixed 
earlier in the stack, but this assertion will help us catch any other cases we 
haven't thought of. See docblock comment for more info. 

## Test Plan 

I manually reverted the fixes from the previous PRs while keeping the new 
fixtures, and verified that this new assertion pass flags the fixtures as 
invalid.
2023-11-06 08:33:31 -08:00
Joe Savona 35fee31355 Handle IIFE with logical mutated later
The previous changes mostly meant that we removed the label terminal and didn't 
have instructions for the same scope split in a way that we couldn't merge. But 
logicals were still causing a split because MergeConsecutiveScopes can't merge 
the blocks in that case. Here we move PruneUnusedLabels earlier in the pipeline 
to ensure that instructions from IIFEs have floated up to the parent block scope 
level.
2023-11-06 08:33:30 -08:00
Joe Savona e502e69b2d Restore React.useMemo validation
This got lost by moving the validation earlier.
2023-11-06 08:33:30 -08:00
Joe Savona 0d3f3884b8 Fix for IIFE return values mutated later
Fixes for the previous PR. What was happening is that our inference was 
inferring the correct mutable ranges and reactive scopes, but the inlining 
process left the instructions from the IIFEs inside a separate block, with a 
'label' terminal preceding it. When we converted to ReactiveFunction this was 
preserved as a ReactiveLabelTerminal, which meant that the first instruction for 
the mutable range could be nested inside one LabelTerminal, while more would be 
in a subsequent LabelTerminal. But we close blocks based on the block scope! 
This meant that we'd have leftover instructions (in the second LabelTerminal) 
that got left out of the block. 

Furthermore, because inlining was happening after EnterSSA we weren't creating 
phis correctly. This PR fixes a bunch of these issues, and a subsequent PR 
handles the remaining cases: 

* We move DropManualMemo and InlineIIFEs before EnterSSA. This means we lose the 
ability to use type information, but we ensure that we create proper SSA ids and 
phis for any reassignments within the IIFE 

* We also update PruneUnusedLabels to not just remove the unused labels, but to 
actually remove LabelTerminals that don't need them.
2023-11-06 08:33:29 -08:00