Skip to content

Commit a4971cc

Browse files
seefeldbclaude
andcommitted
test(ts-transformers): Add test fixtures for recipe<T>() without name parameter
Add test fixtures demonstrating that recipe() with explicit type arguments but no name parameter correctly transforms and injects schemas. New fixtures: - counter-recipe-no-name: recipe<RecipeState>((state) => ...) - map-single-capture-with-type-arg-no-name: recipe<State>((state) => ...) - jsx-conditional-rendering-no-name: recipe<State>((state) => ...) - map-handler-reference-with-type-arg-no-name: recipe<State>((state) => ...) These fixtures validate that the transformer handles recipe() calls with type arguments but without the optional name parameter, ensuring schema injection works correctly in this configuration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f88a76c commit a4971cc

8 files changed

+541
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as __ctHelpers from "commontools";
2+
import { Cell, Default, handler, NAME, recipe, str, UI } from "commontools";
3+
interface CounterState {
4+
value: Cell<number>;
5+
}
6+
interface RecipeState {
7+
value: Default<number, 0>;
8+
}
9+
const increment = handler(true as const satisfies __ctHelpers.JSONSchema, {
10+
type: "object",
11+
properties: {
12+
value: {
13+
type: "number",
14+
asCell: true
15+
}
16+
},
17+
required: ["value"]
18+
} as const satisfies __ctHelpers.JSONSchema, (_e, state) => {
19+
state.value.set(state.value.get() + 1);
20+
});
21+
const decrement = handler(true as const satisfies __ctHelpers.JSONSchema, {
22+
type: "object",
23+
properties: {
24+
value: {
25+
type: "number",
26+
asCell: true
27+
}
28+
},
29+
required: ["value"]
30+
} as const satisfies __ctHelpers.JSONSchema, (_, state: {
31+
value: Cell<number>;
32+
}) => {
33+
state.value.set(state.value.get() - 1);
34+
});
35+
export default recipe({
36+
type: "object",
37+
properties: {
38+
value: {
39+
type: "number",
40+
default: 0
41+
}
42+
},
43+
required: ["value"]
44+
} as const satisfies __ctHelpers.JSONSchema, (state) => {
45+
return {
46+
[NAME]: str `Simple counter: ${state.value}`,
47+
[UI]: (<div>
48+
<ct-button onClick={decrement(state)}>-</ct-button>
49+
<ul>
50+
<li>next number: {__ctHelpers.ifElse(state.value, __ctHelpers.derive({ state: {
51+
value: state.value
52+
} }, ({ state }) => state.value + 1), "unknown")}</li>
53+
</ul>
54+
<ct-button onClick={increment({ value: state.value })}>+</ct-button>
55+
</div>),
56+
value: state.value,
57+
};
58+
});
59+
// @ts-ignore: Internals
60+
function h(...args: any[]) { return __ctHelpers.h.apply(null, args); }
61+
// @ts-ignore: Internals
62+
h.fragment = __ctHelpers.h.fragment;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/// <cts-enable />
2+
import { Cell, Default, handler, NAME, recipe, str, UI } from "commontools";
3+
4+
interface CounterState {
5+
value: Cell<number>;
6+
}
7+
8+
interface RecipeState {
9+
value: Default<number, 0>;
10+
}
11+
12+
const increment = handler<unknown, CounterState>((_e, state) => {
13+
state.value.set(state.value.get() + 1);
14+
});
15+
16+
const decrement = handler((_, state: { value: Cell<number> }) => {
17+
state.value.set(state.value.get() - 1);
18+
});
19+
20+
export default recipe<RecipeState>((state) => {
21+
return {
22+
[NAME]: str`Simple counter: ${state.value}`,
23+
[UI]: (
24+
<div>
25+
<ct-button onClick={decrement(state)}>-</ct-button>
26+
<ul>
27+
<li>next number: {state.value ? state.value + 1 : "unknown"}</li>
28+
</ul>
29+
<ct-button onClick={increment({ value: state.value })}>+</ct-button>
30+
</div>
31+
),
32+
value: state.value,
33+
};
34+
});
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import * as __ctHelpers from "commontools";
2+
import { recipe, UI, handler, Cell } from "commontools";
3+
declare global {
4+
namespace JSX {
5+
interface IntrinsicElements {
6+
"ct-button": any;
7+
}
8+
}
9+
}
10+
// Event handler defined at module scope
11+
const handleClick = handler(true as const satisfies __ctHelpers.JSONSchema, {
12+
type: "object",
13+
properties: {
14+
count: {
15+
type: "number",
16+
asCell: true
17+
}
18+
},
19+
required: ["count"]
20+
} as const satisfies __ctHelpers.JSONSchema, (_, { count }) => {
21+
count.set(count.get() + 1);
22+
});
23+
interface Item {
24+
id: number;
25+
name: string;
26+
}
27+
interface State {
28+
items: Item[];
29+
count: Cell<number>;
30+
}
31+
export default recipe({
32+
$schema: "https://json-schema.org/draft/2020-12/schema",
33+
type: "object",
34+
properties: {
35+
items: {
36+
type: "array",
37+
items: {
38+
$ref: "#/$defs/Item"
39+
}
40+
},
41+
count: {
42+
type: "number",
43+
asCell: true
44+
}
45+
},
46+
required: ["items", "count"],
47+
$defs: {
48+
Item: {
49+
type: "object",
50+
properties: {
51+
id: {
52+
type: "number"
53+
},
54+
name: {
55+
type: "string"
56+
}
57+
},
58+
required: ["id", "name"]
59+
}
60+
}
61+
} as const satisfies __ctHelpers.JSONSchema, (state) => {
62+
return {
63+
[UI]: (<div>
64+
{/* Map callback references handler - should NOT capture it */}
65+
{state.items.mapWithPattern(__ctHelpers.recipe({
66+
$schema: "https://json-schema.org/draft/2020-12/schema",
67+
type: "object",
68+
properties: {
69+
element: {
70+
$ref: "#/$defs/Item"
71+
},
72+
params: {
73+
type: "object",
74+
properties: {
75+
state: {
76+
type: "object",
77+
properties: {
78+
count: {
79+
type: "number",
80+
asCell: true,
81+
asOpaque: true
82+
}
83+
},
84+
required: ["count"]
85+
}
86+
},
87+
required: ["state"]
88+
}
89+
},
90+
required: ["element", "params"],
91+
$defs: {
92+
Item: {
93+
type: "object",
94+
properties: {
95+
id: {
96+
type: "number"
97+
},
98+
name: {
99+
type: "string"
100+
}
101+
},
102+
required: ["id", "name"]
103+
}
104+
}
105+
} as const satisfies __ctHelpers.JSONSchema, ({ element: item, params: { state } }) => (<ct-button onClick={handleClick({ count: state.count })}>
106+
{item.name}
107+
</ct-button>)), {
108+
state: {
109+
count: state.count
110+
}
111+
})}
112+
</div>),
113+
};
114+
});
115+
// @ts-ignore: Internals
116+
function h(...args: any[]) { return __ctHelpers.h.apply(null, args); }
117+
// @ts-ignore: Internals
118+
h.fragment = __ctHelpers.h.fragment;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/// <cts-enable />
2+
import { recipe, UI, handler, Cell } from "commontools";
3+
4+
declare global {
5+
namespace JSX {
6+
interface IntrinsicElements {
7+
"ct-button": any;
8+
}
9+
}
10+
}
11+
12+
// Event handler defined at module scope
13+
const handleClick = handler<unknown, { count: Cell<number> }>((_, { count }) => {
14+
count.set(count.get() + 1);
15+
});
16+
17+
interface Item {
18+
id: number;
19+
name: string;
20+
}
21+
22+
interface State {
23+
items: Item[];
24+
count: Cell<number>;
25+
}
26+
27+
export default recipe<State>((state) => {
28+
return {
29+
[UI]: (
30+
<div>
31+
{/* Map callback references handler - should NOT capture it */}
32+
{state.items.map((item) => (
33+
<ct-button onClick={handleClick({ count: state.count })}>
34+
{item.name}
35+
</ct-button>
36+
))}
37+
</div>
38+
),
39+
};
40+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import * as __ctHelpers from "commontools";
2+
import { recipe, UI } from "commontools";
3+
interface State {
4+
items: Array<{
5+
price: number;
6+
}>;
7+
discount: number;
8+
}
9+
export default recipe({
10+
type: "object",
11+
properties: {
12+
items: {
13+
type: "array",
14+
items: {
15+
type: "object",
16+
properties: {
17+
price: {
18+
type: "number"
19+
}
20+
},
21+
required: ["price"]
22+
}
23+
},
24+
discount: {
25+
type: "number"
26+
}
27+
},
28+
required: ["items", "discount"]
29+
} as const satisfies __ctHelpers.JSONSchema, (state) => {
30+
return {
31+
[UI]: (<div>
32+
{state.items.mapWithPattern(__ctHelpers.recipe({
33+
$schema: "https://json-schema.org/draft/2020-12/schema",
34+
type: "object",
35+
properties: {
36+
element: {
37+
type: "object",
38+
properties: {
39+
price: {
40+
type: "number"
41+
}
42+
},
43+
required: ["price"]
44+
},
45+
params: {
46+
type: "object",
47+
properties: {
48+
state: {
49+
type: "object",
50+
properties: {
51+
discount: {
52+
type: "number",
53+
asOpaque: true
54+
}
55+
},
56+
required: ["discount"]
57+
}
58+
},
59+
required: ["state"]
60+
}
61+
},
62+
required: ["element", "params"]
63+
} as const satisfies __ctHelpers.JSONSchema, ({ element: item, params: { state } }) => (<span>{__ctHelpers.derive({
64+
item: {
65+
price: item.price
66+
},
67+
state: {
68+
discount: state.discount
69+
}
70+
}, ({ item, state }) => item.price * state.discount)}</span>)), {
71+
state: {
72+
discount: state.discount
73+
}
74+
})}
75+
</div>),
76+
};
77+
});
78+
// @ts-ignore: Internals
79+
function h(...args: any[]) { return __ctHelpers.h.apply(null, args); }
80+
// @ts-ignore: Internals
81+
h.fragment = __ctHelpers.h.fragment;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// <cts-enable />
2+
import { recipe, UI } from "commontools";
3+
4+
interface State {
5+
items: Array<{ price: number }>;
6+
discount: number;
7+
}
8+
9+
export default recipe<State>((state) => {
10+
return {
11+
[UI]: (
12+
<div>
13+
{state.items.map((item) => (
14+
<span>{item.price * state.discount}</span>
15+
))}
16+
</div>
17+
),
18+
};
19+
});

0 commit comments

Comments
 (0)