+ Hello! Current count is {count} and its double is {doubleCount}
+ {_unwrappedJsxEl}
+ The prop is {someProp}
+ {_unwrappedJsxEl2}
+ {_unwrappedJsxEl3}
+ {_unwrappedJsxEl4}
+
;
+ };
+ _segment();
+ _$unforgetCache[6].e(_unwrapped);
+ if (_$unforgetCache[7].n) {
+ _segment2();
+ _$unforgetCache[7].e(_unwrappedJsxEl);
}
- let setCount = _$unforgetCache[5].v;
- if (_$unforgetCache[5].n || _unwrapped[1] !== _$unforgetCache[8].v[1]) {
- setCount = _unwrapped[1];
- _$unforgetCache[5].e(setCount);
+ if (_$unforgetCache[10].n) {
+ _segment3();
+ _$unforgetCache[10].e(_unwrappedJsxEl2);
+ }
+ if (_$unforgetCache[11].n) {
+ _segment4();
+ _$unforgetCache[11].e(_unwrappedJsxEl3);
+ }
+ if (_$unforgetCache[12].n) {
+ _segment5();
+ _$unforgetCache[12].e(_unwrappedJsxEl4);
+ }
+ if (_$unforgetCache[8].n || _props !== _$unforgetCache[9].v) {
+ _segment6();
+ _$unforgetCache[8].e(someProp);
+ }
+ if (_$unforgetCache[4].n || _unwrapped !== _$unforgetCache[6].v) {
+ _segment7();
+ _$unforgetCache[4].e(setCount);
+ }
+ if (_$unforgetCache[1].n || _unwrapped !== _$unforgetCache[6].v) {
+ _segment8();
+ _$unforgetCache[1].e(count);
}
- let doubleCount = _$unforgetCache[2].v;
if (_$unforgetCache[2].n || count !== _$unforgetCache[1].v) {
- doubleCount = double(count);
+ _segment9();
_$unforgetCache[2].e(doubleCount);
}
- let _unwrappedJsxExp = _$unforgetCache[3].v;
- if (_$unforgetCache[3].n || setCount !== _$unforgetCache[5].v || count !== _$unforgetCache[1].v) {
- _unwrappedJsxExp = () => setCount(count + 1);
+ if (_$unforgetCache[3].n || setCount !== _$unforgetCache[4].v || count !== _$unforgetCache[1].v) {
+ _segment10();
_$unforgetCache[3].e(_unwrappedJsxExp);
}
- let _unwrappedJsxExp2 = _$unforgetCache[6].v;
- if (_$unforgetCache[6].n || setCount !== _$unforgetCache[5].v || count !== _$unforgetCache[1].v) {
- _unwrappedJsxExp2 = () => setCount(count - 1);
- _$unforgetCache[6].e(_unwrappedJsxExp2);
+ if (_$unforgetCache[5].n || setCount !== _$unforgetCache[4].v || count !== _$unforgetCache[1].v) {
+ _segment11();
+ _$unforgetCache[5].e(_unwrappedJsxExp2);
}
- let _unwrappedJsxEl = _$unforgetCache[9].v;
- if (_$unforgetCache[9].n) {
- _unwrappedJsxEl =
+ {_unwrappedJsxEl}
+ {_unwrappedJsxEl2}
+ {_unwrappedJsxExp2}
+
;
+ };
+ _segment();
+ _$unforgetCache[3].e(_unwrapped);
+ _segment2();
+ _$unforgetCache[10].e(key);
+ _segment3();
+ if (_$unforgetCache[6].n) {
+ _segment4();
+ _$unforgetCache[6].e(_unwrappedJsxExp);
}
- let _unwrappedJsxEl3 = _$unforgetCache[4].v;
- if (_$unforgetCache[4].n || _unwrappedJsxExp !== _$unforgetCache[3].v) {
- _unwrappedJsxEl3 =
- Hello! Current count is {count} and its double is {doubleCount}
+ if (_$unforgetCache[9].n || key[key] !== _$unforgetCache[10].v[key]) {
+ _segment7();
+ _$unforgetCache[9].e(value);
+ }
+ if (_$unforgetCache[8].n || movies !== _$unforgetCache[4].v || filteredMovies !== _$unforgetCache[8].v) {
+ _segment8();
+ _$unforgetCache[8].e(filteredMovies);
+ }
+ if (movies.length !== _$unforgetCache[4].v.length) {
+ _segment9();
+ }
+ if (filteredMovies.length !== _$unforgetCache[8].v.length) {
+ _segment10();
+ }
+ if (movies !== _$unforgetCache[4].v) {
+ _segment11();
+ }
+ if (_$unforgetCache[7].n || filteredMovies !== _$unforgetCache[8].v || value !== _$unforgetCache[9].v) {
+ _segment12();
+ _$unforgetCache[7].e(_unwrappedJsxExp2);
+ }
+ if (_$unforgetCache[1].n || addRandomMovie !== _$unforgetCache[2].v) {
+ _segment13();
+ _$unforgetCache[1].e(_unwrappedJsxEl);
+ }
+ if (_$unforgetCache[5].n || _unwrappedJsxExp !== _$unforgetCache[6].v) {
+ _segment14();
+ _$unforgetCache[5].e(_unwrappedJsxEl2);
+ }
+ if (_$unforgetCache[0].n || _unwrappedJsxEl !== _$unforgetCache[1].v || movies !== _$unforgetCache[4].v || _unwrappedJsxEl2 !== _$unforgetCache[5].v || _unwrappedJsxExp2 !== _$unforgetCache[7].v) {
+ _segment15();
+ _$unforgetCache[0].e(_unwrappedJsxEl3);
+ }
+ return _unwrappedJsxEl3;
+}
+export default MyComponent;"
+`;
+
+exports[`Component fixtures applyModification fixture_6 1`] = `
+"export function SimpleJSX() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl
+ */
+ 1);
+ let _unwrappedJsxEl = _$unforgetCache[0].v;
+ const _segment = () => {
+ _unwrappedJsxEl =
;
+ };
+ if (_$unforgetCache[0].n) {
+ _segment();
+ _$unforgetCache[0].e(_unwrappedJsxEl);
+ }
+ return _unwrappedJsxEl;
+}"
+`;
+
+exports[`Component fixtures applyModification fixture_7 1`] = `
+"export function SimpleJSX() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl3
+ 1 => _unwrappedJsxEl
+ 2 => _unwrappedJsxEl2
+ 3 => value
+ */
+ 4);
+ let value = _$unforgetCache[3].v;
+ const _segment = () => {
+ value = useValue();
+ };
+ let _unwrappedJsxEl = _$unforgetCache[1].v;
+ const _segment2 = () => {
+ _unwrappedJsxEl =
Hello ;
+ };
+ let _unwrappedJsxEl2 = _$unforgetCache[2].v;
+ const _segment3 = () => {
+ _unwrappedJsxEl2 =
{value} ;
+ };
+ let _unwrappedJsxEl3 = _$unforgetCache[0].v;
+ const _segment4 = () => {
+ _unwrappedJsxEl3 =
{_unwrappedJsxEl}
- The prop is {someProp}
+ {_unwrappedJsxEl2}
+
;
+ };
+ _segment();
+ _$unforgetCache[3].e(value);
+ if (_$unforgetCache[1].n) {
+ _segment2();
+ _$unforgetCache[1].e(_unwrappedJsxEl);
+ }
+ if (_$unforgetCache[2].n || value !== _$unforgetCache[3].v) {
+ _segment3();
+ _$unforgetCache[2].e(_unwrappedJsxEl2);
+ }
+ if (_$unforgetCache[0].n || _unwrappedJsxEl !== _$unforgetCache[1].v || _unwrappedJsxEl2 !== _$unforgetCache[2].v) {
+ _segment4();
+ _$unforgetCache[0].e(_unwrappedJsxEl3);
+ }
+ return _unwrappedJsxEl3;
+}"
+`;
+
+exports[`Component fixtures applyModification fixture_8 1`] = `
+"export function SimpleJSX() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl
+ 1 => _unwrappedJsxEl2
+ */
+ 2);
+ let value;
+ const _segment = () => {
+ value = useValue();
+ };
+ const _segment2 = () => {
+ if (value === "loading") {
+ let _unwrappedJsxEl = _$unforgetCache[0].v;
+ const _segment4 = () => {
+ _unwrappedJsxEl =
Loading...
;
+ };
+ if (_$unforgetCache[0].n) {
+ _segment4();
+ _$unforgetCache[0].e(_unwrappedJsxEl);
+ }
+ return _unwrappedJsxEl;
+ }
+ };
+ let _unwrappedJsxEl2 = _$unforgetCache[1].v;
+ const _segment3 = () => {
+ _unwrappedJsxEl2 =
;
+ };
+ _segment();
+ const _temp = _segment2();
+ if (_temp !== _$unforgetNull) {
+ return _temp;
+ }
+ if (_$unforgetCache[1].n) {
+ _segment3();
+ _$unforgetCache[1].e(_unwrappedJsxEl2);
+ }
+ return _unwrappedJsxEl2;
+}"
+`;
+
+exports[`Component fixtures applyModification fixture_9 1`] = `
+"function useValue() {
+ const [value, setValue] = useState(Math.random());
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setValue(Math.random());
+ }, 1000);
+ return () => clearInterval(interval);
+ }, []);
+ return value;
+}
+export function SimpleJSX() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl
+ 1 => valueWith2Decimal
+ 2 => derivedValue
+ 3 => value
+ 4 => _unwrappedJsxEl5
+ 5 => _unwrappedJsxEl2
+ 6 => _unwrappedJsxEl3
+ 7 => _unwrappedJsxEl4
+ */
+ 8);
+ let value = _$unforgetCache[3].v;
+ const _segment = () => {
+ value = useValue();
+ };
+ let _unwrappedJsxEl2 = _$unforgetCache[5].v;
+ const _segment2 = () => {
+ _unwrappedJsxEl2 =
Hello ;
+ };
+ let _unwrappedJsxEl3 = _$unforgetCache[6].v;
+ const _segment3 = () => {
+ _unwrappedJsxEl3 =
{value}
;
+ };
+ let _unwrappedJsxEl4 = _$unforgetCache[7].v;
+ const _segment4 = () => {
+ _unwrappedJsxEl4 =
{derivedValue}
;
+ };
+ let valueWith2Decimal = _$unforgetCache[1].v;
+ const _segment5 = () => {
+ valueWith2Decimal = value.toFixed(2);
+ };
+ const _segment6 = () => {
+ if (value > 0.8) {
+ let _unwrappedJsxEl = _$unforgetCache[0].v;
+ const _segment11 = () => {
+ _unwrappedJsxEl =
Loading because value is {valueWith2Decimal}...
;
+ };
+ const value = useValue();
+ const valueWith2Decimal = value.toFixed(2);
+ if (_$unforgetCache[0].n || valueWith2Decimal !== _$unforgetCache[1].v) {
+ _segment11();
+ _$unforgetCache[0].e(_unwrappedJsxEl);
+ }
+ return _unwrappedJsxEl;
+ }
+ };
+ let derivedValue = _$unforgetCache[2].v;
+ const _segment7 = () => {
+ derivedValue = "state updated to: " + valueWith2Decimal;
+ };
+ let _unwrappedJsxEl5 = _$unforgetCache[4].v;
+ const _segment8 = () => {
+ _unwrappedJsxEl5 =
{_unwrappedJsxEl2}
{_unwrappedJsxEl3}
{_unwrappedJsxEl4}
;
- _$unforgetCache[0].e(_unwrappedJsxEl5);
+ };
+ _segment();
+ _$unforgetCache[3].e(value);
+ if (_$unforgetCache[5].n) {
+ _segment2();
+ _$unforgetCache[5].e(_unwrappedJsxEl2);
+ }
+ if (_$unforgetCache[6].n) {
+ _segment3();
+ _$unforgetCache[6].e(_unwrappedJsxEl3);
+ }
+ if (_$unforgetCache[7].n) {
+ _segment4();
+ _$unforgetCache[7].e(_unwrappedJsxEl4);
+ }
+ if (_$unforgetCache[1].n || value !== _$unforgetCache[3].v) {
+ _segment5();
+ _$unforgetCache[1].e(valueWith2Decimal);
+ }
+ if (value !== _$unforgetCache[3].v) {
+ const _temp = _segment6();
+ if (_temp !== _$unforgetNull) {
+ return _temp;
+ }
+ }
+ if (_$unforgetCache[2].n || valueWith2Decimal !== _$unforgetCache[1].v) {
+ _segment7();
+ _$unforgetCache[2].e(derivedValue);
+ }
+ if (_$unforgetCache[4].n || _unwrappedJsxEl2 !== _$unforgetCache[5].v || _unwrappedJsxEl3 !== _$unforgetCache[6].v || _unwrappedJsxEl4 !== _$unforgetCache[7].v) {
+ _segment8();
+ _$unforgetCache[4].e(_unwrappedJsxEl5);
}
- _$unforgetCommit();
return _unwrappedJsxEl5;
}"
`;
+
+exports[`Component fixtures applyModification fixture_10 1`] = `
+"import React, { useState, useEffect, useMemo, useCallback } from "react";
+const fetchUser = () => {
+ return fetch("https://api.github.com/users/mohebifar").then(response => {
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+ return response.json();
+ });
+};
+const fetchUserFollowers = () => {
+ return fetch("https://api.github.com/users/mohebifar/followers").then(response => {
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+ return response.json();
+ });
+};
+function UserList() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl7
+ 1 => _unwrappedJsxEl3
+ 2 => _unwrappedJsxExp
+ 3 => user
+ 4 => _unwrappedJsxExp3
+ 5 => handleUserClick
+ 6 => _unwrapped
+ 7 => setUser
+ 8 => _unwrappedJsxEl4
+ 9 => _unwrappedJsxEl5
+ 10 => toggleEvenOnes
+ 11 => setEvenOnes
+ 12 => _unwrapped2
+ 13 => evenOnes
+ 14 => evenFollowers
+ 15 => userListElement
+ 16 => memoizedFollowers
+ 17 => followers
+ 18 => _unwrapped3
+ 19 => setFollowers
+ 20 => _unwrappedJsxExp2
+ 21 => _unwrappedJsxEl6
+ */
+ 22);
+ let _unwrapped = _$unforgetCache[6].v;
+ const _segment = () => {
+ _unwrapped = useState([]);
+ };
+ let _unwrapped3 = _$unforgetCache[18].v;
+ const _segment2 = () => {
+ _unwrapped3 = useState([]);
+ };
+ let [loading, setLoading];
+ const _segment3 = () => {
+ [loading, setLoading] = useState(true);
+ };
+ let [error, setError];
+ const _segment4 = () => {
+ [error, setError] = useState(null);
+ };
+ let _unwrapped2 = _$unforgetCache[12].v;
+ const _segment5 = () => {
+ _unwrapped2 = useState(false);
+ };
+ const _segment6 = () => {
+ useEffect(() => {
+ Promise.all([fetchUser(), fetchUserFollowers(), new Promise(resolve => setTimeout(resolve, 2000))]).then(([userData, followersData]) => {
+ setFollowers(followersData);
+ setUser(userData);
+ setLoading(false);
+ }).catch(error => {
+ setError(error.toString());
+ setLoading(false);
+ });
+ }, []);
+
+ // useCallback to memoize a hypothetical handler function
+ };
+ let handleUserClick = _$unforgetCache[5].v;
+ const _segment7 = () => {
+ handleUserClick = useCallback(userId => {
+ console.log("User clicked:", userId);
+ // Handler logic here...
+ }, []);
+ }; // without useCallback
+ let toggleEvenOnes = _$unforgetCache[10].v;
+ const _segment8 = () => {
+ toggleEvenOnes = () => {
+ setEvenOnes(prev => !prev);
+ };
+ };
+ let _unwrappedJsxEl;
+ const _segment9 = () => {
+ _unwrappedJsxEl =
Loading...
;
+ };
+ // Early return for loading state
+ const _segment10 = () => {
+ if (loading) return _unwrappedJsxEl;
+
+ // Early return for error state
+ };
+ let _unwrappedJsxEl2;
+ const _segment11 = () => {
+ _unwrappedJsxEl2 =
Error: {error}
;
+ };
+ const _segment12 = () => {
+ if (error) return _unwrappedJsxEl2;
+ };
+ let _unwrappedJsxExp2 = _$unforgetCache[20].v;
+ const _segment13 = () => {
+ _unwrappedJsxExp2 = evenOnes ? "Show Odd" : "Show Even";
+ };
+ let _unwrappedJsxEl4 = _$unforgetCache[8].v;
+ const _segment14 = () => {
+ _unwrappedJsxEl4 =
User List ;
+ };
+ let _unwrappedJsxEl6 = _$unforgetCache[21].v;
+ const _segment15 = () => {
+ _unwrappedJsxEl6 =
;
+ };
+ let setUser = _$unforgetCache[7].v;
+ const _segment16 = () => {
+ setUser = _unwrapped[1];
+ };
+ let user = _$unforgetCache[3].v;
+ const _segment17 = () => {
+ user = _unwrapped[0];
+ };
+ let setFollowers = _$unforgetCache[19].v;
+ const _segment18 = () => {
+ setFollowers = _unwrapped3[1];
+ };
+ let followers = _$unforgetCache[17].v;
+ const _segment19 = () => {
+ followers = _unwrapped3[0];
+ };
+ let setEvenOnes = _$unforgetCache[11].v;
+ const _segment20 = () => {
+ setEvenOnes = _unwrapped2[1];
+ };
+ let evenOnes = _$unforgetCache[13].v;
+ const _segment21 = () => {
+ evenOnes = _unwrapped2[0];
+ };
+ let memoizedFollowers = _$unforgetCache[16].v;
+ const _segment22 = () => {
+ memoizedFollowers = useMemo(() => followers, [followers]);
+ };
+ let evenFollowers = _$unforgetCache[14].v;
+ const _segment23 = () => {
+ evenFollowers = memoizedFollowers.filter((_, index) => index % 2 === (evenOnes ? 0 : 1));
+ };
+ let userListElement = _$unforgetCache[15].v;
+ const _segment24 = () => {
+ userListElement = evenFollowers.map(follower =>
);
+ };
+ let _unwrappedJsxExp = _$unforgetCache[2].v;
+ const _segment25 = () => {
+ _unwrappedJsxExp = user.name;
+ };
+ let _unwrappedJsxExp3 = _$unforgetCache[4].v;
+ const _segment26 = () => {
+ _unwrappedJsxExp3 = () => handleUserClick(user.id);
+ };
+ let _unwrappedJsxEl3 = _$unforgetCache[1].v;
+ const _segment27 = () => {
+ _unwrappedJsxEl3 =
Follwers of {_unwrappedJsxExp} ;
+ };
+ let _unwrappedJsxEl5 = _$unforgetCache[9].v;
+ const _segment28 = () => {
+ _unwrappedJsxEl5 =
+ {_unwrappedJsxExp2}
+ ;
+ };
+ let _unwrappedJsxEl7 = _$unforgetCache[0].v;
+ const _segment29 = () => {
+ _unwrappedJsxEl7 =
+ {_unwrappedJsxEl3}
+ {_unwrappedJsxEl4}
+ {_unwrappedJsxEl5}
+ {_unwrappedJsxEl6}
+
;
+ };
+ _segment();
+ _$unforgetCache[6].e(_unwrapped);
+ _segment2();
+ _$unforgetCache[18].e(_unwrapped3);
+ _segment3();
+ _segment4();
+ _segment5();
+ _$unforgetCache[12].e(_unwrapped2);
+ _segment6();
+ _segment7();
+ _$unforgetCache[5].e(handleUserClick);
+ if (_$unforgetCache[10].n) {
+ _segment8();
+ _$unforgetCache[10].e(toggleEvenOnes);
+ }
+ _segment9();
+ _segment10();
+ _segment11();
+ _segment12();
+ if (_$unforgetCache[20].n) {
+ _segment13();
+ _$unforgetCache[20].e(_unwrappedJsxExp2);
+ }
+ if (_$unforgetCache[8].n) {
+ _segment14();
+ _$unforgetCache[8].e(_unwrappedJsxEl4);
+ }
+ if (_$unforgetCache[21].n) {
+ _segment15();
+ _$unforgetCache[21].e(_unwrappedJsxEl6);
+ }
+ if (_$unforgetCache[7].n || _unwrapped !== _$unforgetCache[6].v) {
+ _segment16();
+ _$unforgetCache[7].e(setUser);
+ }
+ if (_$unforgetCache[3].n || _unwrapped !== _$unforgetCache[6].v) {
+ _segment17();
+ _$unforgetCache[3].e(user);
+ }
+ if (_$unforgetCache[19].n || _unwrapped3 !== _$unforgetCache[18].v) {
+ _segment18();
+ _$unforgetCache[19].e(setFollowers);
+ }
+ if (_$unforgetCache[17].n || _unwrapped3 !== _$unforgetCache[18].v) {
+ _segment19();
+ _$unforgetCache[17].e(followers);
+ }
+ if (_$unforgetCache[11].n || _unwrapped2 !== _$unforgetCache[12].v) {
+ _segment20();
+ _$unforgetCache[11].e(setEvenOnes);
+ }
+ if (_$unforgetCache[13].n || _unwrapped2 !== _$unforgetCache[12].v) {
+ _segment21();
+ _$unforgetCache[13].e(evenOnes);
+ }
+ _segment22();
+ _$unforgetCache[16].e(memoizedFollowers);
+ if (_$unforgetCache[14].n || memoizedFollowers !== _$unforgetCache[16].v || evenOnes !== _$unforgetCache[13].v) {
+ _segment23();
+ _$unforgetCache[14].e(evenFollowers);
+ }
+ if (_$unforgetCache[15].n || evenFollowers !== _$unforgetCache[14].v) {
+ _segment24();
+ _$unforgetCache[15].e(userListElement);
+ }
+ if (_$unforgetCache[2].n || user.name !== _$unforgetCache[3].v.name) {
+ _segment25();
+ _$unforgetCache[2].e(_unwrappedJsxExp);
+ }
+ if (_$unforgetCache[4].n || handleUserClick !== _$unforgetCache[5].v || user !== _$unforgetCache[3].v) {
+ _segment26();
+ _$unforgetCache[4].e(_unwrappedJsxExp3);
+ }
+ if (_$unforgetCache[1].n || _unwrappedJsxExp !== _$unforgetCache[2].v) {
+ _segment27();
+ _$unforgetCache[1].e(_unwrappedJsxEl3);
+ }
+ if (_$unforgetCache[9].n || toggleEvenOnes !== _$unforgetCache[10].v || evenOnes !== _$unforgetCache[13].v || _unwrappedJsxExp2 !== _$unforgetCache[20].v) {
+ _segment28();
+ _$unforgetCache[9].e(_unwrappedJsxEl5);
+ }
+ if (_$unforgetCache[0].n || _unwrappedJsxEl3 !== _$unforgetCache[1].v || _unwrappedJsxExp3 !== _$unforgetCache[4].v || _unwrappedJsxEl4 !== _$unforgetCache[8].v || _unwrappedJsxEl5 !== _$unforgetCache[9].v || userListElement !== _$unforgetCache[15].v || _unwrappedJsxEl6 !== _$unforgetCache[21].v) {
+ _segment29();
+ _$unforgetCache[0].e(_unwrappedJsxEl7);
+ }
+ return _unwrappedJsxEl7;
+}
+function UserListItem(_props) {
+ const [_$unforgetCache2, _$unforgetCommit2, _$unforgetNull2] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl10
+ 1 => _unwrappedJsxExp4
+ 2 => _unwrappedJsxEl8
+ 3 => _unwrappedJsxExp5
+ 4 => avatar_url
+ 5 => _props
+ 6 => login
+ 7 => html_url
+ 8 => _unwrappedJsxEl9
+ */
+ 9);
+ let _unwrappedJsxExp4 = _$unforgetCache2[1].v;
+ const _segment46 = () => {
+ _unwrappedJsxExp4 = {
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ gap: 10,
+ marginBottom: 10
+ };
+ };
+ let _unwrappedJsxExp5 = _$unforgetCache2[3].v;
+ const _segment47 = () => {
+ _unwrappedJsxExp5 = {
+ width: 100,
+ height: 100,
+ borderRadius: 20
+ };
+ };
+ let _unwrappedJsxEl9 = _$unforgetCache2[8].v;
+ const _segment48 = () => {
+ _unwrappedJsxEl9 =
{login} ;
+ };
+ let html_url = _$unforgetCache2[7].v;
+ const _segment49 = () => {
+ html_url = _props.user.html_url;
+ };
+ let avatar_url = _$unforgetCache2[4].v;
+ const _segment50 = () => {
+ avatar_url = _props.user.avatar_url;
+ };
+ let login = _$unforgetCache2[6].v;
+ const _segment51 = () => {
+ login = _props.user.login;
+ };
+ let _unwrappedJsxEl8 = _$unforgetCache2[2].v;
+ const _segment52 = () => {
+ _unwrappedJsxEl8 =
;
+ };
+ let _unwrappedJsxEl10 = _$unforgetCache2[0].v;
+ const _segment53 = () => {
+ _unwrappedJsxEl10 =
+ {_unwrappedJsxEl8}
+ {_unwrappedJsxEl9}
+ ;
+ };
+ if (_$unforgetCache2[1].n) {
+ _segment46();
+ _$unforgetCache2[1].e(_unwrappedJsxExp4);
+ }
+ if (_$unforgetCache2[3].n) {
+ _segment47();
+ _$unforgetCache2[3].e(_unwrappedJsxExp5);
+ }
+ if (_$unforgetCache2[8].n) {
+ _segment48();
+ _$unforgetCache2[8].e(_unwrappedJsxEl9);
+ }
+ if (_$unforgetCache2[7].n || _props !== _$unforgetCache2[5].v) {
+ _segment49();
+ _$unforgetCache2[7].e(html_url);
+ }
+ if (_$unforgetCache2[4].n || _props !== _$unforgetCache2[5].v) {
+ _segment50();
+ _$unforgetCache2[4].e(avatar_url);
+ }
+ if (_$unforgetCache2[6].n || _props !== _$unforgetCache2[5].v) {
+ _segment51();
+ _$unforgetCache2[6].e(login);
+ }
+ if (_$unforgetCache2[2].n || _unwrappedJsxExp5 !== _$unforgetCache2[3].v || avatar_url !== _$unforgetCache2[4].v || login !== _$unforgetCache2[6].v) {
+ _segment52();
+ _$unforgetCache2[2].e(_unwrappedJsxEl8);
+ }
+ if (_$unforgetCache2[0].n || _unwrappedJsxExp4 !== _$unforgetCache2[1].v || _unwrappedJsxEl8 !== _$unforgetCache2[2].v || login !== _$unforgetCache2[6].v || html_url !== _$unforgetCache2[7].v || _unwrappedJsxEl9 !== _$unforgetCache2[8].v) {
+ _segment53();
+ _$unforgetCache2[0].e(_unwrappedJsxEl10);
+ }
+ return _unwrappedJsxEl10;
+}
+export default UserList;"
+`;
+
+exports[`Component fixtures applyModification fixture_11 1`] = `
+"const useMovies = () => {
+ return {
+ data: [{
+ title: "The Shawshank Redemption",
+ year: 1994
+ }, {
+ title: "The Godfather",
+ year: 1972
+ }, {
+ title: "The Dark Knight",
+ year: 2008
+ }],
+ loading: false
+ };
+};
+export function MyComponent() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl
+ 1 => _unwrappedJsxEl2
+ 2 => _unwrappedJsxExp
+ 3 => filteredMovies
+ 4 => i
+ */
+ 5);
+ let i = _$unforgetCache[4].v;
+ const _segment = () => {
+ i = [];
+ };
+ const _segment2 = () => {
+ i = [{
+ title: "The Shawshank Redemption",
+ year: 1994
+ }];
+ };
+ let {
+ movies,
+ loading
+ };
+ const _segment3 = () => {
+ ({
+ movies,
+ loading
+ } = useMovies());
+ };
+ const _segment4 = () => {
+ if (loading) {
+ let _unwrappedJsxEl = _$unforgetCache[0].v;
+ const _segment11 = () => {
+ _unwrappedJsxEl =
Loading...
;
+ };
+ if (_$unforgetCache[0].n) {
+ _segment11();
+ _$unforgetCache[0].e(_unwrappedJsxEl);
+ }
+ return _unwrappedJsxEl;
+ }
+ };
+ const _segment5 = () => {
+ for (let i = 0; i < movies.length; i++) {
+ const _segment13 = () => {
+ if (movies[i].year > 2000) {
+ const _segment14 = () => {
+ filteredMovies.push(movies[i]);
+ };
+ let i = [];
+ const filteredMovies = i.concat([]);
+ if (filteredMovies !== _$unforgetCache[3].v) {
+ _segment14();
+ }
+ }
+ };
+ _segment13();
+ }
+ };
+ let filteredMovies = _$unforgetCache[3].v;
+ const _segment6 = () => {
+ filteredMovies = i.concat([]);
+ };
+ let _unwrappedJsxExp = _$unforgetCache[2].v;
+ const _segment7 = () => {
+ _unwrappedJsxExp = filteredMovies.map(movie =>
+ {movie.title}
+ {/* {value} */}
+
);
+ };
+ let _unwrappedJsxEl2 = _$unforgetCache[1].v;
+ const _segment8 = () => {
+ _unwrappedJsxEl2 =
+ {_unwrappedJsxExp}
+
;
+ };
+ if (_$unforgetCache[4].n) {
+ _segment();
+ _$unforgetCache[4].e(i);
+ }
+ _segment2();
+ _segment3();
+ const _temp = _segment4();
+ if (_temp !== _$unforgetNull) {
+ return _temp;
+ }
+ _segment5();
+ if (_$unforgetCache[3].n || i !== _$unforgetCache[4].v || filteredMovies !== _$unforgetCache[3].v) {
+ _segment6();
+ _$unforgetCache[3].e(filteredMovies);
+ }
+ if (_$unforgetCache[2].n || filteredMovies !== _$unforgetCache[3].v) {
+ _segment7();
+ _$unforgetCache[2].e(_unwrappedJsxExp);
+ }
+ if (_$unforgetCache[1].n || _unwrappedJsxExp !== _$unforgetCache[2].v) {
+ _segment8();
+ _$unforgetCache[1].e(_unwrappedJsxEl2);
+ }
+ return _unwrappedJsxEl2;
+}"
+`;
+
+exports[`Component fixtures applyModification fixture_12 1`] = `
+"export function MyComponent() {
+ const [_$unforgetCache, _$unforgetCommit, _$unforgetNull] = useCreateCache$unforget(
+ /*
+ 0 => _unwrappedJsxEl
+ 1 => value
+ */
+ 2);
+ let value = _$unforgetCache[1].v;
+ const _segment = () => {
+ value = "user";
+ };
+ let _unwrappedJsxEl = _$unforgetCache[0].v;
+ const _segment2 = () => {
+ _unwrappedJsxEl =
Loading {value}...
;
+ };
+ if (_$unforgetCache[1].n) {
+ _segment();
+ _$unforgetCache[1].e(value);
+ }
+ if (_$unforgetCache[0].n || value !== _$unforgetCache[1].v) {
+ _segment2();
+ _$unforgetCache[0].e(_unwrappedJsxEl);
+ }
+ return _unwrappedJsxEl;
+ console.log("unreachable");
+ return null;
+}"
+`;
diff --git a/packages/compiler/src/fixtures/fixture_1.js b/packages/compiler/src/fixtures/fixture_1.js
index 5174949..23a3d6c 100644
--- a/packages/compiler/src/fixtures/fixture_1.js
+++ b/packages/compiler/src/fixtures/fixture_1.js
@@ -1,9 +1,16 @@
export function MyComponent() {
- const [state, setState] = useState(0);
+ const value = [];
- const myDerivedVariable = state + 1;
+ let i = 2;
+ const k = "hi";
- const unusedVariable = 1;
+ value.push(...["Hello", "World", i]);
- return
{myDerivedVariable}
;
+ const m = i;
+
+ if (m === 2) {
+ return null;
+ }
+
+ return
{value[0]}
;
}
diff --git a/packages/compiler/src/fixtures/fixture_10.js b/packages/compiler/src/fixtures/fixture_10.js
new file mode 100644
index 0000000..e64cd64
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_10.js
@@ -0,0 +1,111 @@
+import React, { useState, useEffect, useMemo, useCallback } from "react";
+
+const fetchUser = () => {
+ return fetch("https://api.github.com/users/mohebifar").then((response) => {
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+ return response.json();
+ });
+};
+
+const fetchUserFollowers = () => {
+ return fetch("https://api.github.com/users/mohebifar/followers").then(
+ (response) => {
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+ return response.json();
+ }
+ );
+};
+
+function UserList() {
+ const [user, setUser] = useState([]);
+ const [followers, setFollowers] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [evenOnes, setEvenOnes] = useState(false);
+
+ useEffect(() => {
+ Promise.all([
+ fetchUser(),
+ fetchUserFollowers(),
+ new Promise((resolve) => setTimeout(resolve, 2000)),
+ ])
+ .then(([userData, followersData]) => {
+ setFollowers(followersData);
+ setUser(userData);
+ setLoading(false);
+ })
+ .catch((error) => {
+ setError(error.toString());
+ setLoading(false);
+ });
+ }, []);
+
+ // useCallback to memoize a hypothetical handler function
+ const handleUserClick = useCallback((userId) => {
+ console.log("User clicked:", userId);
+ // Handler logic here...
+ }, []);
+
+ // without useCallback
+ const toggleEvenOnes = () => {
+ setEvenOnes((prev) => !prev);
+ };
+
+ const memoizedFollowers = useMemo(() => followers, [followers]);
+
+ const evenFollowers = memoizedFollowers.filter(
+ (_, index) => index % 2 === (evenOnes ? 0 : 1)
+ );
+
+ // Early return for loading state
+ if (loading) return
Loading...
;
+
+ // Early return for error state
+ if (error) return
Error: {error}
;
+
+ const userListElement = evenFollowers.map((follower) => (
+
+ ));
+
+ return (
+
+
Follwers of {user.name}
+
User List
+
+ {evenOnes ? "Show Odd" : "Show Even"}
+
+
handleUserClick(user.id)}>{userListElement}
+
+ );
+}
+
+function UserListItem({ user: { login, avatar_url, html_url } }) {
+ return (
+
+
+ {login}
+
+ );
+}
+
+export default UserList;
diff --git a/packages/compiler/src/fixtures/fixture_11.js b/packages/compiler/src/fixtures/fixture_11.js
new file mode 100644
index 0000000..e4c8fe3
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_11.js
@@ -0,0 +1,41 @@
+const useMovies = () => {
+ return {
+ data: [
+ { title: "The Shawshank Redemption", year: 1994 },
+ { title: "The Godfather", year: 1972 },
+ { title: "The Dark Knight", year: 2008 },
+ ],
+ loading: false,
+ };
+};
+
+export function MyComponent() {
+ let i = [];
+
+ const filteredMovies = i.concat([]);
+
+ i = [{ title: "The Shawshank Redemption", year: 1994 }];
+
+ const { movies, loading } = useMovies();
+
+ if (loading) {
+ return
Loading...
;
+ }
+
+ for (let i = 0; i < movies.length; i++) {
+ if (movies[i].year > 2000) {
+ filteredMovies.push(movies[i]);
+ }
+ }
+
+ return (
+
+ {filteredMovies.map((movie) => (
+
+ {movie.title}
+ {/* {value} */}
+
+ ))}
+
+ );
+}
diff --git a/packages/compiler/src/fixtures/fixture_12.js b/packages/compiler/src/fixtures/fixture_12.js
new file mode 100644
index 0000000..1365ab1
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_12.js
@@ -0,0 +1,8 @@
+export function MyComponent() {
+ const value = "user";
+ return
Loading {value}...
;
+
+ console.log("unreachable");
+
+ return null;
+}
diff --git a/packages/compiler/src/fixtures/fixture_5.js b/packages/compiler/src/fixtures/fixture_5.js
new file mode 100644
index 0000000..18de669
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_5.js
@@ -0,0 +1,90 @@
+const words = [
+ "Shawshank",
+ "Redemption",
+ "Godfather",
+ "Dark",
+ "Knight",
+ "Forest",
+ "Gump",
+ "Private",
+ "Ryan",
+];
+
+const initalMovies = [
+ {
+ title: "The Shawshank Redemption",
+ year: 1994,
+ },
+ {
+ title: "The Godfather",
+ year: 1972,
+ },
+ {
+ title: "The Dark Knight",
+ year: 2008,
+ },
+];
+
+const useMovies = () => {
+ const [movies, setMovies] = useState(initalMovies);
+
+ const addRandomMovie = () => {
+ setMovies((p) => [
+ ...p,
+ {
+ title:
+ "The " +
+ words[Math.floor(Math.random() * words.length)] +
+ " " +
+ words[Math.floor(Math.random() * words.length)],
+ year: 1990 + Math.floor(Math.random() * 20),
+ },
+ ]);
+ };
+
+ return [movies, addRandomMovie];
+};
+
+const useKey = () => "key1";
+
+const object = { key1: "value1", key2: "value2" };
+
+export function MyComponent() {
+ const [movies, addRandomMovie] = useMovies();
+
+ const key = useKey();
+
+ const value = object[key];
+
+ const filteredMovies = [];
+
+ const i = 5;
+
+ for (let i = 0; i < movies.length; i++) {
+ if (movies[i].year > 2000) {
+ filteredMovies.push(movies[i]);
+ }
+ }
+
+ if (filteredMovies.length > 0) {
+ // Just some side effect
+ console.log("Movies after 2000: ", filteredMovies);
+ }
+
+ console.log("Total number of movies: ", movies.length);
+
+ return (
+
+
Add random movie
+
total number of movies: {movies.length}
+ {filteredMovies.map((movie) => (
+
+ {movie.title}
+ {value}
+
+ ))}
+
+ );
+}
+
+export default MyComponent;
diff --git a/packages/compiler/src/fixtures/fixture_6.js b/packages/compiler/src/fixtures/fixture_6.js
new file mode 100644
index 0000000..858900e
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_6.js
@@ -0,0 +1,3 @@
+export function SimpleJSX() {
+ return
;
+}
diff --git a/packages/compiler/src/fixtures/fixture_7.js b/packages/compiler/src/fixtures/fixture_7.js
new file mode 100644
index 0000000..3bee389
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_7.js
@@ -0,0 +1,10 @@
+export function SimpleJSX() {
+ const value = useValue();
+
+ return (
+
+ Hello
+ {value}
+
+ );
+}
diff --git a/packages/compiler/src/fixtures/fixture_8.js b/packages/compiler/src/fixtures/fixture_8.js
new file mode 100644
index 0000000..378be91
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_8.js
@@ -0,0 +1,9 @@
+export function SimpleJSX() {
+ const value = useValue();
+
+ if (value === "loading") {
+ return
Loading...
;
+ }
+
+ return
;
+}
diff --git a/packages/compiler/src/fixtures/fixture_9.js b/packages/compiler/src/fixtures/fixture_9.js
new file mode 100644
index 0000000..8b7f946
--- /dev/null
+++ b/packages/compiler/src/fixtures/fixture_9.js
@@ -0,0 +1,31 @@
+function useValue() {
+ const [value, setValue] = useState(Math.random());
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setValue(Math.random());
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, []);
+ return value;
+}
+
+export function SimpleJSX() {
+ const value = useValue();
+
+ const valueWith2Decimal = value.toFixed(2);
+
+ if (value > 0.8) {
+ return
Loading because value is {valueWith2Decimal}...
;
+ }
+
+ const derivedValue = "state updated to: " + valueWith2Decimal;
+
+ return (
+
+
Hello
+
{value}
+
{derivedValue}
+
+ );
+}
diff --git a/packages/compiler/src/utils/constants.ts b/packages/compiler/src/utils/constants.ts
index bbf87e2..9d8f3ac 100644
--- a/packages/compiler/src/utils/constants.ts
+++ b/packages/compiler/src/utils/constants.ts
@@ -2,12 +2,33 @@ export const RUNTIME_MODULE_NAME = "@react-unforget/runtime";
export const RUNTIME_MODULE_CREATE_CACHE_HOOK_NAME = "useCreateCache$unforget";
export const DEFAULT_CACHE_VARIABLE_NAME = "$unforgetCache";
export const DEFAULT_CACHE_COMMIT_VARIABLE_NAME = "$unforgetCommit";
+export const DEFAULT_CACHE_NULL_VARIABLE_NAME = "$unforgetNull";
export const DEFAULT_UNUSED_VARIABLE_NAME = "_unused";
export const DEFAULT_UNWRAPPED_VARIABLE_NAME = "_unwrapped";
-export const DEFAULT_UNWRAPPED_JSX_EXPRESSION_VARIABLE_NAME = "_unwrappedJsxExp";
+export const DEFAULT_UNWRAPPED_JSX_EXPRESSION_VARIABLE_NAME =
+ "_unwrappedJsxExp";
+export const DEFAULT_UNWRAPPED_PROPS_VARIABLE_NAME = "_props";
export const DEFAULT_UNWRAPPED_JSX_ELEMENT_VARIABLE_NAME = "_unwrappedJsxEl";
+export const DEFAULT_SIDE_EFFECT_VARIABLE_NAME = "_sideEffect";
+export const DEFAULT_SEGMENT_CALLABLE_VARIABLE_NAME = "_segment";
export const RUNTIME_MODULE_CACHE_ENQUEUE_METHOD_NAME = "e";
export const RUNTIME_MODULE_CACHE_VALUE_PROP_NAME = "v";
export const RUNTIME_MODULE_CACHE_IS_NOT_SET_PROP_NAME = "n";
+
+export const MUTATING_METHODS = [
+ "push",
+ "pop",
+ "shift",
+ "unshift",
+ "splice",
+ "sort",
+ "reverse",
+ "copyWithin",
+ "fill",
+ "set",
+ "delete",
+ "add",
+ "clear",
+];
diff --git a/packages/compiler/src/utils/errors/CircularDependencyError.ts b/packages/compiler/src/utils/errors/CircularDependencyError.ts
new file mode 100644
index 0000000..da414b5
--- /dev/null
+++ b/packages/compiler/src/utils/errors/CircularDependencyError.ts
@@ -0,0 +1,10 @@
+export class CircularDependencyError extends Error {
+ constructor(from: babel.NodePath, to: babel.NodePath) {
+ super(
+ "Circular dependency detected - from: " +
+ from.toString() +
+ " to: " +
+ to.toString()
+ );
+ }
+}
diff --git a/packages/compiler/src/utils/errors/LeftmostIdNotFound.ts b/packages/compiler/src/utils/errors/LeftmostIdNotFound.ts
new file mode 100644
index 0000000..c17cd87
--- /dev/null
+++ b/packages/compiler/src/utils/errors/LeftmostIdNotFound.ts
@@ -0,0 +1,7 @@
+import type * as t from "@babel/types";
+
+export class LeftmostIdNotFound extends Error {
+ constructor(path: t.Node) {
+ super("Could not find leftmost identifier name for" + path.toString());
+ }
+}
diff --git a/packages/compiler/src/utils/errors/RightmostIdNotFound.ts b/packages/compiler/src/utils/errors/RightmostIdNotFound.ts
index 502c8bc..4ed1263 100644
--- a/packages/compiler/src/utils/errors/RightmostIdNotFound.ts
+++ b/packages/compiler/src/utils/errors/RightmostIdNotFound.ts
@@ -1,7 +1,7 @@
-import type * as babel from "@babel/core";
+import * as t from "@babel/types";
export class RightmostIdNotFound extends Error {
- constructor(path: babel.NodePath) {
+ constructor(path: t.Node) {
super("Could not find rightmost identifier name for" + path.toString());
}
}
diff --git a/packages/compiler/src/utils/find-mutating-expression.ts b/packages/compiler/src/utils/find-mutating-expression.ts
new file mode 100644
index 0000000..ac8f478
--- /dev/null
+++ b/packages/compiler/src/utils/find-mutating-expression.ts
@@ -0,0 +1,49 @@
+import { MUTATING_METHODS } from "./constants";
+import { getDeclaredIdentifiersInLVal } from "./get-declared-identifiers-in-lval";
+import { getLeftmostIdName } from "./get-leftmost-id-name";
+import { getRightmostIdName } from "./get-rightmost-id-name";
+
+export function findMutatingExpression(
+ path: babel.NodePath
,
+ name: string
+) {
+ const result = path.find((innerPath) => {
+ if (innerPath.isStatement()) {
+ return true;
+ }
+
+ if (innerPath.isAssignmentExpression()) {
+ const left = innerPath.node.left;
+ const leftMostIds = innerPath.isLVal()
+ ? getDeclaredIdentifiersInLVal(innerPath)
+ : [getLeftmostIdName(left)];
+
+ return leftMostIds.includes(name);
+ }
+
+ if (innerPath.isUpdateExpression()) {
+ return getLeftmostIdName(innerPath.node.argument) === name;
+ }
+
+ if (innerPath.isCallExpression()) {
+ const callee = innerPath.get("callee");
+
+ if (callee.isMemberExpression()) {
+ const leftMostId = getLeftmostIdName(callee.node);
+ const rightmostId = getRightmostIdName(callee.node);
+
+ if (MUTATING_METHODS.includes(rightmostId)) {
+ return leftMostId === name;
+ }
+ }
+ }
+
+ return false;
+ });
+
+ if (result?.isStatement()) {
+ return null;
+ }
+
+ return result as babel.NodePath;
+}
diff --git a/packages/compiler/src/utils/get-block-statements-of-path.ts b/packages/compiler/src/utils/get-block-statements-of-path.ts
new file mode 100644
index 0000000..9ffbed8
--- /dev/null
+++ b/packages/compiler/src/utils/get-block-statements-of-path.ts
@@ -0,0 +1,98 @@
+export function getBlockStatementsOfPath(
+ path: babel.NodePath
+): babel.NodePath[] {
+ if (path.isBlockStatement()) {
+ return [path];
+ }
+
+ if (path.isIfStatement()) {
+ const consequent = path.get("consequent");
+ const alternate = path.get("alternate");
+
+ if (consequent.isBlockStatement()) {
+ return [consequent];
+ }
+
+ if (consequent.isIfStatement()) {
+ return getBlockStatementsOfPath(consequent);
+ }
+
+ if (alternate && alternate.isBlockStatement()) {
+ return [alternate];
+ }
+
+ if (alternate && alternate.isIfStatement()) {
+ return getBlockStatementsOfPath(alternate);
+ }
+ }
+
+ if (path.isWhileStatement()) {
+ const body = path.get("body");
+
+ if (body.isBlockStatement()) {
+ return [body];
+ }
+ }
+
+ if (path.isDoWhileStatement()) {
+ const body = path.get("body");
+
+ if (body.isBlockStatement()) {
+ return [body];
+ }
+ }
+
+ if (path.isForStatement()) {
+ const body = path.get("body");
+
+ if (body.isBlockStatement()) {
+ return [body];
+ }
+ }
+
+ if (path.isForInStatement()) {
+ const body = path.get("body");
+
+ if (body.isBlockStatement()) {
+ return [body];
+ }
+ }
+
+ if (path.isForOfStatement()) {
+ const body = path.get("body");
+
+ if (body.isBlockStatement()) {
+ return [body];
+ }
+ }
+
+ if (path.isSwitchStatement()) {
+ const cases = path.get("cases");
+
+ const blockStatements: babel.NodePath[] = [];
+
+ cases.forEach((c) => {
+ const consequent = c.get("consequent");
+
+ if (consequent.length > 0) {
+ consequent.forEach((consequentStatement) => {
+ if (consequentStatement.isBlockStatement()) {
+ blockStatements.push(consequentStatement);
+ }
+ });
+ }
+ });
+
+ return blockStatements;
+ }
+
+ if (path.isTryStatement()) {
+ const block = path.get("block");
+
+ if (block.isBlockStatement()) {
+ return [block];
+ }
+ }
+
+ return [];
+}
diff --git a/packages/compiler/src/utils/get-leftmost-id-name.ts b/packages/compiler/src/utils/get-leftmost-id-name.ts
new file mode 100644
index 0000000..2651bba
--- /dev/null
+++ b/packages/compiler/src/utils/get-leftmost-id-name.ts
@@ -0,0 +1,16 @@
+import * as t from "@babel/types";
+import { LeftmostIdNotFound } from "./errors/LeftmostIdNotFound";
+
+export function getLeftmostIdName(
+ node: babel.types.LVal | babel.types.Expression
+): string {
+ if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
+ const object = node.object;
+
+ return getLeftmostIdName(object);
+ } else if (t.isIdentifier(node)) {
+ return node.name;
+ }
+
+ throw new LeftmostIdNotFound(node);
+}
diff --git a/packages/compiler/src/utils/get-referenced-variables-inside.ts b/packages/compiler/src/utils/get-referenced-variables-inside.ts
index b274ea6..11ecea2 100644
--- a/packages/compiler/src/utils/get-referenced-variables-inside.ts
+++ b/packages/compiler/src/utils/get-referenced-variables-inside.ts
@@ -5,24 +5,20 @@ import { isReferenceIdentifier } from "./is-reference-identifier";
export function getReferencedVariablesInside(
path: babel.NodePath
) {
- const references = new Set();
+ const map = new Map, Binding>();
path.traverse({
Identifier(innerPath) {
if (isReferenceIdentifier(innerPath)) {
- references.add(innerPath.node.name);
+ const name = innerPath.node.name;
+
+ const binding = path.scope.getBinding(name);
+ if (binding) {
+ map.set(innerPath, binding);
+ }
}
},
});
- const bindings: Binding[] = [];
-
- references.forEach((name) => {
- const binding = path.scope.getBinding(name);
- if (binding) {
- bindings.push(binding);
- }
- });
-
- return bindings;
+ return map;
}
diff --git a/packages/compiler/src/utils/get-returns-of-function.ts b/packages/compiler/src/utils/get-returns-of-function.ts
index a019f15..24f379d 100644
--- a/packages/compiler/src/utils/get-returns-of-function.ts
+++ b/packages/compiler/src/utils/get-returns-of-function.ts
@@ -1,12 +1,12 @@
import type * as babel from "@babel/core";
-import { getFunctionParent } from "./get-function-parent";
+import { isInTheSameFunctionScope } from "./is-in-the-same-function-scope";
export function getReturnsOfFunction(fn: babel.NodePath) {
const returns: babel.NodePath[] = [];
fn.traverse({
ReturnStatement(path) {
- if (getFunctionParent(path)?.node === fn.node) {
+ if (isInTheSameFunctionScope(path, fn)) {
returns.push(path);
}
},
diff --git a/packages/compiler/src/utils/get-rightmost-id-name.ts b/packages/compiler/src/utils/get-rightmost-id-name.ts
index 8f6061c..0835e64 100644
--- a/packages/compiler/src/utils/get-rightmost-id-name.ts
+++ b/packages/compiler/src/utils/get-rightmost-id-name.ts
@@ -1,24 +1,22 @@
-import type * as babel from "@babel/core";
+import * as t from "@babel/types";
import { RightmostIdNotFound } from "./errors/RightmostIdNotFound";
export function getRightmostIdName(
- path: babel.NodePath<
- babel.types.Expression | babel.types.V8IntrinsicIdentifier
- >
+ node: t.Expression | t.V8IntrinsicIdentifier
): string {
- if (path.isMemberExpression()) {
- const property = path.get("property");
+ if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
+ const property = node.property;
- if (property.isStringLiteral()) {
- return property.node.value;
- } else if (property.isIdentifier() && !path.node.computed) {
- return property.node.name;
- } else if (property.isNumericLiteral()) {
- return property.node.value.toString();
+ if (t.isStringLiteral(property)) {
+ return property.value;
+ } else if (t.isIdentifier(property) && !node.computed) {
+ return property.name;
+ } else if (t.isNumericLiteral(property)) {
+ return property.value.toString();
}
- } else if (path.isIdentifier()) {
- return path.node.name;
+ } else if (t.isIdentifier(node)) {
+ return node.name;
}
- throw new RightmostIdNotFound(path);
+ throw new RightmostIdNotFound(node);
}
diff --git a/packages/compiler/src/utils/is-hook-call.ts b/packages/compiler/src/utils/is-hook-call.ts
index 3de5283..d36fa3d 100644
--- a/packages/compiler/src/utils/is-hook-call.ts
+++ b/packages/compiler/src/utils/is-hook-call.ts
@@ -1,5 +1,10 @@
import type * as babel from "@babel/core";
import { getRightmostIdName } from "./get-rightmost-id-name";
+import { isInTheSameFunctionScope } from "./is-in-the-same-function-scope";
+
+export function doesMatchHookName(name: string) {
+ return /^use[A-Z]/.test(name);
+}
export function isHookCall(path: babel.NodePath) {
path.assertCallExpression();
@@ -9,12 +14,32 @@ export function isHookCall(path: babel.NodePath) {
let rightmostId = "";
try {
- rightmostId = getRightmostIdName(callee);
+ rightmostId = getRightmostIdName(callee.node);
} catch {
// We pessimistically assume that it's a hook if we can't identify the rightmost id
// TODO: Make this configurable / throw an error / or log a warning
return true;
}
- return /^use[A-Z]/.test(rightmostId);
+ return doesMatchHookName(rightmostId);
+}
+
+export function hasHookCall(
+ path: babel.NodePath,
+ componentPath: babel.NodePath
+) {
+ let hasHookCall = false;
+ path.traverse({
+ CallExpression: (innerPath) => {
+ if (
+ isHookCall(innerPath) &&
+ isInTheSameFunctionScope(innerPath, componentPath)
+ ) {
+ hasHookCall = true;
+ return;
+ }
+ },
+ });
+
+ return hasHookCall;
}
diff --git a/packages/compiler/src/utils/is-in-the-same-function-scope.ts b/packages/compiler/src/utils/is-in-the-same-function-scope.ts
new file mode 100644
index 0000000..0104507
--- /dev/null
+++ b/packages/compiler/src/utils/is-in-the-same-function-scope.ts
@@ -0,0 +1,19 @@
+import * as babel from "@babel/core";
+import * as t from "@babel/types";
+import { Scope } from "@babel/traverse";
+
+// We need this to properly detect if return statements belong to the same function
+export function isInTheSameFunctionScope(
+ path: babel.NodePath,
+ fn: babel.NodePath
+) {
+ let currentScope: Scope | null = path.scope;
+ do {
+ if (t.isFunction(currentScope.block)) {
+ return currentScope.block === fn.node;
+ }
+ currentScope = currentScope?.parent ?? null;
+} while (currentScope);
+
+ return false;
+}
diff --git a/packages/compiler/src/utils/is-variable-in-scope-of.ts b/packages/compiler/src/utils/is-variable-in-scope-of.ts
new file mode 100644
index 0000000..3b81508
--- /dev/null
+++ b/packages/compiler/src/utils/is-variable-in-scope-of.ts
@@ -0,0 +1,7 @@
+import { Binding, Scope } from "@babel/traverse";
+
+export function isVariableInScopeOf(binding: Binding, scope: Scope) {
+ const name = binding.identifier.name;
+
+ return scope.getBinding(name) === binding;
+}
diff --git a/packages/compiler/src/utils/member-expression-to-dot-notation.ts b/packages/compiler/src/utils/member-expression-to-dot-notation.ts
deleted file mode 100644
index 87bc8ff..0000000
--- a/packages/compiler/src/utils/member-expression-to-dot-notation.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as t from "@babel/types";
-
-// Convert a member expression to dot notation
-// This is used to make an identifier from a member expression
-export function memberExpressionToDotNotation(node: t.Expression) {
- // Recursive function to handle nested member expressions
- function traverse(node: t.Expression | t.PrivateName): string {
- if (node.type === "ThisExpression") {
- return "this";
- } else if (node.type === "Identifier") {
- // Base case: if the node is an identifier, prepend its name to the dotNotation string
- return node.name;
- } else if (t.isLiteral(node)) {
- switch (node.type) {
- case "StringLiteral":
- return `"${node.value}"`;
- case "BigIntLiteral":
- return `${node.value}n`;
- case "NullLiteral":
- return "null";
- case "TemplateLiteral":
- return `\`${node.quasis?.[0]?.value.raw}\``;
- case "RegExpLiteral":
- return `/${node.pattern}/${node.flags}`;
- default:
- return String(node.value);
- }
- } else if (node.type === "MemberExpression") {
- // Handle non-computed member expressions
-
- const propsPrefix = node.computed ? `[` : ".";
- const propsSuffix = node.computed ? `]` : "";
-
- const propsNotation = propsPrefix + traverse(node.property) + propsSuffix;
-
- return traverse(node.object) + propsNotation;
- }
-
- throw new Error("Node type not supported");
- }
-
- // Start the traversal with the given node
- return traverse(node);
-}
diff --git a/packages/compiler/src/utils/reorder-by-topology.ts b/packages/compiler/src/utils/reorder-by-topology.ts
new file mode 100644
index 0000000..e3490a0
--- /dev/null
+++ b/packages/compiler/src/utils/reorder-by-topology.ts
@@ -0,0 +1,57 @@
+import { ComponentMutableSegment } from "~/classes/ComponentMutableSegment";
+import { CircularDependencyError } from "./errors/CircularDependencyError";
+
+type StatementPath = babel.NodePath;
+
+export function reorderByTopology(
+ statements: StatementPath[] | Set,
+ map: Map
+) {
+ const stack: StatementPath[] = [];
+ const visited = new Set();
+ const recursionStack = new Set();
+
+ function dfs(statement: StatementPath) {
+ if (visited.has(statement)) {
+ return;
+ }
+
+ visited.add(statement);
+ recursionStack.add(statement);
+
+ const dependencies = map.get(statement)?.getDependencies();
+
+ // Visit all the dependent nodes
+ dependencies?.forEach(({ componentVariable }) => {
+ const dependencyStatement = componentVariable.getParentStatement()!;
+ // If the dependent node is in the recursion stack, we have a cycle
+ if (dependencyStatement === statement) {
+ return;
+ }
+
+ if (recursionStack.has(dependencyStatement)) {
+ throw new CircularDependencyError(statement, dependencyStatement);
+ }
+
+ dfs(dependencyStatement);
+ });
+
+ stack.push(statement);
+ recursionStack.delete(statement);
+ }
+
+ statements.forEach((statement) => {
+ if (map.get(statement)?.getDependencies().size === 0) {
+ stack.push(statement);
+ visited.add(statement);
+ }
+ });
+
+ statements.forEach((statement) => {
+ if (!visited.has(statement)) {
+ dfs(statement);
+ }
+ });
+
+ return stack;
+}
diff --git a/packages/compiler/src/utils/tests/get-referenced-variables-inside.test.ts b/packages/compiler/src/utils/tests/get-referenced-variables-inside.test.ts
index 92c3690..822b42b 100644
--- a/packages/compiler/src/utils/tests/get-referenced-variables-inside.test.ts
+++ b/packages/compiler/src/utils/tests/get-referenced-variables-inside.test.ts
@@ -38,7 +38,8 @@ describe("getReferencedVariablesInside", () => {
const fn = getFunctionFromBodyPath(body);
const returnStatement = getReturnStatement(fn)!;
- const bindings = getReferencedVariablesInside(returnStatement);
+ const variables = getReferencedVariablesInside(returnStatement);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(1);
expect(bindings[0]?.path).toEqual(body.at(0)?.get("declarations.0"));
@@ -55,7 +56,8 @@ describe("getReferencedVariablesInside", () => {
const fn = getFunctionFromBodyPath(body);
const returnStatement = getReturnStatement(fn)!;
- const bindings = getReferencedVariablesInside(returnStatement);
+ const variables = getReferencedVariablesInside(returnStatement);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(1);
expect(bindings[0]?.identifier.name).toStrictEqual("derivedValue");
@@ -75,7 +77,8 @@ describe("getReferencedVariablesInside", () => {
const fn = getFunctionFromBodyPath(body);
const returnStatement = getReturnStatement(fn)!;
- const bindings = getReferencedVariablesInside(returnStatement);
+ const variables = getReferencedVariablesInside(returnStatement);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(2);
expect(bindings[0]?.identifier.name).toStrictEqual("derivedValue");
@@ -97,9 +100,9 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
const returnStatement = getReturnStatement(fn)!;
- const bindings = getReferencedVariablesInside(returnStatement);
+ const variables = getReferencedVariablesInside(returnStatement);
+ const bindings = Array.from(variables.values());
- // expect(bindings).toHaveLength(3);
expect(bindings.map((b) => b.identifier.name)).toStrictEqual([
"obj",
"key",
@@ -118,7 +121,8 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
const returnStatement = getReturnStatement(fn)!;
- const bindings = getReferencedVariablesInside(returnStatement);
+ const variables = getReferencedVariablesInside(returnStatement);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(3);
expect(bindings.map((b) => b.identifier.name)).toStrictEqual([
@@ -139,7 +143,8 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
const returnStatement = getReturnStatement(fn)!;
- const bindings = getReferencedVariablesInside(returnStatement);
+ const variables = getReferencedVariablesInside(returnStatement);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(3);
expect(bindings.map((b) => b.identifier.name)).toStrictEqual([
@@ -158,7 +163,8 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
- const bindings = getReferencedVariablesInside(fn);
+ const variables = getReferencedVariablesInside(fn);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(0);
});
@@ -173,7 +179,8 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
- const bindings = getReferencedVariablesInside(fn);
+ const variables = getReferencedVariablesInside(fn);
+ const bindings = Array.from(variables.values());
expect(bindings).toHaveLength(0);
});
@@ -196,7 +203,8 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
- const bindings = getReferencedVariablesInside(fn);
+ const variables = getReferencedVariablesInside(fn);
+ const bindings = Array.from(variables.values());
expect(bindings.map((b) => b.identifier.name)).toStrictEqual([
"computedProperty",
@@ -224,7 +232,8 @@ function MyComponent() {
`);
const fn = getFunctionFromBodyPath(body);
- const bindings = getReferencedVariablesInside(fn);
+ const variables = getReferencedVariablesInside(fn);
+ const bindings = Array.from(variables.values());
expect(bindings.map((b) => b.identifier.name)).toStrictEqual([
"computedProperty",
diff --git a/packages/compiler/src/utils/tests/get-rightmost-id-name.test.ts b/packages/compiler/src/utils/tests/get-rightmost-id-name.test.ts
index c28563e..1c367d4 100644
--- a/packages/compiler/src/utils/tests/get-rightmost-id-name.test.ts
+++ b/packages/compiler/src/utils/tests/get-rightmost-id-name.test.ts
@@ -6,9 +6,11 @@ import { parse } from "../testing";
const parseCodeAndRun = (code: string) => {
const path = parse(code);
return getRightmostIdName(
- path.get(
- "body.0.declarations.0.init"
- ) as babel.NodePath
+ (
+ path.get(
+ "body.0.declarations.0.init"
+ ) as babel.NodePath
+ ).node
);
};
diff --git a/packages/compiler/src/utils/tests/is-in-the-same-function-scope.test.ts b/packages/compiler/src/utils/tests/is-in-the-same-function-scope.test.ts
new file mode 100644
index 0000000..50e6819
--- /dev/null
+++ b/packages/compiler/src/utils/tests/is-in-the-same-function-scope.test.ts
@@ -0,0 +1,76 @@
+import * as babel from "@babel/core";
+import { isInTheSameFunctionScope } from "../is-in-the-same-function-scope";
+import { parse } from "../testing";
+
+describe("isInTheSameFunctionScope", () => {
+ it("basic example", () => {
+ const path = parse(`
+ function testFunction() {
+ return true;
+ }
+ `);
+
+ const testFn = path.get("body.0") as babel.NodePath;
+ const returnStatement = testFn.get(
+ "body.body.0"
+ ) as babel.NodePath;
+
+ expect(isInTheSameFunctionScope(returnStatement, testFn)).toStrictEqual(
+ true
+ );
+ });
+
+ it("with node in an if statement block", () => {
+ const path = parse(`
+function testFunction() {
+ if (true) {
+ return true;
+ }
+}
+ `);
+
+ const testFn = path.get("body.0") as babel.NodePath;
+ const returnStatement = testFn.get(
+ "body.body.0.consequent.body.0"
+ ) as babel.NodePath;
+
+ expect(isInTheSameFunctionScope(returnStatement, testFn)).toStrictEqual(
+ true
+ );
+ });
+
+ it("detects when a path is not in the same function scope immediately", () => {
+ const path = parse(`
+function testFunction() {
+ const callback = () => {
+ return 'foo';
+ }
+ if (true) {
+ return true;
+ }
+}
+ `);
+
+ const testFn = path.get("body.0") as babel.NodePath;
+ const callbackFn = testFn.get(
+ "body.body.0.declarations.0.init"
+ ) as babel.NodePath;
+ const returnStatementInCallback = callbackFn.get(
+ "body.body.0"
+ ) as babel.NodePath;
+
+ const returnStatement = testFn.get(
+ "body.body.1.consequent.body.0"
+ ) as babel.NodePath;
+
+ expect(isInTheSameFunctionScope(returnStatement, testFn)).toStrictEqual(
+ true
+ );
+ expect(
+ isInTheSameFunctionScope(returnStatementInCallback, testFn)
+ ).toStrictEqual(false);
+ expect(
+ isInTheSameFunctionScope(returnStatementInCallback, callbackFn)
+ ).toStrictEqual(true);
+ });
+});
diff --git a/packages/compiler/src/utils/tests/member-expression-to-dot-notation.test.ts b/packages/compiler/src/utils/tests/member-expression-to-dot-notation.test.ts
deleted file mode 100644
index 8857fca..0000000
--- a/packages/compiler/src/utils/tests/member-expression-to-dot-notation.test.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import * as babel from "@babel/core";
-import { memberExpressionToDotNotation } from "../member-expression-to-dot-notation";
-import { parse } from "../testing";
-
-function parseExpression(code: string) {
- const ast = parse(code);
- return (
- ast.get("body.0.expression") as babel.NodePath
- ).node;
-}
-
-describe("memberExpressionToDotNotation", () => {
- it("should handle Identifier", () => {
- const node = parseExpression("foo.bar");
- expect(memberExpressionToDotNotation(node)).toBe("foo.bar");
- });
-
- it("should handle Literal", () => {
- const node = parseExpression("foo[123]");
- expect(memberExpressionToDotNotation(node)).toBe("foo[123]");
- });
-
- it("should handle nested MemberExpression", () => {
- const node = parseExpression("foo.bar.baz");
- expect(memberExpressionToDotNotation(node)).toBe("foo.bar.baz");
- });
-
- it("should handle computed MemberExpression", () => {
- const node = parseExpression("foo[bar.baz]");
- expect(memberExpressionToDotNotation(node)).toBe("foo[bar.baz]");
- });
-
- it("should handle ThisExpression", () => {
- const node = parseExpression("this.foo");
- expect(memberExpressionToDotNotation(node)).toBe("this.foo");
- });
-
- it("should handle computed ThisExpression", () => {
- const node = parseExpression("this[foo]");
- expect(memberExpressionToDotNotation(node)).toBe("this[foo]");
- });
-
- it("should handle computed string literal", () => {
- const node = parseExpression("test['foo']");
- expect(memberExpressionToDotNotation(node)).toBe('test["foo"]');
- });
-
- it("should handle computed string template", () => {
- const node = parseExpression("test[`foo`]");
- expect(memberExpressionToDotNotation(node)).toBe("test[`foo`]");
- });
-
- it("should handle bigint literal", () => {
- const node = parseExpression("test[123n]");
- expect(memberExpressionToDotNotation(node)).toBe("test[123n]");
- });
-});
diff --git a/packages/compiler/src/utils/unwrap-jsx-elements.ts b/packages/compiler/src/utils/unwrap-jsx-elements.ts
index 024557e..42d7f98 100644
--- a/packages/compiler/src/utils/unwrap-jsx-elements.ts
+++ b/packages/compiler/src/utils/unwrap-jsx-elements.ts
@@ -1,6 +1,7 @@
import * as babel from "@babel/core";
import * as t from "@babel/types";
import { DEFAULT_UNWRAPPED_JSX_ELEMENT_VARIABLE_NAME } from "./constants";
+import { isInTheSameFunctionScope } from "./is-in-the-same-function-scope";
import { unwrapGenericExpression } from "./unwrap-generic-expression";
type JSXChild = t.JSXElement["children"][number];
@@ -23,6 +24,9 @@ export function unwrapJsxElements(fn: babel.NodePath) {
}
if (path.isJSXElement() || path.isJSXFragment()) {
+ if (!isInTheSameFunctionScope(path, fn)) {
+ return;
+ }
const childrenPath = path.get("children") as babel.NodePath<
t.JSXElement["children"][number]
>[];
diff --git a/packages/compiler/src/utils/unwrap-jsx-expressions.ts b/packages/compiler/src/utils/unwrap-jsx-expressions.ts
index fb4e259..d784fad 100644
--- a/packages/compiler/src/utils/unwrap-jsx-expressions.ts
+++ b/packages/compiler/src/utils/unwrap-jsx-expressions.ts
@@ -1,6 +1,7 @@
import * as babel from "@babel/core";
import * as t from "@babel/types";
import { DEFAULT_UNWRAPPED_JSX_EXPRESSION_VARIABLE_NAME } from "./constants";
+import { isInTheSameFunctionScope } from "./is-in-the-same-function-scope";
import { unwrapGenericExpression } from "./unwrap-generic-expression";
export function unwrapJsxExpressions(fn: babel.NodePath) {
@@ -16,6 +17,9 @@ export function unwrapJsxExpressions(fn: babel.NodePath) {
) {
return;
}
+ if (!isInTheSameFunctionScope(path, fn)) {
+ return;
+ }
unwrapGenericExpression(
fn,
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index 17f3f46..956ff99 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -22,7 +22,7 @@ interface Commit {
export const useCreateCache$unforget = (
size: S
-): [FixedArray, Commit] => {
+): [FixedArray, Commit, UnassignedType] => {
const valuesRef: MutableRefObject | null> =
useRef(null);
@@ -31,6 +31,9 @@ export const useCreateCache$unforget = (
const valuesToCommit: MutableRefObject | null> =
useRef(null);
+ // This is needed for hot reloading to work
+ const previousSize = useRef(size);
+
if (!valuesToCommit.current) {
valuesToCommit.current = new Map();
}
@@ -46,7 +49,8 @@ export const useCreateCache$unforget = (
};
}
- if (!valuesRef.current) {
+ if (!valuesRef.current || previousSize.current !== size) {
+ previousSize.current = size;
valuesRef.current = Array.from({ length: size }, (_, i) => {
return {
v: UNASSIGNED,
@@ -64,5 +68,5 @@ export const useCreateCache$unforget = (
throw new Error("Unreachable");
}
- return [valuesRef.current, commitRef.current];
+ return [valuesRef.current, commitRef.current, UNASSIGNED];
};