diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
index 8411844626..70c8df8ee0 100644
--- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
@@ -967,6 +967,20 @@ function codegenForInit(
init: ReactiveValue
): t.Expression | t.VariableDeclaration | null {
if (init.kind === "SequenceExpression") {
+ for (const instr of init.instructions) {
+ if (instr.value.kind === "DeclareContext") {
+ CompilerError.throwTodo({
+ reason: `Support for loops where the index variable is a context variable`,
+ loc: instr.loc,
+ description:
+ instr.value.lvalue.place.identifier.name != null
+ ? `'${instr.value.lvalue.place.identifier.name.value}' is a context variable`
+ : null,
+ suggestions: null,
+ });
+ }
+ }
+
const body = codegenBlock(
cx,
init.instructions.map((instruction) => ({
@@ -983,7 +997,7 @@ function codegenForInit(
{
reason: "Expected a variable declaration",
loc: init.loc,
- description: null,
+ description: `Got ${instr.type}`,
suggestions: null,
}
);
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md
new file mode 100644
index 0000000000..712f86617f
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.expect.md
@@ -0,0 +1,57 @@
+
+## Input
+
+```javascript
+import { useHook } from "shared-runtime";
+
+function Component(props) {
+ const data = useHook();
+ const items = [];
+ // NOTE: `item` is a context variable because it's reassigned and also referenced
+ // within a closure, the `onClick` handler of each item
+ for (let key in props.data) {
+ key = key ?? null; // no-op reassignment to force a context variable
+ items.push(
+
data.set(key)}>
+ {key}
+
+ );
+ }
+ return {items}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ data: { a: "a", b: true, c: "hello" } }],
+};
+
+```
+
+
+## Error
+
+```
+ 6 | // NOTE: `item` is a context variable because it's reassigned and also referenced
+ 7 | // within a closure, the `onClick` handler of each item
+> 8 | for (let key in props.data) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 9 | key = key ?? null; // no-op reassignment to force a context variable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 10 | items.push(
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 11 | data.set(key)}>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 12 | {key}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 13 |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 14 | );
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 15 | }
+ | ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15)
+ 16 | return {items}
;
+ 17 | }
+ 18 |
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js
new file mode 100644
index 0000000000..34facb7f8f
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-in-loop-with-context-variable-iterator.js
@@ -0,0 +1,22 @@
+import { useHook } from "shared-runtime";
+
+function Component(props) {
+ const data = useHook();
+ const items = [];
+ // NOTE: `item` is a context variable because it's reassigned and also referenced
+ // within a closure, the `onClick` handler of each item
+ for (let key in props.data) {
+ key = key ?? null; // no-op reassignment to force a context variable
+ items.push(
+ data.set(key)}>
+ {key}
+
+ );
+ }
+ return {items}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ data: { a: "a", b: true, c: "hello" } }],
+};
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md
index a2fb2d6d60..f342283f83 100644
--- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-loop-with-context-variable-iterator.expect.md
@@ -19,17 +19,13 @@ function Component() {
## Error
```
- 4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
- 5 | // within a closure, the `onClick` handler of each item
-> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 7 | items.push( data.set(i)} />);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 8 | }
- | ^^^^ [ReactForget] Invariant: Expected a variable declaration (6:8)
- 9 | return items;
- 10 | }
- 11 |
+ 4 | // NOTE: `i` is a context variable because it's reassigned and also referenced
+ 5 | // within a closure, the `onClick` handler of each item
+> 6 | for (let i = MIN; i <= MAX; i += INCREMENT) {
+ | ^^^^^^^^^^^ [ReactForget] Todo: Support for loops where the index variable is a context variable. 'i' is a context variable (6:6)
+ 7 | items.push( data.set(i)} />);
+ 8 | }
+ 9 | return items;
```
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md
new file mode 100644
index 0000000000..3ad92cbd2b
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.expect.md
@@ -0,0 +1,57 @@
+
+## Input
+
+```javascript
+import { useHook } from "shared-runtime";
+
+function Component(props) {
+ const data = useHook();
+ const items = [];
+ // NOTE: `item` is a context variable because it's reassigned and also referenced
+ // within a closure, the `onClick` handler of each item
+ for (let item of props.data) {
+ item = item ?? {}; // reassignment to force a context variable
+ items.push(
+ data.set(item)}>
+ {item.id}
+
+ );
+ }
+ return {items}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ data: [{ id: "1" }, { id: "2" }] }],
+};
+
+```
+
+
+## Error
+
+```
+ 6 | // NOTE: `item` is a context variable because it's reassigned and also referenced
+ 7 | // within a closure, the `onClick` handler of each item
+> 8 | for (let item of props.data) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 9 | item = item ?? {}; // reassignment to force a context variable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 10 | items.push(
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 11 | data.set(item)}>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 12 | {item.id}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 13 |
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 14 | );
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+> 15 | }
+ | ^^^^ [ReactForget] Todo: Support non-trivial ForOf inits (8:15)
+ 16 | return {items}
;
+ 17 | }
+ 18 |
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js
new file mode 100644
index 0000000000..f0ba5094a5
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-for-of-loop-with-context-variable-iterator.js
@@ -0,0 +1,22 @@
+import { useHook } from "shared-runtime";
+
+function Component(props) {
+ const data = useHook();
+ const items = [];
+ // NOTE: `item` is a context variable because it's reassigned and also referenced
+ // within a closure, the `onClick` handler of each item
+ for (let item of props.data) {
+ item = item ?? {}; // reassignment to force a context variable
+ items.push(
+ data.set(item)}>
+ {item.id}
+
+ );
+ }
+ return {items}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ data: [{ id: "1" }, { id: "2" }] }],
+};