mirror of
https://github.com/facebook/react.git
synced 2026-02-21 19:31:52 +00:00
[commit] Fix for nested optional chaining within other value blocks
Fixes a longstanding issue where we didn't support code like `useFoo(value?.bar(), value?.bar()) ?? {}` - we would attempt to construct a ReactiveFunction, recursively processing the blocks, but the inner optional `value?.bar()` wouldn't match with what the outer optional was expecting to find. It's a one-line fix!
Note: memoization in the examples is not ideal, but i've confirmed that it is not strictly related to the optional issue.
This commit is contained in:
@@ -1104,7 +1104,7 @@ class Driver {
|
||||
loc,
|
||||
};
|
||||
return {
|
||||
block: init.fallthrough,
|
||||
block: final.block,
|
||||
value: sequence,
|
||||
place: final.place,
|
||||
id: final.id,
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test chained optional property access with nullish coalescing and method call
|
||||
function Component(props: {obj: {a?: {b?: {getC(): string}}} | null}) {
|
||||
'use memo';
|
||||
const result = props.obj?.a?.b?.getC() ?? 'default';
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{obj: {a: {b: {getC: () => 'deep'}}}}],
|
||||
sequentialRenders: [
|
||||
{obj: {a: {b: {getC: () => 'deep'}}}},
|
||||
{obj: null},
|
||||
{obj: {a: null}},
|
||||
{obj: {a: {b: null}}},
|
||||
{obj: {a: {b: {getC: () => 'other'}}}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test chained optional property access with nullish coalescing and method call
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(4);
|
||||
let t0;
|
||||
if ($[0] !== props.obj?.a?.b) {
|
||||
t0 = props.obj?.a?.b?.getC() ?? "default";
|
||||
$[0] = props.obj?.a?.b;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[2] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[2] = result;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
obj: {
|
||||
a: {
|
||||
b: {
|
||||
getC: () => {
|
||||
return "deep";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
obj: {
|
||||
a: {
|
||||
b: {
|
||||
getC: () => {
|
||||
return "deep";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ obj: null },
|
||||
{ obj: { a: null } },
|
||||
{ obj: { a: { b: null } } },
|
||||
{
|
||||
obj: {
|
||||
a: {
|
||||
b: {
|
||||
getC: () => {
|
||||
return "other";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"deep"}</div>
|
||||
<div>{"value":"default"}</div>
|
||||
<div>{"value":"default"}</div>
|
||||
<div>{"value":"default"}</div>
|
||||
<div>{"value":"other"}</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test chained optional property access with nullish coalescing and method call
|
||||
function Component(props: {obj: {a?: {b?: {getC(): string}}} | null}) {
|
||||
'use memo';
|
||||
const result = props.obj?.a?.b?.getC() ?? 'default';
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{obj: {a: {b: {getC: () => 'deep'}}}}],
|
||||
sequentialRenders: [
|
||||
{obj: {a: {b: {getC: () => 'deep'}}}},
|
||||
{obj: null},
|
||||
{obj: {a: null}},
|
||||
{obj: {a: {b: null}}},
|
||||
{obj: {a: {b: {getC: () => 'other'}}}},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test chained optional property access with nullish coalescing
|
||||
function Component(props: {obj: {a?: {b?: {c: string}}} | null}) {
|
||||
'use memo';
|
||||
return props.obj?.a?.b?.c ?? 'default';
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{obj: {a: {b: {c: 'deep'}}}}],
|
||||
sequentialRenders: [
|
||||
{obj: {a: {b: {c: 'deep'}}}},
|
||||
{obj: null},
|
||||
{obj: {a: null}},
|
||||
{obj: {a: {b: null}}},
|
||||
{obj: {a: {b: {c: 'other'}}}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test chained optional property access with nullish coalescing
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
return props.obj?.a?.b?.c ?? "default";
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ obj: { a: { b: { c: "deep" } } } }],
|
||||
sequentialRenders: [
|
||||
{ obj: { a: { b: { c: "deep" } } } },
|
||||
{ obj: null },
|
||||
{ obj: { a: null } },
|
||||
{ obj: { a: { b: null } } },
|
||||
{ obj: { a: { b: { c: "other" } } } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "deep"
|
||||
"default"
|
||||
"default"
|
||||
"default"
|
||||
"other"
|
||||
@@ -0,0 +1,17 @@
|
||||
// Test chained optional property access with nullish coalescing
|
||||
function Component(props: {obj: {a?: {b?: {c: string}}} | null}) {
|
||||
'use memo';
|
||||
return props.obj?.a?.b?.c ?? 'default';
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{obj: {a: {b: {c: 'deep'}}}}],
|
||||
sequentialRenders: [
|
||||
{obj: {a: {b: {c: 'deep'}}}},
|
||||
{obj: null},
|
||||
{obj: {a: null}},
|
||||
{obj: {a: {b: null}}},
|
||||
{obj: {a: {b: {c: 'other'}}}},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test deeply nested: optional in ternary condition with logical fallback using method calls
|
||||
function Component(props: {
|
||||
value: {getFlag(): boolean; getData(): string} | null;
|
||||
fallback: string;
|
||||
}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = (value?.getFlag() ? value?.getData() : null) ?? props.fallback;
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
value: {getFlag: () => true, getData: () => 'success'},
|
||||
fallback: 'default',
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
value: {getFlag: () => true, getData: () => 'success'},
|
||||
fallback: 'default',
|
||||
},
|
||||
{
|
||||
value: {getFlag: () => false, getData: () => 'success'},
|
||||
fallback: 'default',
|
||||
},
|
||||
{value: null, fallback: 'default'},
|
||||
{value: {getFlag: () => true, getData: () => 'other'}, fallback: 'default'},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test deeply nested: optional in ternary condition with logical fallback using method calls
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(5);
|
||||
|
||||
const value = props.value;
|
||||
let t0;
|
||||
if ($[0] !== props.fallback || $[1] !== value) {
|
||||
t0 = (value?.getFlag() ? value?.getData() : null) ?? props.fallback;
|
||||
$[0] = props.fallback;
|
||||
$[1] = value;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[3] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[3] = result;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
value: {
|
||||
getFlag: () => {
|
||||
return true;
|
||||
},
|
||||
getData: () => {
|
||||
return "success";
|
||||
},
|
||||
},
|
||||
fallback: "default",
|
||||
},
|
||||
],
|
||||
|
||||
sequentialRenders: [
|
||||
{
|
||||
value: {
|
||||
getFlag: () => {
|
||||
return true;
|
||||
},
|
||||
getData: () => {
|
||||
return "success";
|
||||
},
|
||||
},
|
||||
fallback: "default",
|
||||
},
|
||||
{
|
||||
value: {
|
||||
getFlag: () => {
|
||||
return false;
|
||||
},
|
||||
getData: () => {
|
||||
return "success";
|
||||
},
|
||||
},
|
||||
fallback: "default",
|
||||
},
|
||||
{ value: null, fallback: "default" },
|
||||
{
|
||||
value: {
|
||||
getFlag: () => {
|
||||
return true;
|
||||
},
|
||||
getData: () => {
|
||||
return "other";
|
||||
},
|
||||
},
|
||||
fallback: "default",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"success"}</div>
|
||||
<div>{"value":"default"}</div>
|
||||
<div>{"value":"default"}</div>
|
||||
<div>{"value":"other"}</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test deeply nested: optional in ternary condition with logical fallback using method calls
|
||||
function Component(props: {
|
||||
value: {getFlag(): boolean; getData(): string} | null;
|
||||
fallback: string;
|
||||
}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = (value?.getFlag() ? value?.getData() : null) ?? props.fallback;
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
value: {getFlag: () => true, getData: () => 'success'},
|
||||
fallback: 'default',
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
value: {getFlag: () => true, getData: () => 'success'},
|
||||
fallback: 'default',
|
||||
},
|
||||
{
|
||||
value: {getFlag: () => false, getData: () => 'success'},
|
||||
fallback: 'default',
|
||||
},
|
||||
{value: null, fallback: 'default'},
|
||||
{value: {getFlag: () => true, getData: () => 'other'}, fallback: 'default'},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test deeply nested: optional in ternary condition with logical fallback
|
||||
function Component(props: {
|
||||
value: {flag: boolean; data: string} | null;
|
||||
fallback: string;
|
||||
}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return (value?.flag ? value?.data : null) ?? props.fallback;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: {flag: true, data: 'success'}, fallback: 'default'}],
|
||||
sequentialRenders: [
|
||||
// flag true, returns data
|
||||
{value: {flag: true, data: 'success'}, fallback: 'default'},
|
||||
// flag false, ternary returns null, falls back
|
||||
{value: {flag: false, data: 'success'}, fallback: 'default'},
|
||||
// value is null, value?.flag is undefined/falsy, ternary returns null, falls back
|
||||
{value: null, fallback: 'default'},
|
||||
// different data value
|
||||
{value: {flag: true, data: 'other'}, fallback: 'default'},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test deeply nested: optional in ternary condition with logical fallback
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
|
||||
const value = props.value;
|
||||
return (value?.flag ? value?.data : null) ?? props.fallback;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: { flag: true, data: "success" }, fallback: "default" }],
|
||||
sequentialRenders: [
|
||||
// flag true, returns data
|
||||
{ value: { flag: true, data: "success" }, fallback: "default" },
|
||||
// flag false, ternary returns null, falls back
|
||||
{ value: { flag: false, data: "success" }, fallback: "default" },
|
||||
// value is null, value?.flag is undefined/falsy, ternary returns null, falls back
|
||||
{ value: null, fallback: "default" },
|
||||
// different data value
|
||||
{ value: { flag: true, data: "other" }, fallback: "default" },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "success"
|
||||
"default"
|
||||
"default"
|
||||
"other"
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test deeply nested: optional in ternary condition with logical fallback
|
||||
function Component(props: {
|
||||
value: {flag: boolean; data: string} | null;
|
||||
fallback: string;
|
||||
}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return (value?.flag ? value?.data : null) ?? props.fallback;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: {flag: true, data: 'success'}, fallback: 'default'}],
|
||||
sequentialRenders: [
|
||||
// flag true, returns data
|
||||
{value: {flag: true, data: 'success'}, fallback: 'default'},
|
||||
// flag false, ternary returns null, falls back
|
||||
{value: {flag: false, data: 'success'}, fallback: 'default'},
|
||||
// value is null, value?.flag is undefined/falsy, ternary returns null, falls back
|
||||
{value: null, fallback: 'default'},
|
||||
// different data value
|
||||
{value: {flag: true, data: 'other'}, fallback: 'default'},
|
||||
],
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useNoAlias} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ?? {};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: useFoo,
|
||||
props: [{value: null}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Todo: Unexpected terminal kind `optional` for logical test block
|
||||
|
||||
error.todo-optional-call-chain-in-logical-expr.ts:5:30
|
||||
3 | function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
4 | const value = props.value;
|
||||
> 5 | return useNoAlias(value?.x, value?.y) ?? {};
|
||||
| ^^^^^^^^ Unexpected terminal kind `optional` for logical test block
|
||||
6 | }
|
||||
7 |
|
||||
8 | export const FIXTURE_ENTRYPONT = {
|
||||
```
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useNoAlias} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ? {} : null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: useFoo,
|
||||
props: [{value: null}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Todo: Unexpected terminal kind `optional` for ternary test block
|
||||
|
||||
error.todo-optional-call-chain-in-ternary.ts:5:30
|
||||
3 | function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
4 | const value = props.value;
|
||||
> 5 | return useNoAlias(value?.x, value?.y) ? {} : null;
|
||||
| ^^^^^^^^ Unexpected terminal kind `optional` for ternary test block
|
||||
6 | }
|
||||
7 |
|
||||
8 | export const FIXTURE_ENTRYPONT = {
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test logical expression as part of optional chain base with method call
|
||||
function Component(props: {
|
||||
a: {x: {getY(): string} | null} | null;
|
||||
b: {x: {getY(): string}} | null;
|
||||
}) {
|
||||
'use memo';
|
||||
const result = (props.a || props.b)?.x?.getY();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: null, b: {x: {getY: () => 'found'}}}],
|
||||
sequentialRenders: [
|
||||
{a: null, b: {x: {getY: () => 'found'}}},
|
||||
{a: {x: {getY: () => 'first'}}, b: {x: {getY: () => 'second'}}},
|
||||
{a: null, b: null},
|
||||
{a: {x: null}, b: {x: {getY: () => 'second'}}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test logical expression as part of optional chain base with method call
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(5);
|
||||
let t0;
|
||||
if ($[0] !== props.a || $[1] !== props.b) {
|
||||
t0 = (props.a || props.b)?.x?.getY();
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[3] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[3] = result;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
a: null,
|
||||
b: {
|
||||
x: {
|
||||
getY: () => {
|
||||
return "found";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
a: null,
|
||||
b: {
|
||||
x: {
|
||||
getY: () => {
|
||||
return "found";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
a: {
|
||||
x: {
|
||||
getY: () => {
|
||||
return "first";
|
||||
},
|
||||
},
|
||||
},
|
||||
b: {
|
||||
x: {
|
||||
getY: () => {
|
||||
return "second";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ a: null, b: null },
|
||||
{
|
||||
a: { x: null },
|
||||
b: {
|
||||
x: {
|
||||
getY: () => {
|
||||
return "second";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"found"}</div>
|
||||
<div>{"value":"first"}</div>
|
||||
<div>{}</div>
|
||||
<div>{}</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test logical expression as part of optional chain base with method call
|
||||
function Component(props: {
|
||||
a: {x: {getY(): string} | null} | null;
|
||||
b: {x: {getY(): string}} | null;
|
||||
}) {
|
||||
'use memo';
|
||||
const result = (props.a || props.b)?.x?.getY();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: null, b: {x: {getY: () => 'found'}}}],
|
||||
sequentialRenders: [
|
||||
{a: null, b: {x: {getY: () => 'found'}}},
|
||||
{a: {x: {getY: () => 'first'}}, b: {x: {getY: () => 'second'}}},
|
||||
{a: null, b: null},
|
||||
{a: {x: null}, b: {x: {getY: () => 'second'}}},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test logical expression as part of optional chain base
|
||||
function Component(props: {
|
||||
a: {x: {y: string} | null} | null;
|
||||
b: {x: {y: string}} | null;
|
||||
}) {
|
||||
'use memo';
|
||||
return (props.a || props.b)?.x?.y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: null, b: {x: {y: 'found'}}}],
|
||||
sequentialRenders: [
|
||||
// a is null, uses b
|
||||
{a: null, b: {x: {y: 'found'}}},
|
||||
// a is truthy, uses a
|
||||
{a: {x: {y: 'first'}}, b: {x: {y: 'second'}}},
|
||||
// both null
|
||||
{a: null, b: null},
|
||||
// a is truthy but a.x is null
|
||||
{a: {x: null}, b: {x: {y: 'second'}}},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test logical expression as part of optional chain base
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
return (props.a || props.b)?.x?.y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: null, b: { x: { y: "found" } } }],
|
||||
sequentialRenders: [
|
||||
// a is null, uses b
|
||||
{ a: null, b: { x: { y: "found" } } },
|
||||
// a is truthy, uses a
|
||||
{ a: { x: { y: "first" } }, b: { x: { y: "second" } } },
|
||||
// both null
|
||||
{ a: null, b: null },
|
||||
// a is truthy but a.x is null
|
||||
{ a: { x: null }, b: { x: { y: "second" } } },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "found"
|
||||
"first"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Test logical expression as part of optional chain base
|
||||
function Component(props: {
|
||||
a: {x: {y: string} | null} | null;
|
||||
b: {x: {y: string}} | null;
|
||||
}) {
|
||||
'use memo';
|
||||
return (props.a || props.b)?.x?.y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: null, b: {x: {y: 'found'}}}],
|
||||
sequentialRenders: [
|
||||
// a is null, uses b
|
||||
{a: null, b: {x: {y: 'found'}}},
|
||||
// a is truthy, uses a
|
||||
{a: {x: {y: 'first'}}, b: {x: {y: 'second'}}},
|
||||
// both null
|
||||
{a: null, b: null},
|
||||
// a is truthy but a.x is null
|
||||
{a: {x: null}, b: {x: {y: 'second'}}},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test optional chaining with method calls in both branches of a ternary
|
||||
function Component(props: {
|
||||
a: {getX(): string} | null;
|
||||
b: {getY(): string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
const result = props.cond ? props.a?.getX() : props.b?.getY();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {getX: () => 'hello'}, b: {getY: () => 'world'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: {getX: () => 'hello'}, b: {getY: () => 'world'}, cond: true},
|
||||
{a: {getX: () => 'hello'}, b: {getY: () => 'world'}, cond: false},
|
||||
{a: null, b: {getY: () => 'world'}, cond: true},
|
||||
{a: {getX: () => 'hello'}, b: null, cond: false},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test optional chaining with method calls in both branches of a ternary
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(6);
|
||||
let t0;
|
||||
if ($[0] !== props.a || $[1] !== props.b || $[2] !== props.cond) {
|
||||
t0 = props.cond ? props.a?.getX() : props.b?.getY();
|
||||
$[0] = props.a;
|
||||
$[1] = props.b;
|
||||
$[2] = props.cond;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[4] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[4] = result;
|
||||
$[5] = t1;
|
||||
} else {
|
||||
t1 = $[5];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
b: {
|
||||
getY: () => {
|
||||
return "world";
|
||||
},
|
||||
},
|
||||
cond: true,
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
b: {
|
||||
getY: () => {
|
||||
return "world";
|
||||
},
|
||||
},
|
||||
cond: true,
|
||||
},
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
b: {
|
||||
getY: () => {
|
||||
return "world";
|
||||
},
|
||||
},
|
||||
cond: false,
|
||||
},
|
||||
{
|
||||
a: null,
|
||||
b: {
|
||||
getY: () => {
|
||||
return "world";
|
||||
},
|
||||
},
|
||||
cond: true,
|
||||
},
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
b: null,
|
||||
cond: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"hello"}</div>
|
||||
<div>{"value":"world"}</div>
|
||||
<div>{}</div>
|
||||
<div>{}</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test optional chaining with method calls in both branches of a ternary
|
||||
function Component(props: {
|
||||
a: {getX(): string} | null;
|
||||
b: {getY(): string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
const result = props.cond ? props.a?.getX() : props.b?.getY();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {getX: () => 'hello'}, b: {getY: () => 'world'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: {getX: () => 'hello'}, b: {getY: () => 'world'}, cond: true},
|
||||
{a: {getX: () => 'hello'}, b: {getY: () => 'world'}, cond: false},
|
||||
{a: null, b: {getY: () => 'world'}, cond: true},
|
||||
{a: {getX: () => 'hello'}, b: null, cond: false},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test optional chaining in both branches of a ternary
|
||||
function Component(props: {
|
||||
a: {x: string} | null;
|
||||
b: {y: string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
return props.cond ? props.a?.x : props.b?.y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {x: 'hello'}, b: {y: 'world'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
// cond=true, picks a?.x -> 'hello'
|
||||
{a: {x: 'hello'}, b: {y: 'world'}, cond: true},
|
||||
// cond=false, picks b?.y -> 'world'
|
||||
{a: {x: 'hello'}, b: {y: 'world'}, cond: false},
|
||||
// cond=true, a=null, picks a?.x -> undefined
|
||||
{a: null, b: {y: 'world'}, cond: true},
|
||||
// cond=false, b=null, picks b?.y -> undefined
|
||||
{a: {x: 'hello'}, b: null, cond: false},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test optional chaining in both branches of a ternary
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
return props.cond ? props.a?.x : props.b?.y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: { x: "hello" }, b: { y: "world" }, cond: true }],
|
||||
sequentialRenders: [
|
||||
// cond=true, picks a?.x -> 'hello'
|
||||
{ a: { x: "hello" }, b: { y: "world" }, cond: true },
|
||||
// cond=false, picks b?.y -> 'world'
|
||||
{ a: { x: "hello" }, b: { y: "world" }, cond: false },
|
||||
// cond=true, a=null, picks a?.x -> undefined
|
||||
{ a: null, b: { y: "world" }, cond: true },
|
||||
// cond=false, b=null, picks b?.y -> undefined
|
||||
{ a: { x: "hello" }, b: null, cond: false },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "hello"
|
||||
"world"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test optional chaining in both branches of a ternary
|
||||
function Component(props: {
|
||||
a: {x: string} | null;
|
||||
b: {y: string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
return props.cond ? props.a?.x : props.b?.y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {x: 'hello'}, b: {y: 'world'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
// cond=true, picks a?.x -> 'hello'
|
||||
{a: {x: 'hello'}, b: {y: 'world'}, cond: true},
|
||||
// cond=false, picks b?.y -> 'world'
|
||||
{a: {x: 'hello'}, b: {y: 'world'}, cond: false},
|
||||
// cond=true, a=null, picks a?.x -> undefined
|
||||
{a: null, b: {y: 'world'}, cond: true},
|
||||
// cond=false, b=null, picks b?.y -> undefined
|
||||
{a: {x: 'hello'}, b: null, cond: false},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {getX(): string; getY(): string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = useIdentity({x: value?.getX(), y: value?.getY()}) ?? {};
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{value: null}],
|
||||
sequentialRenders: [
|
||||
{value: null},
|
||||
{value: {getX: () => 'x1', getY: () => 'y1'}},
|
||||
{value: {getX: () => 'x2', getY: () => 'y2'}},
|
||||
{value: null},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify, useIdentity } from "shared-runtime";
|
||||
|
||||
function useFoo(props) {
|
||||
"use memo";
|
||||
const $ = _c(2);
|
||||
|
||||
const value = props.value;
|
||||
const result = useIdentity({ x: value?.getX(), y: value?.getY() }) ?? {};
|
||||
let t0;
|
||||
if ($[0] !== result) {
|
||||
t0 = <Stringify value={result} />;
|
||||
$[0] = result;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ value: null }],
|
||||
sequentialRenders: [
|
||||
{ value: null },
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "x1";
|
||||
},
|
||||
getY: () => {
|
||||
return "y1";
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "x2";
|
||||
},
|
||||
getY: () => {
|
||||
return "y2";
|
||||
},
|
||||
},
|
||||
},
|
||||
{ value: null },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":{}}</div>
|
||||
<div>{"value":{"x":"x1","y":"y1"}}</div>
|
||||
<div>{"value":{"x":"x2","y":"y2"}}</div>
|
||||
<div>{"value":{}}</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import {Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {getX(): string; getY(): string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = useIdentity({x: value?.getX(), y: value?.getY()}) ?? {};
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{value: null}],
|
||||
sequentialRenders: [
|
||||
{value: null},
|
||||
{value: {getX: () => 'x1', getY: () => 'y1'}},
|
||||
{value: {getX: () => 'x2', getY: () => 'y2'}},
|
||||
{value: null},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useNoAlias} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ?? {};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: useFoo,
|
||||
props: [{value: null}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useNoAlias } from "shared-runtime";
|
||||
|
||||
function useFoo(props) {
|
||||
"use memo";
|
||||
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ?? {};
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: useFoo,
|
||||
props: [{ value: null }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useNoAlias} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ?? {};
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {getX(): string; getY(): string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = useIdentity({x: value?.getX(), y: value?.getY()}) ? {} : null;
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{value: null}],
|
||||
sequentialRenders: [
|
||||
{value: null},
|
||||
{value: {getX: () => 'x1', getY: () => 'y1'}},
|
||||
{value: {getX: () => 'x2', getY: () => 'y2'}},
|
||||
{value: null},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify, useIdentity } from "shared-runtime";
|
||||
|
||||
function useFoo(props) {
|
||||
"use memo";
|
||||
const $ = _c(2);
|
||||
|
||||
const value = props.value;
|
||||
const result = useIdentity({ x: value?.getX(), y: value?.getY() })
|
||||
? {}
|
||||
: null;
|
||||
let t0;
|
||||
if ($[0] !== result) {
|
||||
t0 = <Stringify value={result} />;
|
||||
$[0] = result;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{ value: null }],
|
||||
sequentialRenders: [
|
||||
{ value: null },
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "x1";
|
||||
},
|
||||
getY: () => {
|
||||
return "y1";
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "x2";
|
||||
},
|
||||
getY: () => {
|
||||
return "y2";
|
||||
},
|
||||
},
|
||||
},
|
||||
{ value: null },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":{}}</div>
|
||||
<div>{"value":{}}</div>
|
||||
<div>{"value":{}}</div>
|
||||
<div>{"value":{}}</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import {Stringify, useIdentity} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {getX(): string; getY(): string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = useIdentity({x: value?.getX(), y: value?.getY()}) ? {} : null;
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: useFoo,
|
||||
params: [{value: null}],
|
||||
sequentialRenders: [
|
||||
{value: null},
|
||||
{value: {getX: () => 'x1', getY: () => 'y1'}},
|
||||
{value: {getX: () => 'x2', getY: () => 'y2'}},
|
||||
{value: null},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {useNoAlias} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ? {} : null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: useFoo,
|
||||
props: [{value: null}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useNoAlias } from "shared-runtime";
|
||||
|
||||
function useFoo(props) {
|
||||
"use memo";
|
||||
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ? {} : null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPONT = {
|
||||
fn: useFoo,
|
||||
props: [{ value: null }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useNoAlias} from 'shared-runtime';
|
||||
|
||||
function useFoo(props: {value: {x: string; y: string} | null}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return useNoAlias(value?.x, value?.y) ? {} : null;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test optional chaining inside logical AND (&&) with method calls
|
||||
function Component(props: {value: {getX(): string} | null; enabled: boolean}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = props.enabled && value?.getX();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: {getX: () => 'hello'}, enabled: true}],
|
||||
sequentialRenders: [
|
||||
{value: {getX: () => 'hello'}, enabled: true},
|
||||
{value: {getX: () => 'hello'}, enabled: false},
|
||||
{value: null, enabled: true},
|
||||
{value: {getX: () => 'world'}, enabled: true},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test optional chaining inside logical AND (&&) with method calls
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(5);
|
||||
|
||||
const value = props.value;
|
||||
let t0;
|
||||
if ($[0] !== props.enabled || $[1] !== value) {
|
||||
t0 = props.enabled && value?.getX();
|
||||
$[0] = props.enabled;
|
||||
$[1] = value;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[3] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[3] = result;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
enabled: false,
|
||||
},
|
||||
{ value: null, enabled: true },
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "world";
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"hello"}</div>
|
||||
<div>{"value":false}</div>
|
||||
<div>{}</div>
|
||||
<div>{"value":"world"}</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test optional chaining inside logical AND (&&) with method calls
|
||||
function Component(props: {value: {getX(): string} | null; enabled: boolean}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = props.enabled && value?.getX();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: {getX: () => 'hello'}, enabled: true}],
|
||||
sequentialRenders: [
|
||||
{value: {getX: () => 'hello'}, enabled: true},
|
||||
{value: {getX: () => 'hello'}, enabled: false},
|
||||
{value: null, enabled: true},
|
||||
{value: {getX: () => 'world'}, enabled: true},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test optional chaining inside logical AND (&&)
|
||||
function Component(props: {value: {x: string} | null; enabled: boolean}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return props.enabled && value?.x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: {x: 'hello'}, enabled: true}],
|
||||
sequentialRenders: [
|
||||
{value: {x: 'hello'}, enabled: true},
|
||||
{value: {x: 'hello'}, enabled: false},
|
||||
{value: null, enabled: true},
|
||||
{value: {x: 'world'}, enabled: true},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test optional chaining inside logical AND (&&)
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
|
||||
const value = props.value;
|
||||
return props.enabled && value?.x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: { x: "hello" }, enabled: true }],
|
||||
sequentialRenders: [
|
||||
{ value: { x: "hello" }, enabled: true },
|
||||
{ value: { x: "hello" }, enabled: false },
|
||||
{ value: null, enabled: true },
|
||||
{ value: { x: "world" }, enabled: true },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "hello"
|
||||
false
|
||||
|
||||
"world"
|
||||
@@ -0,0 +1,17 @@
|
||||
// Test optional chaining inside logical AND (&&)
|
||||
function Component(props: {value: {x: string} | null; enabled: boolean}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return props.enabled && value?.x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: {x: 'hello'}, enabled: true}],
|
||||
sequentialRenders: [
|
||||
{value: {x: 'hello'}, enabled: true},
|
||||
{value: {x: 'hello'}, enabled: false},
|
||||
{value: null, enabled: true},
|
||||
{value: {x: 'world'}, enabled: true},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test optional chaining inside logical OR (||) with method calls
|
||||
function Component(props: {value: {getX(): string} | null; fallback: string}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = value?.getX() || props.fallback;
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: null, fallback: 'default'}],
|
||||
sequentialRenders: [
|
||||
{value: null, fallback: 'default'},
|
||||
{value: {getX: () => 'hello'}, fallback: 'default'},
|
||||
{value: {getX: () => ''}, fallback: 'default'},
|
||||
{value: null, fallback: 'other'},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test optional chaining inside logical OR (||) with method calls
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(5);
|
||||
|
||||
const value = props.value;
|
||||
let t0;
|
||||
if ($[0] !== props.fallback || $[1] !== value) {
|
||||
t0 = value?.getX() || props.fallback;
|
||||
$[0] = props.fallback;
|
||||
$[1] = value;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
t0 = $[2];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[3] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[3] = result;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: null, fallback: "default" }],
|
||||
sequentialRenders: [
|
||||
{ value: null, fallback: "default" },
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "hello";
|
||||
},
|
||||
},
|
||||
fallback: "default",
|
||||
},
|
||||
{
|
||||
value: {
|
||||
getX: () => {
|
||||
return "";
|
||||
},
|
||||
},
|
||||
fallback: "default",
|
||||
},
|
||||
{ value: null, fallback: "other" },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"default"}</div>
|
||||
<div>{"value":"hello"}</div>
|
||||
<div>{"value":"default"}</div>
|
||||
<div>{"value":"other"}</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test optional chaining inside logical OR (||) with method calls
|
||||
function Component(props: {value: {getX(): string} | null; fallback: string}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
const result = value?.getX() || props.fallback;
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: null, fallback: 'default'}],
|
||||
sequentialRenders: [
|
||||
{value: null, fallback: 'default'},
|
||||
{value: {getX: () => 'hello'}, fallback: 'default'},
|
||||
{value: {getX: () => ''}, fallback: 'default'},
|
||||
{value: null, fallback: 'other'},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test optional chaining inside logical OR (||)
|
||||
function Component(props: {value: {x: string} | null; fallback: string}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return value?.x || props.fallback;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: null, fallback: 'default'}],
|
||||
sequentialRenders: [
|
||||
{value: null, fallback: 'default'},
|
||||
{value: {x: 'hello'}, fallback: 'default'},
|
||||
{value: {x: ''}, fallback: 'default'},
|
||||
{value: null, fallback: 'other'},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test optional chaining inside logical OR (||)
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
|
||||
const value = props.value;
|
||||
return value?.x || props.fallback;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: null, fallback: "default" }],
|
||||
sequentialRenders: [
|
||||
{ value: null, fallback: "default" },
|
||||
{ value: { x: "hello" }, fallback: "default" },
|
||||
{ value: { x: "" }, fallback: "default" },
|
||||
{ value: null, fallback: "other" },
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "default"
|
||||
"hello"
|
||||
"default"
|
||||
"other"
|
||||
@@ -0,0 +1,17 @@
|
||||
// Test optional chaining inside logical OR (||)
|
||||
function Component(props: {value: {x: string} | null; fallback: string}) {
|
||||
'use memo';
|
||||
const value = props.value;
|
||||
return value?.x || props.fallback;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: null, fallback: 'default'}],
|
||||
sequentialRenders: [
|
||||
{value: null, fallback: 'default'},
|
||||
{value: {x: 'hello'}, fallback: 'default'},
|
||||
{value: {x: ''}, fallback: 'default'},
|
||||
{value: null, fallback: 'other'},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test ternary expression producing the value used in optional chaining with method call
|
||||
function Component(props: {
|
||||
a: {getX(): string} | null;
|
||||
b: {getX(): string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
const obj = props.cond ? props.a : props.b;
|
||||
const result = obj?.getX();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {getX: () => 'a'}, b: {getX: () => 'b'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: {getX: () => 'a'}, b: {getX: () => 'b'}, cond: true},
|
||||
{a: {getX: () => 'a'}, b: {getX: () => 'b'}, cond: false},
|
||||
{a: null, b: {getX: () => 'b'}, cond: true},
|
||||
{a: {getX: () => 'a'}, b: null, cond: false},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
// Test ternary expression producing the value used in optional chaining with method call
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
const $ = _c(4);
|
||||
|
||||
const obj = props.cond ? props.a : props.b;
|
||||
let t0;
|
||||
if ($[0] !== obj) {
|
||||
t0 = obj?.getX();
|
||||
$[0] = obj;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const result = t0;
|
||||
let t1;
|
||||
if ($[2] !== result) {
|
||||
t1 = <Stringify value={result} />;
|
||||
$[2] = result;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "a";
|
||||
},
|
||||
},
|
||||
b: {
|
||||
getX: () => {
|
||||
return "b";
|
||||
},
|
||||
},
|
||||
cond: true,
|
||||
},
|
||||
],
|
||||
sequentialRenders: [
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "a";
|
||||
},
|
||||
},
|
||||
b: {
|
||||
getX: () => {
|
||||
return "b";
|
||||
},
|
||||
},
|
||||
cond: true,
|
||||
},
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "a";
|
||||
},
|
||||
},
|
||||
b: {
|
||||
getX: () => {
|
||||
return "b";
|
||||
},
|
||||
},
|
||||
cond: false,
|
||||
},
|
||||
{
|
||||
a: null,
|
||||
b: {
|
||||
getX: () => {
|
||||
return "b";
|
||||
},
|
||||
},
|
||||
cond: true,
|
||||
},
|
||||
{
|
||||
a: {
|
||||
getX: () => {
|
||||
return "a";
|
||||
},
|
||||
},
|
||||
b: null,
|
||||
cond: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"value":"a"}</div>
|
||||
<div>{"value":"b"}</div>
|
||||
<div>{}</div>
|
||||
<div>{}</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
import {Stringify} from 'shared-runtime';
|
||||
|
||||
// Test ternary expression producing the value used in optional chaining with method call
|
||||
function Component(props: {
|
||||
a: {getX(): string} | null;
|
||||
b: {getX(): string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
const obj = props.cond ? props.a : props.b;
|
||||
const result = obj?.getX();
|
||||
return <Stringify value={result} />;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {getX: () => 'a'}, b: {getX: () => 'b'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: {getX: () => 'a'}, b: {getX: () => 'b'}, cond: true},
|
||||
{a: {getX: () => 'a'}, b: {getX: () => 'b'}, cond: false},
|
||||
{a: null, b: {getX: () => 'b'}, cond: true},
|
||||
{a: {getX: () => 'a'}, b: null, cond: false},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// Test ternary expression producing the value used in optional chaining
|
||||
function Component(props: {
|
||||
a: {x: string} | null;
|
||||
b: {x: string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
const obj = props.cond ? props.a : props.b;
|
||||
return obj?.x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {x: 'a'}, b: {x: 'b'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: {x: 'a'}, b: {x: 'b'}, cond: true}, // picks a -> 'a'
|
||||
{a: {x: 'a'}, b: {x: 'b'}, cond: false}, // picks b -> 'b'
|
||||
{a: null, b: {x: 'b'}, cond: true}, // picks a (null) -> undefined
|
||||
{a: {x: 'a'}, b: null, cond: false}, // picks b (null) -> undefined
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// Test ternary expression producing the value used in optional chaining
|
||||
function Component(props) {
|
||||
"use memo";
|
||||
|
||||
const obj = props.cond ? props.a : props.b;
|
||||
return obj?.x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ a: { x: "a" }, b: { x: "b" }, cond: true }],
|
||||
sequentialRenders: [
|
||||
{ a: { x: "a" }, b: { x: "b" }, cond: true }, // picks a -> 'a'
|
||||
{ a: { x: "a" }, b: { x: "b" }, cond: false }, // picks b -> 'b'
|
||||
{ a: null, b: { x: "b" }, cond: true }, // picks a (null) -> undefined
|
||||
{ a: { x: "a" }, b: null, cond: false }, // picks b (null) -> undefined
|
||||
],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) "a"
|
||||
"b"
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Test ternary expression producing the value used in optional chaining
|
||||
function Component(props: {
|
||||
a: {x: string} | null;
|
||||
b: {x: string} | null;
|
||||
cond: boolean;
|
||||
}) {
|
||||
'use memo';
|
||||
const obj = props.cond ? props.a : props.b;
|
||||
return obj?.x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{a: {x: 'a'}, b: {x: 'b'}, cond: true}],
|
||||
sequentialRenders: [
|
||||
{a: {x: 'a'}, b: {x: 'b'}, cond: true}, // picks a -> 'a'
|
||||
{a: {x: 'a'}, b: {x: 'b'}, cond: false}, // picks b -> 'b'
|
||||
{a: null, b: {x: 'b'}, cond: true}, // picks a (null) -> undefined
|
||||
{a: {x: 'a'}, b: null, cond: false}, // picks b (null) -> undefined
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user