@ic-reactor/cli 0.0.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +267 -0
- package/dist/index.js +1709 -0
- package/package.json +56 -0
- package/schema.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# @ic-reactor/cli
|
|
2
|
+
|
|
3
|
+
> 🔧 Generate shadcn-style React hooks for ICP canisters
|
|
4
|
+
|
|
5
|
+
The `@ic-reactor/cli` generates **customizable, user-owned** React hooks for interacting with Internet Computer canisters. Unlike build-time code generation, you get full control over the generated code.
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
Like [shadcn/ui](https://ui.shadcn.com/), this CLI generates code **into your project** rather than hiding it in `node_modules`. This means:
|
|
10
|
+
|
|
11
|
+
- ✅ **Full control** - Customize hooks to your needs
|
|
12
|
+
- ✅ **No magic** - See exactly what's happening
|
|
13
|
+
- ✅ **Version controlled** - Hooks are part of your codebase
|
|
14
|
+
- ✅ **Framework agnostic** - Works with any React setup
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -D @ic-reactor/cli
|
|
20
|
+
# or
|
|
21
|
+
pnpm add -D @ic-reactor/cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Initialize your project
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @ic-reactor/cli init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This creates:
|
|
33
|
+
|
|
34
|
+
- `reactor.config.json` - Configuration file
|
|
35
|
+
- `src/lib/client.ts` - Client manager (optional)
|
|
36
|
+
- `src/canisters/` - Output directory for hooks
|
|
37
|
+
|
|
38
|
+
### 2. Add hooks for a canister
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @ic-reactor/cli add
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Interactive prompts will guide you through:
|
|
45
|
+
|
|
46
|
+
1. Selecting a canister
|
|
47
|
+
2. Choosing methods to generate hooks for
|
|
48
|
+
3. Selecting hook types (Query, Suspense Query, Infinite Query, Mutation)
|
|
49
|
+
|
|
50
|
+
### 3. Use the hooks
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { useGetMessageQuery, getMessageQuery } from "./canisters/backend/hooks"
|
|
54
|
+
|
|
55
|
+
function MyComponent() {
|
|
56
|
+
// Use the React hook
|
|
57
|
+
const { data, isLoading } = useGetMessageQuery()
|
|
58
|
+
|
|
59
|
+
// Or fetch directly (for loaders, etc.)
|
|
60
|
+
await getMessageQuery.fetch()
|
|
61
|
+
|
|
62
|
+
// Invalidate cache
|
|
63
|
+
await getMessageQuery.invalidate()
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
### `init`
|
|
70
|
+
|
|
71
|
+
Initialize ic-reactor in your project.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx @ic-reactor/cli init [options]
|
|
75
|
+
|
|
76
|
+
Options:
|
|
77
|
+
-y, --yes Skip prompts and use defaults
|
|
78
|
+
-o, --out-dir <path> Output directory for generated hooks
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `add`
|
|
82
|
+
|
|
83
|
+
Add hooks for canister methods (from local .did file).
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx @ic-reactor/cli add [options]
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
-c, --canister <name> Canister to add hooks for
|
|
90
|
+
-m, --methods <methods...> Specific methods to generate
|
|
91
|
+
-a, --all Add hooks for all methods
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `fetch`
|
|
95
|
+
|
|
96
|
+
**Fetch Candid from a live canister** and generate hooks. No local .did file needed!
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npx @ic-reactor/cli fetch [options]
|
|
100
|
+
|
|
101
|
+
Options:
|
|
102
|
+
-i, --canister-id <id> Canister ID to fetch from
|
|
103
|
+
-n, --network <network> Network: 'ic' or 'local' (default: ic)
|
|
104
|
+
--name <name> Name for the canister in generated code
|
|
105
|
+
-m, --methods <methods...> Specific methods to generate
|
|
106
|
+
-a, --all Add hooks for all methods
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Example:**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Fetch from IC mainnet
|
|
113
|
+
npx @ic-reactor/cli fetch -i ryjl3-tyaaa-aaaaa-aaaba-cai
|
|
114
|
+
|
|
115
|
+
# Fetch from local replica
|
|
116
|
+
npx @ic-reactor/cli fetch -i bkyz2-fmaaa-aaaaa-qaaaq-cai -n local
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `list`
|
|
120
|
+
|
|
121
|
+
List available methods from a canister.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx @ic-reactor/cli list [options]
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
-c, --canister <name> Canister to list methods from
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `sync`
|
|
131
|
+
|
|
132
|
+
Regenerate hooks after DID file changes.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx @ic-reactor/cli sync [options]
|
|
136
|
+
|
|
137
|
+
Options:
|
|
138
|
+
-c, --canister <name> Canister to sync (default: all)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
### reactor.config.json
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"$schema": "https://raw.githubusercontent.com/B3Pay/ic-reactor/main/packages/cli/schema.json",
|
|
148
|
+
"outDir": "src/canisters",
|
|
149
|
+
"canisters": {
|
|
150
|
+
"backend": {
|
|
151
|
+
"didFile": "./backend.did",
|
|
152
|
+
"clientManagerPath": "../../lib/client",
|
|
153
|
+
"useDisplayReactor": true
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"generatedHooks": {
|
|
157
|
+
"backend": ["get_message", "set_message"]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Configuration Options
|
|
163
|
+
|
|
164
|
+
| Option | Description |
|
|
165
|
+
| ------------------------------------ | ----------------------------------------- |
|
|
166
|
+
| `outDir` | Directory where hooks are generated |
|
|
167
|
+
| `canisters` | Canister configurations |
|
|
168
|
+
| `canisters.<name>.didFile` | Path to the `.did` file |
|
|
169
|
+
| `canisters.<name>.clientManagerPath` | Import path to client manager |
|
|
170
|
+
| `canisters.<name>.useDisplayReactor` | Use DisplayReactor for type transforms |
|
|
171
|
+
| `generatedHooks` | Tracks which methods have hooks generated |
|
|
172
|
+
|
|
173
|
+
## Generated File Structure
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
src/canisters/
|
|
177
|
+
├── backend/
|
|
178
|
+
│ ├── reactor.ts # Shared reactor instance
|
|
179
|
+
│ └── hooks/
|
|
180
|
+
│ ├── index.ts # Barrel exports
|
|
181
|
+
│ ├── getMessageQuery.ts # Query hook
|
|
182
|
+
│ ├── setMessageMutation.ts # Mutation hook
|
|
183
|
+
│ └── getPostsInfiniteQuery.ts # Infinite query
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Hook Types
|
|
187
|
+
|
|
188
|
+
### Query (methods without side effects)
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// For methods WITH arguments - factory pattern
|
|
192
|
+
export const getUserQuery = createQueryFactory(reactor, {
|
|
193
|
+
functionName: "get_user",
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Usage
|
|
197
|
+
const query = getUserQuery([userId])
|
|
198
|
+
const { data } = query.useQuery()
|
|
199
|
+
|
|
200
|
+
// For methods WITHOUT arguments - static instance
|
|
201
|
+
export const getConfigQuery = createQuery(reactor, {
|
|
202
|
+
functionName: "get_config",
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
// Usage
|
|
206
|
+
const { data } = getConfigQuery.useQuery()
|
|
207
|
+
await getConfigQuery.fetch()
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Mutation (methods with side effects)
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
export const setMessageMutation = createMutation(reactor, {
|
|
214
|
+
functionName: "set_message",
|
|
215
|
+
invalidateQueries: [getMessageQuery.getQueryKey()],
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// Usage
|
|
219
|
+
const { mutate, isPending } = setMessageMutation.useMutation()
|
|
220
|
+
mutate(["Hello, ICP!"])
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Infinite Query (paginated data)
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
export const getPostsInfiniteQuery = createInfiniteQuery(reactor, {
|
|
227
|
+
functionName: "get_posts",
|
|
228
|
+
initialPageParam: 0,
|
|
229
|
+
getArgs: (cursor) => [{ cursor, limit: 10 }],
|
|
230
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// Usage
|
|
234
|
+
const { data, fetchNextPage, hasNextPage } =
|
|
235
|
+
getPostsInfiniteQuery.useInfiniteQuery()
|
|
236
|
+
const allPosts = data?.pages.flatMap((page) => page.items) ?? []
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Customization
|
|
240
|
+
|
|
241
|
+
Since the code is generated into your project, you can:
|
|
242
|
+
|
|
243
|
+
1. **Modify hook options** - Change staleTime, select transforms, etc.
|
|
244
|
+
2. **Add custom logic** - Error handling, optimistic updates
|
|
245
|
+
3. **Combine hooks** - Create composite hooks
|
|
246
|
+
4. **Type overrides** - Adjust TypeScript types
|
|
247
|
+
|
|
248
|
+
Example customization:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// getMessageQuery.ts (generated, then customized)
|
|
252
|
+
export const getMessageQuery = createQuery(reactor, {
|
|
253
|
+
functionName: "get_message",
|
|
254
|
+
staleTime: 30 * 1000, // Custom: 30 seconds
|
|
255
|
+
select: (data) => data.message.toUpperCase(), // Custom transform
|
|
256
|
+
})
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Requirements
|
|
260
|
+
|
|
261
|
+
- Node.js 18+
|
|
262
|
+
- @ic-reactor/react 3.x
|
|
263
|
+
- React 18+
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT
|