@ic-reactor/vite-plugin 0.15.1 → 4.0.0-beta.1
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 +141 -173
- package/dist/index.cjs +534 -207
- package/dist/index.d.cts +88 -30
- package/dist/index.d.ts +88 -30
- package/dist/index.js +534 -211
- package/package.json +6 -8
- package/src/generate.ts +594 -0
- package/src/index.ts +456 -430
- package/llms.txt +0 -156
package/README.md
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
# @ic-reactor/vite-plugin
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
|
|
8
|
-
Vite plugin for
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
> **ic-reactor 4 is a prerelease.** `4.0.0-beta.1` is published under npm's
|
|
4
|
+
> `beta` dist-tag, and `latest` stays the 0.15 plugin of the 3.x line until 4.0
|
|
5
|
+
> GA. That plugin, which generates reactor bindings with `@ic-reactor/codegen`,
|
|
6
|
+
> is documented at https://ic-reactor.b3pay.net/v3/packages/vite-plugin.
|
|
7
|
+
|
|
8
|
+
A Vite plugin for an app built on a module that `candid-core-cli gen` generates
|
|
9
|
+
from a `.did` file. It does two things, and exports only `icReactor` and the
|
|
10
|
+
type `IcReactorPluginOptions`:
|
|
11
|
+
|
|
12
|
+
- **Generation.** It runs `candid-core-cli gen` on each configured `.did` file
|
|
13
|
+
when a build or the dev server starts, and again when that file changes. The
|
|
14
|
+
generated module is candid-core's, as the generator wrote it: the plugin
|
|
15
|
+
adds no wrapper files, hooks or reactors.
|
|
16
|
+
- **Environment.** Under `vite dev` and `vite preview` it sets the `ic_env`
|
|
17
|
+
cookie and proxies `/api` to the local IC network, so the app finds its
|
|
18
|
+
canister IDs and the replica's root key without configuration.
|
|
11
19
|
|
|
12
20
|
## Install
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
The plugin runs the `@candid-core/cli` your app installs, and that CLI has to
|
|
23
|
+
pair with the `@candid-core/schema` runtime the generated modules import. Both
|
|
24
|
+
are pinned to one exact release while they are betas, and so is the plugin's
|
|
25
|
+
peer on the CLI:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install --save-exact @candid-core/schema@0.3.0-beta.1
|
|
29
|
+
npm install --save-dev --save-exact @candid-core/cli@0.2.0-beta.1
|
|
30
|
+
npm install --save-dev @ic-reactor/vite-plugin@beta
|
|
17
31
|
```
|
|
18
32
|
|
|
19
33
|
## Quick Start
|
|
@@ -21,156 +35,140 @@ pnpm add @ic-reactor/react @tanstack/react-query @icp-sdk/core
|
|
|
21
35
|
```ts
|
|
22
36
|
// vite.config.ts
|
|
23
37
|
import { defineConfig } from "vite"
|
|
24
|
-
import react from "@vitejs/plugin-react"
|
|
25
38
|
import { icReactor } from "@ic-reactor/vite-plugin"
|
|
26
39
|
|
|
27
40
|
export default defineConfig({
|
|
28
41
|
plugins: [
|
|
29
|
-
react(),
|
|
30
42
|
icReactor({
|
|
31
|
-
canisters:
|
|
43
|
+
canisters: {
|
|
44
|
+
ledger: { didFile: "../backend/ledger.did" },
|
|
45
|
+
},
|
|
32
46
|
}),
|
|
33
47
|
],
|
|
34
48
|
})
|
|
35
49
|
```
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export const queryClient = new QueryClient()
|
|
43
|
-
export const clientManager = new ClientManager({
|
|
44
|
-
queryClient,
|
|
45
|
-
})
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
No opt-in flag is needed to pick up the plugin's environment in development:
|
|
49
|
-
the plugin sets the `ic_env` cookie and `ClientManager` reads it automatically
|
|
50
|
-
in the browser. That trust stops at the local replica — cookies are not
|
|
51
|
-
origin-isolated, so on a custom domain, on mainnet, and on Codespaces or Gitpod
|
|
52
|
-
(whose workspaces share a parent domain with every other user's) the cookie is
|
|
53
|
-
ignored and a reactor with no `canisterId` throws. Set the per-canister `canisterId` in the
|
|
54
|
-
plugin config to bake it into the generated output for those builds, or pass
|
|
55
|
-
`allowEnvConfig: true` to `ClientManager` if you trust every subdomain of the
|
|
56
|
-
domain you serve from.
|
|
57
|
-
|
|
58
|
-
The plugin generates files under `src/declarations/<canister>/` by default —
|
|
59
|
-
`declarations/<did-basename>.{js,d.ts,did}` plus a managed `index.generated.ts`
|
|
60
|
-
and a stable `index.ts` wrapper. With `target: "react"`, `index.generated.ts`
|
|
61
|
-
exports the reactor and six hooks named after the canister
|
|
62
|
-
(`use<Canister>Query`, `use<Canister>SuspenseQuery`,
|
|
63
|
-
`use<Canister>InfiniteQuery`, `use<Canister>SuspenseInfiniteQuery`,
|
|
64
|
-
`use<Canister>Mutation`, `use<Canister>Method`).
|
|
65
|
-
|
|
66
|
-
Set `factories: true` on a canister to also generate
|
|
67
|
-
`index.factories.generated.ts`, a query or mutation object per method bound to
|
|
68
|
-
the generated reactor: `createQuery` for a query method without arguments,
|
|
69
|
-
`createQueryFactory` for one with arguments, and `createMutation` for an
|
|
70
|
-
update or oneway method, named `<method>Query` or `<method>Mutation` with the
|
|
71
|
-
method name in camelCase. The default `index.ts` wrapper re-exports it; an
|
|
72
|
-
`index.ts` you have edited is left alone, and the plugin warns in the terminal
|
|
73
|
-
until it re-exports the factories. It needs `target: "react"`. See
|
|
74
|
-
https://ic-reactor.b3pay.net/v3/packages/codegen#query-and-mutation-factories
|
|
75
|
-
for the naming rule for any method name.
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
import { icReactor } from "@ic-reactor/vite-plugin"
|
|
51
|
+
On `vite dev` and `vite build` this writes `src/canisters/ledger.ts`, the
|
|
52
|
+
generated module (it exports `actor` and the type `Actor`), and
|
|
53
|
+
`src/canisters/ledger.envelope.json` next to it. The generator names its output
|
|
54
|
+
after the `.did` file, not after the key in `canisters`.
|
|
79
55
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{ name: "backend", didFile: "./backend/backend.did", factories: true },
|
|
83
|
-
],
|
|
84
|
-
})
|
|
85
|
-
```
|
|
56
|
+
The first line the plugin logs says where an agent reads how to use the
|
|
57
|
+
library: `ic-reactor: agent guide at node_modules/@ic-reactor/core/llms.txt`.
|
|
86
58
|
|
|
87
|
-
|
|
88
|
-
import { getMessageQuery, setMessageMutation } from "./declarations/backend"
|
|
59
|
+
## Options
|
|
89
60
|
|
|
90
|
-
|
|
91
|
-
|
|
61
|
+
| Option | Default | Meaning |
|
|
62
|
+
| ------------------- | ------------------------- | --------------------------------------------------------------------------------- |
|
|
63
|
+
| `canisters` | `{}` | The app's canisters, by their name in the `icp` project; see below |
|
|
64
|
+
| `injectEnvironment` | `true` | Set the cookie and the `/api` proxy under `vite dev` and `vite preview` |
|
|
65
|
+
| `failOnError` | build `true`, dev `false` | Abort the Vite run when a canister fails to generate; see "When generation fails" |
|
|
92
66
|
|
|
93
|
-
|
|
94
|
-
await setMessageMutation.execute(["hello"])
|
|
95
|
-
```
|
|
67
|
+
Each entry of `canisters` takes:
|
|
96
68
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
build.
|
|
69
|
+
| Field | Default | Meaning |
|
|
70
|
+
| ------------ | ----------------- | -------------------------------------------------------------------------------------- |
|
|
71
|
+
| `didFile` | none | The canister's Candid file, relative to the Vite root. Without it nothing is generated |
|
|
72
|
+
| `outDir` | `"src/canisters"` | Where the generator writes, relative to the Vite root |
|
|
73
|
+
| `canisterId` | none | A fixed ID for the cookie, which wins over the one `icp` reports |
|
|
103
74
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
## Options
|
|
75
|
+
Canisters that share one interface share one module. An ICP ledger and a ckBTC
|
|
76
|
+
ledger both on `icrc1.did` are two entries, so that the `ic_env` cookie carries
|
|
77
|
+
each ID, and one generated module:
|
|
108
78
|
|
|
109
79
|
```ts
|
|
80
|
+
// vite.config.ts
|
|
81
|
+
import { defineConfig } from "vite"
|
|
110
82
|
import { icReactor } from "@ic-reactor/vite-plugin"
|
|
111
83
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
plugins: [
|
|
86
|
+
icReactor({
|
|
87
|
+
canisters: {
|
|
88
|
+
icp_ledger: { didFile: "did/icrc1.did" },
|
|
89
|
+
ckbtc_ledger: { didFile: "did/icrc1.did" },
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
119
92
|
],
|
|
120
|
-
outDir: "src/declarations",
|
|
121
|
-
clientManagerPath: "../../clients",
|
|
122
|
-
target: "react",
|
|
123
|
-
injectEnvironment: true,
|
|
124
|
-
failOnError: true,
|
|
125
93
|
})
|
|
126
94
|
```
|
|
127
95
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
`
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
- `
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
|
|
167
|
-
-
|
|
168
|
-
- `
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
96
|
+
The generator is given `icrc1.did` once, writes `src/canisters/icrc1.ts` once,
|
|
97
|
+
and a save of the file regenerates it once. (Naming a canister without a
|
|
98
|
+
`didFile` is the same thing when only the cookie is wanted.) Different `.did`
|
|
99
|
+
files that name the same module cannot share an `outDir`: `a/ledger.did` and
|
|
100
|
+
`b/ledger.did` both mean `ledger.ts`, and the second is refused with a message
|
|
101
|
+
naming both. Give one an `outDir` of its own. Names that differ only in case
|
|
102
|
+
(`Ledger.did` and `ledger.did`) collide only where the filesystem of the
|
|
103
|
+
`outDir` ignores case, as macOS and Windows do by default: the plugin checks
|
|
104
|
+
the filesystem and does not refuse them where it tells them apart.
|
|
105
|
+
|
|
106
|
+
## Generation
|
|
107
|
+
|
|
108
|
+
The generator is WebAssembly, so the plugin does not load it: it runs the bin
|
|
109
|
+
script of the `@candid-core/cli` installed for your app (resolved from the Vite
|
|
110
|
+
root, so a monorepo's hoisted copy is found) with the running Node binary, in a
|
|
111
|
+
child process and not through a shell. A trap, a crash or a runaway loop on a
|
|
112
|
+
bad `.did` ends that process and the plugin reports it. The dev server is not
|
|
113
|
+
affected.
|
|
114
|
+
|
|
115
|
+
- Canisters that write into one `outDir` share one process, and each `outDir`
|
|
116
|
+
has its own. A process that dies without a report is run again, one
|
|
117
|
+
canister at a time and side by side, so the failure lands on the canister
|
|
118
|
+
that caused it.
|
|
119
|
+
- A process that runs longer than 60 seconds is killed, and counts as one that
|
|
120
|
+
died: when it was generating several canisters, each is run again alone and
|
|
121
|
+
only the one that hangs fails. A hang therefore costs up to two timeouts
|
|
122
|
+
(120 seconds) before the others are generated.
|
|
123
|
+
- What the generator writes to stderr is logged as a warning, a line at a time
|
|
124
|
+
and as it arrives, naming the `.did` files its process is generating. With
|
|
125
|
+
`--json` the generator reports in its JSON document, so what shows up here
|
|
126
|
+
is a crash's output or a warning from the runtime. When a process fails, its
|
|
127
|
+
stderr is also in the error message, so that text appears twice. Past 200
|
|
128
|
+
lines from one process the rest is not logged, so a runaway generator does
|
|
129
|
+
not flood the terminal.
|
|
130
|
+
- Closing the dev server (or restarting it, as a `vite.config` edit does) kills
|
|
131
|
+
a generator that is still running and drops the runs still waiting, so none
|
|
132
|
+
outlives the server it belonged to or runs beside the next one.
|
|
133
|
+
- A declaration the generator cannot represent is left out of the module, and
|
|
134
|
+
the plugin logs each one as a warning, for example
|
|
135
|
+
`ic-reactor: ledger: omitted declaration Bad (reserved_field_name)`.
|
|
136
|
+
- Editing a `.did` regenerates only the canisters that name it. Saves that
|
|
137
|
+
arrive while it runs collapse into one more run. `vite build --watch`
|
|
138
|
+
regenerates a canister only when its `.did` text changed.
|
|
139
|
+
- Deleting a `.did` under `vite dev` fails the canisters that name it at once,
|
|
140
|
+
in the log and the overlay: the module it generated is still on disk, and
|
|
141
|
+
would otherwise look current. Putting the file back regenerates them.
|
|
142
|
+
|
|
143
|
+
### When generation fails
|
|
144
|
+
|
|
145
|
+
A failed canister costs that canister, and nothing else:
|
|
146
|
+
|
|
147
|
+
- Under `vite build` the build fails, with the generator's diagnostics (or its
|
|
148
|
+
stderr, if it crashed) in the error message. A build that exits 0 would ship
|
|
149
|
+
the bindings left over from the last good run.
|
|
150
|
+
- Under `vite dev` the failure is logged and shown in the browser's error
|
|
151
|
+
overlay, and the server keeps serving. The overlay lists the canisters that
|
|
152
|
+
are still broken: fixing one of several updates it, and fixing the last
|
|
153
|
+
clears it. Vite's client clears an error overlay whenever it applies a hot
|
|
154
|
+
update, as it does for Vite's own errors, so a canister that is still broken
|
|
155
|
+
can drop out of the browser's overlay after you save some other file. Its
|
|
156
|
+
error stays in the terminal log, and a full reload shows the overlay again.
|
|
157
|
+
- `failOnError` overrides either default.
|
|
158
|
+
|
|
159
|
+
### In CI
|
|
160
|
+
|
|
161
|
+
`candid-core-cli gen --check` compares the generated files with the ones on
|
|
162
|
+
disk, writes nothing, and exits 1 on any difference. Run it with the same
|
|
163
|
+
arguments the plugin uses to fail a pipeline when committed output is stale:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"scripts": {
|
|
168
|
+
"gen:check": "candid-core-cli gen ../backend/ledger.did -o src/canisters --check"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
174
172
|
|
|
175
173
|
## Local Development Behavior
|
|
176
174
|
|
|
@@ -178,8 +176,8 @@ When `injectEnvironment` is enabled during `vite dev` or `vite preview`, the
|
|
|
178
176
|
plugin:
|
|
179
177
|
|
|
180
178
|
1. asks `icp` for the local network status
|
|
181
|
-
2. resolves canister IDs
|
|
182
|
-
|
|
179
|
+
2. resolves canister IDs: the keys of `canisters`, and `internet_identity`,
|
|
180
|
+
which is added automatically if not already listed
|
|
183
181
|
3. sets the `ic_env` cookie on each response
|
|
184
182
|
4. proxies `/api` to the local replica
|
|
185
183
|
|
|
@@ -213,38 +211,8 @@ If your Vite config or another plugin sets `server.proxy["/api"]`, the plugin
|
|
|
213
211
|
leaves that entry alone, whether detection succeeds or not, and that proxy does
|
|
214
212
|
not follow detection.
|
|
215
213
|
|
|
216
|
-
##
|
|
217
|
-
|
|
218
|
-
On startup and on `.did` file changes, the plugin regenerates declarations and
|
|
219
|
-
the managed `index.generated.ts` implementation, and `index.factories.generated.ts`
|
|
220
|
-
for a canister that sets `factories: true`. The user-facing `index.ts`
|
|
221
|
-
entry is created once, then preserved unless it still matches the default
|
|
222
|
-
wrapper or a legacy generated scaffold that can be migrated automatically.
|
|
223
|
-
When a watched `.did` file changes, the plugin sends a full browser reload so
|
|
224
|
-
the new declarations are picked up.
|
|
225
|
-
|
|
226
|
-
The plugin follows the dev server's file watcher itself, so a `.did` file that
|
|
227
|
-
appears after the server started, or that a build tool deletes and writes
|
|
228
|
-
again, is generated too, and saves regenerate even with `server.hmr: false`.
|
|
229
|
-
|
|
230
|
-
Regeneration is serialized per canister — saves that land while a run is in
|
|
231
|
-
flight collapse into a single rerun — so two rapid saves cannot interleave
|
|
232
|
-
inside the pipeline's delete-then-write sequence.
|
|
233
|
-
A regeneration that fails is reported to the terminal and to the browser error
|
|
234
|
-
overlay rather than leaving the page on stale bindings.
|
|
235
|
-
|
|
236
|
-
`vite build --watch` watches the configured `.did` files as well. Saving one
|
|
237
|
-
starts a rebuild that regenerates that canister's bindings. A rebuild that any
|
|
238
|
-
other file starts leaves the generated files alone.
|
|
239
|
-
|
|
240
|
-
## When To Use It
|
|
241
|
-
|
|
242
|
-
- Vite apps with active `.did` iteration
|
|
243
|
-
- teams that want zero extra codegen commands during development
|
|
244
|
-
- projects that want the same output format as the CLI without manual steps
|
|
245
|
-
|
|
246
|
-
## See Also
|
|
214
|
+
## Tests
|
|
247
215
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
216
|
+
Vitest runs the plugin in mode `test`. There the plugin injects no environment
|
|
217
|
+
and never runs `icp`, whatever `injectEnvironment` says. Generation is not an
|
|
218
|
+
environment concern and still runs, so the modules a test imports exist.
|