@ic-reactor/vite-plugin 0.1.0 → 0.3.0
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 +51 -169
- package/dist/index.cjs +134 -349
- package/dist/index.d.cts +13 -25
- package/dist/index.d.ts +13 -25
- package/dist/index.js +139 -347
- package/package.json +24 -18
- package/src/index.test.ts +273 -0
- package/src/index.ts +267 -0
package/README.md
CHANGED
|
@@ -24,15 +24,11 @@ Automatically generate type-safe React hooks for your Internet Computer canister
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
# With npm
|
|
28
|
-
npm install -D @ic-reactor/vite-plugin
|
|
29
|
-
|
|
30
27
|
# With pnpm
|
|
31
28
|
pnpm add -D @ic-reactor/vite-plugin
|
|
32
29
|
|
|
33
30
|
# Required peer dependencies
|
|
34
|
-
|
|
35
|
-
npm install -D @icp-sdk/bindgen
|
|
31
|
+
pnpm add @ic-reactor/react @tanstack/react-query @icp-sdk/core
|
|
36
32
|
```
|
|
37
33
|
|
|
38
34
|
## Quick Start
|
|
@@ -62,52 +58,30 @@ export default defineConfig({
|
|
|
62
58
|
|
|
63
59
|
### 2. Create Your ClientManager
|
|
64
60
|
|
|
65
|
-
The plugin expects you to have a `ClientManager` exported from a file. By default, it looks for `./src/lib/
|
|
61
|
+
The plugin expects you to have a `ClientManager` exported from a file. By default, it looks for `./src/lib/clients.ts`:
|
|
66
62
|
|
|
67
63
|
```typescript
|
|
68
|
-
// src/lib/
|
|
64
|
+
// src/lib/clients.ts
|
|
69
65
|
import { ClientManager } from "@ic-reactor/react"
|
|
70
66
|
import { QueryClient } from "@tanstack/react-query"
|
|
71
67
|
|
|
72
68
|
export const queryClient = new QueryClient()
|
|
73
|
-
|
|
74
|
-
export const clientManager = new ClientManager({
|
|
75
|
-
queryClient,
|
|
76
|
-
withCanisterEnv: true, // Enable environment-based canister ID resolution
|
|
77
|
-
})
|
|
69
|
+
export const clientManager = new ClientManager({ queryClient })
|
|
78
70
|
```
|
|
79
71
|
|
|
80
72
|
### 3. Use Generated Hooks
|
|
81
73
|
|
|
82
|
-
The plugin generates
|
|
74
|
+
The plugin generates an `index.ts` file in your canister folder (default: `./src/lib/canisters/<name>/index.ts`):
|
|
83
75
|
|
|
84
76
|
```tsx
|
|
85
|
-
|
|
86
|
-
import {
|
|
87
|
-
backendReactor,
|
|
88
|
-
useBackendQuery,
|
|
89
|
-
useBackendMutation,
|
|
90
|
-
useBackendSuspenseQuery,
|
|
91
|
-
} from "./canisters/backend"
|
|
77
|
+
import { useActorQuery, useActorMutation } from "./canisters/backend"
|
|
92
78
|
|
|
93
79
|
function MyComponent() {
|
|
94
|
-
|
|
95
|
-
const { data, isPending } = useBackendQuery({
|
|
80
|
+
const { data, isPending } = useActorQuery({
|
|
96
81
|
functionName: "get_message",
|
|
97
82
|
})
|
|
98
83
|
|
|
99
|
-
|
|
100
|
-
const { mutate } = useBackendMutation({
|
|
101
|
-
functionName: "set_message",
|
|
102
|
-
onSuccess: () => console.log("Message updated!"),
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<div>
|
|
107
|
-
<p>{isPending ? "Loading..." : data}</p>
|
|
108
|
-
<button onClick={() => mutate(["Hello IC!"])}>Update</button>
|
|
109
|
-
</div>
|
|
110
|
-
)
|
|
84
|
+
return <p>{isPending ? "Loading..." : data}</p>
|
|
111
85
|
}
|
|
112
86
|
```
|
|
113
87
|
|
|
@@ -115,123 +89,30 @@ function MyComponent() {
|
|
|
115
89
|
|
|
116
90
|
### Plugin Options
|
|
117
91
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/** Path to import ClientManager from (default: "../../lib/client") */
|
|
125
|
-
clientManagerPath?: string
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
interface CanisterConfig {
|
|
129
|
-
/** Name of the canister (used for variable naming) */
|
|
130
|
-
name: string
|
|
131
|
-
/** Path to the .did file */
|
|
132
|
-
didFile: string
|
|
133
|
-
/** Output directory (default: ./src/canisters/<name>) */
|
|
134
|
-
outDir?: string
|
|
135
|
-
/** Use DisplayReactor for React-friendly types (default: true) */
|
|
136
|
-
useDisplayReactor?: boolean
|
|
137
|
-
/** Path to import ClientManager from (relative to generated file) */
|
|
138
|
-
clientManagerPath?: string
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Example: Multiple Canisters
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
// vite.config.ts
|
|
146
|
-
import { icReactorPlugin } from "@ic-reactor/vite-plugin"
|
|
147
|
-
|
|
148
|
-
export default defineConfig({
|
|
149
|
-
plugins: [
|
|
150
|
-
icReactorPlugin({
|
|
151
|
-
clientManagerPath: "@/lib/client", // Global default
|
|
152
|
-
canisters: [
|
|
153
|
-
{
|
|
154
|
-
name: "backend",
|
|
155
|
-
didFile: "./backend/backend.did",
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
name: "ledger",
|
|
159
|
-
didFile: "./ledger/ledger.did",
|
|
160
|
-
useDisplayReactor: true, // BigInt → string, etc.
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: "nft",
|
|
164
|
-
didFile: "./nft/nft.did",
|
|
165
|
-
outDir: "./src/services/nft", // Custom output
|
|
166
|
-
clientManagerPath: "@/lib/nft-client", // Custom client
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
}),
|
|
170
|
-
],
|
|
171
|
-
})
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Advanced Plugin
|
|
92
|
+
| Option | Type | Description | Default |
|
|
93
|
+
| :------------------ | :----------------- | :--------------------------------------------------- | :---------------------- |
|
|
94
|
+
| `canisters` | `CanisterConfig[]` | List of canisters to generate hooks for. | `[]` |
|
|
95
|
+
| `outDir` | `string` | Base output directory for generated files. | `"./src/lib/canisters"` |
|
|
96
|
+
| `injectEnvironment` | `boolean` | Whether to inject canister IDs into `ic_env` cookie. | `true` |
|
|
97
|
+
| `clientManagerPath` | `string` | Path to a custom `ClientManager` instance. | `"../../clients"` |
|
|
175
98
|
|
|
176
|
-
|
|
99
|
+
### Canister Config
|
|
177
100
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
{
|
|
186
|
-
name: "backend",
|
|
187
|
-
didFile: "./backend/backend.did",
|
|
188
|
-
},
|
|
189
|
-
],
|
|
190
|
-
}),
|
|
191
|
-
],
|
|
192
|
-
})
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
This generates method-specific hooks:
|
|
196
|
-
|
|
197
|
-
```tsx
|
|
198
|
-
import {
|
|
199
|
-
useGetMessageQuery,
|
|
200
|
-
useSetMessageMutation,
|
|
201
|
-
getMessageQuery, // Static query for no-arg methods
|
|
202
|
-
} from "./canisters/backend"
|
|
203
|
-
|
|
204
|
-
function MyComponent() {
|
|
205
|
-
// Method-specific hook
|
|
206
|
-
const { data } = useGetMessageQuery([], { staleTime: 5000 })
|
|
207
|
-
|
|
208
|
-
// Static query usage
|
|
209
|
-
const { data: cached } = getMessageQuery.useQuery()
|
|
210
|
-
|
|
211
|
-
const { mutate } = useSetMessageMutation()
|
|
212
|
-
|
|
213
|
-
return <div>{data}</div>
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## Generated File Structure
|
|
218
|
-
|
|
219
|
-
```
|
|
220
|
-
src/
|
|
221
|
-
├── canisters/
|
|
222
|
-
│ └── backend/
|
|
223
|
-
│ ├── index.ts # Reactor + hooks
|
|
224
|
-
│ └── declarations/
|
|
225
|
-
│ └── backend.did.ts # Type declarations
|
|
226
|
-
├── lib/
|
|
227
|
-
│ └── client.ts # Your ClientManager (not generated)
|
|
228
|
-
```
|
|
101
|
+
| Option | Type | Description | Required |
|
|
102
|
+
| :------------------ | :-------- | :-------------------------------------------------- | :------- |
|
|
103
|
+
| `name` | `string` | Name of the canister (used in generated code). | Yes |
|
|
104
|
+
| `didFile` | `string` | Path to the `.did` file. | Yes |
|
|
105
|
+
| `outDir` | `string` | Override output directory for this canister. | No |
|
|
106
|
+
| `useDisplayReactor` | `boolean` | Use `DisplayReactor` instead of standard `Reactor`. | `true` |
|
|
107
|
+
| `clientManagerPath` | `string` | Override client manager path for this canister. | No |
|
|
229
108
|
|
|
230
109
|
## How It Works
|
|
231
110
|
|
|
232
|
-
1. **Build Start**: The plugin reads your `.did` files and
|
|
233
|
-
2. **Code Generation**: Creates a reactor instance and typed hooks for each canister
|
|
234
|
-
3. **Hot Reload**: Watches for `.did` file changes and regenerates
|
|
111
|
+
1. **Build Start**: The plugin reads your `.did` files and generates TypeScript declarations (`.js` and `.d.ts`).
|
|
112
|
+
2. **Code Generation**: Creates a reactor instance and typed hooks (using `createActorHooks`) for each canister in `index.ts`.
|
|
113
|
+
3. **Hot Reload**: Watches for `.did` file changes and regenerates everything automatically.
|
|
114
|
+
4. **Local Proxy**: Configures a Vite proxy to redirect `/api` calls to your local replica.
|
|
115
|
+
5. **Environment Detection**: Automatically injects canister IDs from `icp-cli` or `dfx` cache into your session.
|
|
235
116
|
|
|
236
117
|
## DisplayReactor vs Reactor
|
|
237
118
|
|
|
@@ -260,27 +141,28 @@ icReactorPlugin({
|
|
|
260
141
|
|
|
261
142
|
## Integration with ICP CLI
|
|
262
143
|
|
|
263
|
-
|
|
144
|
+
`@ic-reactor/vite-plugin` now supports **zero-config local `icp-cli` canister env injection** during `vite dev`.
|
|
145
|
+
|
|
146
|
+
When dev server starts, the plugin automatically tries to read:
|
|
147
|
+
|
|
148
|
+
- `.icp/cache/mappings/local.ids.json`
|
|
149
|
+
|
|
150
|
+
If present, it sets an `ic_env` cookie with:
|
|
151
|
+
|
|
152
|
+
- `ic_root_key=<local-root-key>`
|
|
153
|
+
- `PUBLIC_CANISTER_ID:<name>=<canister-id>`
|
|
154
|
+
|
|
155
|
+
This means `withCanisterEnv: true` works out of the box after `icp deploy`, without custom cookie code in `vite.config.ts`.
|
|
264
156
|
|
|
265
157
|
```typescript
|
|
266
158
|
// vite.config.ts
|
|
159
|
+
import { defineConfig } from "vite"
|
|
160
|
+
import react from "@vitejs/plugin-react"
|
|
267
161
|
import { icReactorPlugin } from "@ic-reactor/vite-plugin"
|
|
268
|
-
import fs from "fs"
|
|
269
|
-
import path from "path"
|
|
270
|
-
|
|
271
|
-
function loadCanisterIds(): Record<string, string> {
|
|
272
|
-
const idsPath = path.resolve(__dirname, ".icp/cache/mappings/local.ids.json")
|
|
273
|
-
try {
|
|
274
|
-
return JSON.parse(fs.readFileSync(idsPath, "utf-8"))
|
|
275
|
-
} catch {
|
|
276
|
-
return {}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const canisterIds = loadCanisterIds()
|
|
281
162
|
|
|
282
163
|
export default defineConfig({
|
|
283
164
|
plugins: [
|
|
165
|
+
react(),
|
|
284
166
|
icReactorPlugin({
|
|
285
167
|
canisters: [
|
|
286
168
|
{
|
|
@@ -290,15 +172,15 @@ export default defineConfig({
|
|
|
290
172
|
],
|
|
291
173
|
}),
|
|
292
174
|
],
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
If you need to disable this behavior:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
icReactorPlugin({
|
|
182
|
+
canisters: [...],
|
|
183
|
+
injectEnvironment: false,
|
|
302
184
|
})
|
|
303
185
|
```
|
|
304
186
|
|