@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/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
-
declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugin;
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* IC-Reactor Vite Plugin
|
|
7
5
|
*
|
|
8
6
|
* A Vite plugin that generates ic-reactor hooks from Candid .did files.
|
|
9
|
-
*
|
|
10
|
-
* ⚠️ IMPORTANT: This plugin ONLY generates the reactor and hooks.
|
|
11
|
-
* The user is responsible for creating and configuring:
|
|
12
|
-
* - ClientManager
|
|
13
|
-
* - QueryClient
|
|
14
|
-
*
|
|
15
|
-
* The generated file will import the clientManager from a user-specified path.
|
|
7
|
+
* Uses @ic-reactor/codegen for all code generation logic.
|
|
16
8
|
*
|
|
17
9
|
* Usage:
|
|
18
10
|
* ```ts
|
|
@@ -25,7 +17,7 @@ declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugi
|
|
|
25
17
|
* {
|
|
26
18
|
* name: "backend",
|
|
27
19
|
* didFile: "../backend/backend.did",
|
|
28
|
-
* clientManagerPath: "../lib/client"
|
|
20
|
+
* clientManagerPath: "../lib/client"
|
|
29
21
|
* }
|
|
30
22
|
* ]
|
|
31
23
|
* })
|
|
@@ -35,33 +27,29 @@ declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugi
|
|
|
35
27
|
*/
|
|
36
28
|
|
|
37
29
|
interface CanisterConfig {
|
|
38
|
-
/** Name of the canister (used for variable naming) */
|
|
39
30
|
name: string;
|
|
40
|
-
/** Path to the .did file */
|
|
41
|
-
didFile: string;
|
|
42
|
-
/** Output directory (default: ./src/canisters/<name>) */
|
|
43
31
|
outDir?: string;
|
|
44
|
-
|
|
45
|
-
useDisplayReactor?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Path to import ClientManager from (relative to generated file).
|
|
48
|
-
* The file at this path should export: { clientManager: ClientManager }
|
|
49
|
-
* Default: "../../lib/client"
|
|
50
|
-
*/
|
|
32
|
+
didFile?: string;
|
|
51
33
|
clientManagerPath?: string;
|
|
52
34
|
}
|
|
53
35
|
interface IcReactorPluginOptions {
|
|
54
36
|
/** List of canisters to generate hooks for */
|
|
55
37
|
canisters: CanisterConfig[];
|
|
56
|
-
/** Base output directory (default: ./src/canisters) */
|
|
38
|
+
/** Base output directory (default: ./src/lib/canisters) */
|
|
57
39
|
outDir?: string;
|
|
58
40
|
/**
|
|
59
41
|
* Path to import ClientManager from (relative to generated file).
|
|
60
|
-
*
|
|
61
|
-
* Default: "../../lib/client"
|
|
42
|
+
* Default: "../../clients"
|
|
62
43
|
*/
|
|
63
44
|
clientManagerPath?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Automatically inject the IC environment (canister IDs and root key)
|
|
47
|
+
* into the browser using an `ic_env` cookie. (default: true)
|
|
48
|
+
*
|
|
49
|
+
* This is useful for local development with `icp`.
|
|
50
|
+
*/
|
|
51
|
+
injectEnvironment?: boolean;
|
|
64
52
|
}
|
|
65
53
|
declare function icReactorPlugin(options: IcReactorPluginOptions): Plugin;
|
|
66
54
|
|
|
67
|
-
export { type CanisterConfig, type IcReactorPluginOptions,
|
|
55
|
+
export { type CanisterConfig, type IcReactorPluginOptions, icReactorPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,373 +1,166 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { generate as generate2 } from "@icp-sdk/bindgen/core";
|
|
3
|
-
import fs2 from "fs";
|
|
4
|
-
import path2 from "path";
|
|
5
|
-
|
|
6
|
-
// src/advanced.ts
|
|
1
|
+
// src/index.ts
|
|
7
2
|
import fs from "fs";
|
|
8
3
|
import path from "path";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
4
|
+
import { execFileSync } from "child_process";
|
|
5
|
+
import {
|
|
6
|
+
generateDeclarations,
|
|
7
|
+
generateReactorFile,
|
|
8
|
+
generateClientFile
|
|
9
|
+
} from "@ic-reactor/codegen";
|
|
10
|
+
function getIcEnvironmentInfo(canisterNames) {
|
|
11
|
+
const environment = process.env.ICP_ENVIRONMENT || "local";
|
|
12
|
+
try {
|
|
13
|
+
const networkStatus = JSON.parse(
|
|
14
|
+
execFileSync("icp", ["network", "status", "-e", environment, "--json"], {
|
|
15
|
+
encoding: "utf-8"
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
const rootKey = networkStatus.root_key;
|
|
19
|
+
const proxyTarget = `http://127.0.0.1:${networkStatus.port}`;
|
|
20
|
+
const canisterIds = {};
|
|
21
|
+
for (const name of canisterNames) {
|
|
22
|
+
try {
|
|
23
|
+
const canisterId = execFileSync(
|
|
24
|
+
"icp",
|
|
25
|
+
["canister", "status", name, "-e", environment, "-i"],
|
|
26
|
+
{
|
|
27
|
+
encoding: "utf-8"
|
|
28
|
+
}
|
|
29
|
+
).trim();
|
|
30
|
+
canisterIds[name] = canisterId;
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { environment, rootKey, proxyTarget, canisterIds };
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
30
37
|
}
|
|
31
|
-
return methods;
|
|
32
38
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const pascalMethod = toPascalCase(name);
|
|
40
|
-
const camelMethod = toCamelCase(name);
|
|
41
|
-
if (type === "query") {
|
|
42
|
-
const hook = `
|
|
43
|
-
export const use${pascalMethod}Query = (
|
|
44
|
-
args: Parameters<${pascalName}Service["${name}"]>,
|
|
45
|
-
options?: any
|
|
46
|
-
) =>
|
|
47
|
-
useActorQuery({
|
|
48
|
-
functionName: "${name}",
|
|
49
|
-
args,
|
|
50
|
-
...options,
|
|
51
|
-
})
|
|
52
|
-
`;
|
|
53
|
-
const staticQuery = !hasArgs ? `
|
|
54
|
-
export const ${camelMethod}Query = createQuery(${camelName}Reactor, {
|
|
55
|
-
functionName: "${name}",
|
|
56
|
-
})
|
|
57
|
-
` : "";
|
|
58
|
-
return hook + staticQuery;
|
|
59
|
-
} else {
|
|
60
|
-
const hook = `
|
|
61
|
-
export const use${pascalMethod}Mutation = (
|
|
62
|
-
options?: any
|
|
63
|
-
) =>
|
|
64
|
-
useActorMutation({
|
|
65
|
-
functionName: "${name}",
|
|
66
|
-
...options,
|
|
67
|
-
})
|
|
68
|
-
`;
|
|
69
|
-
const staticMutation = !hasArgs ? `
|
|
70
|
-
export const ${camelMethod}Mutation = createMutation(${camelName}Reactor, {
|
|
71
|
-
functionName: "${name}",
|
|
72
|
-
})
|
|
73
|
-
` : "";
|
|
74
|
-
return hook + staticMutation;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
return `/**
|
|
78
|
-
* AUTO-GENERATED BY @ic-reactor/vite-plugin
|
|
79
|
-
* DO NOT EDIT MANUALLY
|
|
80
|
-
*
|
|
81
|
-
* Canister: ${canisterName}
|
|
82
|
-
* Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
83
|
-
*
|
|
84
|
-
* This file provides type-safe React hooks for interacting with the
|
|
85
|
-
* ${canisterName} canister using ic-reactor.
|
|
86
|
-
*/
|
|
87
|
-
|
|
88
|
-
import {
|
|
89
|
-
${reactorType},
|
|
90
|
-
createActorHooks,
|
|
91
|
-
createAuthHooks,
|
|
92
|
-
createQuery,
|
|
93
|
-
createMutation,
|
|
94
|
-
} from "@ic-reactor/react"
|
|
95
|
-
|
|
96
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
97
|
-
// USER-PROVIDED CLIENT MANAGER
|
|
98
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
99
|
-
import { clientManager } from "${clientManagerPath}"
|
|
100
|
-
|
|
101
|
-
// Import generated declarations from @icp-sdk/bindgen
|
|
102
|
-
import {
|
|
103
|
-
idlFactory,
|
|
104
|
-
type _SERVICE,
|
|
105
|
-
} from "./declarations/${canisterName}.did"
|
|
106
|
-
|
|
107
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
108
|
-
// REACTOR INSTANCE
|
|
109
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
110
|
-
type ${pascalName}Service = _SERVICE
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* ${pascalName} Reactor with ${useDisplayReactor ? "Display" : "Candid"} type transformations.
|
|
114
|
-
* ${useDisplayReactor ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
|
|
115
|
-
*/
|
|
116
|
-
export const ${camelName}Reactor = new ${reactorType}<${pascalName}Service>({
|
|
117
|
-
clientManager,
|
|
118
|
-
idlFactory,
|
|
119
|
-
name: "${canisterName}",
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
123
|
-
// ACTOR & AUTH HOOKS
|
|
124
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
125
|
-
export const {
|
|
126
|
-
useActorQuery,
|
|
127
|
-
useActorMutation,
|
|
128
|
-
useActorSuspenseQuery,
|
|
129
|
-
useActorInfiniteQuery,
|
|
130
|
-
useActorSuspenseInfiniteQuery,
|
|
131
|
-
useActorMethod,
|
|
132
|
-
} = createActorHooks(${camelName}Reactor)
|
|
133
|
-
|
|
134
|
-
export const use${pascalName}Query = useActorQuery
|
|
135
|
-
export const use${pascalName}Mutation = useActorMutation
|
|
136
|
-
export const use${pascalName}SuspenseQuery = useActorSuspenseQuery
|
|
137
|
-
export const use${pascalName}InfiniteQuery = useActorInfiniteQuery
|
|
138
|
-
export const use${pascalName}SuspenseInfiniteQuery = useActorSuspenseInfiniteQuery
|
|
139
|
-
export const use${pascalName}Method = useActorMethod
|
|
140
|
-
|
|
141
|
-
export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(
|
|
142
|
-
clientManager
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
146
|
-
// METHOD HOOKS
|
|
147
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
148
|
-
${hooks.join("")}
|
|
149
|
-
|
|
150
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
151
|
-
// RE-EXPORTS
|
|
152
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
153
|
-
export { idlFactory }
|
|
154
|
-
export type { ${pascalName}Service }
|
|
155
|
-
`;
|
|
39
|
+
function buildIcEnvCookie(canisterIds, rootKey) {
|
|
40
|
+
const envParts = [`ic_root_key=${rootKey}`];
|
|
41
|
+
for (const [name, id] of Object.entries(canisterIds)) {
|
|
42
|
+
envParts.push(`PUBLIC_CANISTER_ID:${name}=${id}`);
|
|
43
|
+
}
|
|
44
|
+
return encodeURIComponent(envParts.join("&"));
|
|
156
45
|
}
|
|
157
|
-
function
|
|
158
|
-
const baseOutDir = options.outDir ?? "./src/canisters";
|
|
46
|
+
function icReactorPlugin(options) {
|
|
47
|
+
const baseOutDir = options.outDir ?? "./src/lib/canisters";
|
|
159
48
|
return {
|
|
160
|
-
name: "ic-reactor-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
actor: {
|
|
176
|
-
disabled: true
|
|
177
|
-
},
|
|
178
|
-
force: true
|
|
49
|
+
name: "ic-reactor-plugin",
|
|
50
|
+
config(_config, { command }) {
|
|
51
|
+
if (command !== "serve" || !(options.injectEnvironment ?? true)) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const canisterNames = options.canisters.map((c) => c.name);
|
|
55
|
+
const icEnv = getIcEnvironmentInfo(canisterNames);
|
|
56
|
+
if (!icEnv) {
|
|
57
|
+
return {
|
|
58
|
+
server: {
|
|
59
|
+
proxy: {
|
|
60
|
+
"/api": {
|
|
61
|
+
target: "http://127.0.0.1:4943",
|
|
62
|
+
changeOrigin: true
|
|
63
|
+
}
|
|
179
64
|
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
} catch (e) {
|
|
196
|
-
console.warn(
|
|
197
|
-
`[ic-reactor] Could not read DID file at ${canister.didFile}, skipping hook generation.`
|
|
198
|
-
);
|
|
199
|
-
continue;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const cookieValue = buildIcEnvCookie(icEnv.canisterIds, icEnv.rootKey);
|
|
69
|
+
return {
|
|
70
|
+
server: {
|
|
71
|
+
headers: {
|
|
72
|
+
"Set-Cookie": `ic_env=${cookieValue}; Path=/; SameSite=Lax;`
|
|
73
|
+
},
|
|
74
|
+
proxy: {
|
|
75
|
+
"/api": {
|
|
76
|
+
target: icEnv.proxyTarget,
|
|
77
|
+
changeOrigin: true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
200
80
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
async buildStart() {
|
|
84
|
+
const defaultClientPath = path.resolve(
|
|
85
|
+
process.cwd(),
|
|
86
|
+
"src/lib/clients.ts"
|
|
87
|
+
);
|
|
88
|
+
if (!fs.existsSync(defaultClientPath)) {
|
|
209
89
|
console.log(
|
|
210
|
-
`[ic-reactor]
|
|
90
|
+
`[ic-reactor] Default client manager not found. Creating at ${defaultClientPath}`
|
|
211
91
|
);
|
|
92
|
+
const clientContent = generateClientFile();
|
|
93
|
+
fs.mkdirSync(path.dirname(defaultClientPath), { recursive: true });
|
|
94
|
+
fs.writeFileSync(defaultClientPath, clientContent);
|
|
212
95
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
);
|
|
219
|
-
if (canister) {
|
|
96
|
+
for (const canister of options.canisters) {
|
|
97
|
+
let didFile = canister.didFile;
|
|
98
|
+
const outDir = canister.outDir ?? path.join(baseOutDir, canister.name);
|
|
99
|
+
if (!didFile) {
|
|
100
|
+
const environment = process.env.ICP_ENVIRONMENT || "local";
|
|
220
101
|
console.log(
|
|
221
|
-
`[ic-reactor]
|
|
102
|
+
`[ic-reactor] didFile not specified for "${canister.name}". Attempting to download from canister...`
|
|
222
103
|
);
|
|
223
|
-
|
|
104
|
+
try {
|
|
105
|
+
const candidContent = execFileSync(
|
|
106
|
+
"icp",
|
|
107
|
+
[
|
|
108
|
+
"canister",
|
|
109
|
+
"metadata",
|
|
110
|
+
canister.name,
|
|
111
|
+
"candid:service",
|
|
112
|
+
"-e",
|
|
113
|
+
environment
|
|
114
|
+
],
|
|
115
|
+
{ encoding: "utf-8" }
|
|
116
|
+
).trim();
|
|
117
|
+
const declarationsDir = path.join(outDir, "declarations");
|
|
118
|
+
if (!fs.existsSync(declarationsDir)) {
|
|
119
|
+
fs.mkdirSync(declarationsDir, { recursive: true });
|
|
120
|
+
}
|
|
121
|
+
didFile = path.join(declarationsDir, `${canister.name}.did`);
|
|
122
|
+
fs.writeFileSync(didFile, candidContent);
|
|
123
|
+
console.log(
|
|
124
|
+
`[ic-reactor] Candid downloaded and saved to ${didFile}`
|
|
125
|
+
);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(
|
|
128
|
+
`[ic-reactor] Failed to download candid for ${canister.name}: ${error}`
|
|
129
|
+
);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
224
132
|
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// src/simple.ts
|
|
231
|
-
function toPascalCase2(str) {
|
|
232
|
-
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
233
|
-
}
|
|
234
|
-
function toCamelCase2(str) {
|
|
235
|
-
const pascal = toPascalCase2(str);
|
|
236
|
-
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
237
|
-
}
|
|
238
|
-
function generateReactorFile(canisterName, useDisplayReactor, clientManagerPath) {
|
|
239
|
-
const pascalName = toPascalCase2(canisterName);
|
|
240
|
-
const camelName = toCamelCase2(canisterName);
|
|
241
|
-
const reactorType = useDisplayReactor ? "DisplayReactor" : "Reactor";
|
|
242
|
-
return `/**
|
|
243
|
-
* AUTO-GENERATED BY @ic-reactor/vite-plugin
|
|
244
|
-
*
|
|
245
|
-
* Canister: ${canisterName}
|
|
246
|
-
* Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
247
|
-
*
|
|
248
|
-
* This file provides type-safe React hooks for interacting with the
|
|
249
|
-
* ${canisterName} canister using ic-reactor.
|
|
250
|
-
*/
|
|
251
|
-
|
|
252
|
-
import {
|
|
253
|
-
${reactorType},
|
|
254
|
-
createActorHooks,
|
|
255
|
-
} from "@ic-reactor/react"
|
|
256
|
-
|
|
257
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
258
|
-
// USER-PROVIDED CLIENT MANAGER
|
|
259
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
260
|
-
// The clientManager is imported from the user's own configuration file.
|
|
261
|
-
// This allows full customization of agent options, network settings, etc.
|
|
262
|
-
import { clientManager } from "${clientManagerPath}"
|
|
263
|
-
|
|
264
|
-
// Import generated declarations from @icp-sdk/bindgen
|
|
265
|
-
import {
|
|
266
|
-
idlFactory,
|
|
267
|
-
type _SERVICE,
|
|
268
|
-
} from "./declarations/${canisterName}.did"
|
|
269
|
-
|
|
270
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
271
|
-
// REACTOR INSTANCE
|
|
272
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* ${pascalName} Reactor with ${useDisplayReactor ? "Display" : "Candid"} type transformations.
|
|
276
|
-
* ${useDisplayReactor ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
|
|
277
|
-
*/
|
|
278
|
-
export const ${camelName}Reactor = new ${reactorType}<_SERVICE>({
|
|
279
|
-
clientManager,
|
|
280
|
-
idlFactory,
|
|
281
|
-
name: "${canisterName}",
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
285
|
-
// HOOKS
|
|
286
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* React hooks for the ${canisterName} canister.
|
|
290
|
-
*/
|
|
291
|
-
const {
|
|
292
|
-
useActorQuery: use${pascalName}Query,
|
|
293
|
-
useActorSuspenseQuery: use${pascalName}SuspenseQuery,
|
|
294
|
-
useActorInfiniteQuery: use${pascalName}InfiniteQuery,
|
|
295
|
-
useActorSuspenseInfiniteQuery: use${pascalName}SuspenseInfiniteQuery,
|
|
296
|
-
useActorMutation: use${pascalName}Mutation,
|
|
297
|
-
useActorMethod: use${pascalName}Method,
|
|
298
|
-
} = createActorHooks(${camelName}Reactor)
|
|
299
|
-
|
|
300
|
-
export {
|
|
301
|
-
use${pascalName}Query,
|
|
302
|
-
use${pascalName}SuspenseQuery,
|
|
303
|
-
use${pascalName}InfiniteQuery,
|
|
304
|
-
use${pascalName}SuspenseInfiniteQuery,
|
|
305
|
-
use${pascalName}Mutation,
|
|
306
|
-
use${pascalName}Method,
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
310
|
-
// RE-EXPORTS
|
|
311
|
-
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
312
|
-
|
|
313
|
-
export { idlFactory }
|
|
314
|
-
export type { _SERVICE as ${pascalName}Service }
|
|
315
|
-
`;
|
|
316
|
-
}
|
|
317
|
-
function icReactorPlugin(options) {
|
|
318
|
-
const baseOutDir = options.outDir ?? "./src/canisters";
|
|
319
|
-
return {
|
|
320
|
-
name: "ic-reactor-plugin",
|
|
321
|
-
async buildStart() {
|
|
322
|
-
for (const canister of options.canisters) {
|
|
323
|
-
const outDir = canister.outDir ?? path2.join(baseOutDir, canister.name);
|
|
324
|
-
const declarationsDir = path2.join(outDir, "declarations");
|
|
325
133
|
console.log(
|
|
326
|
-
`[ic-reactor] Generating hooks for ${canister.name} from ${
|
|
134
|
+
`[ic-reactor] Generating hooks for ${canister.name} from ${didFile}`
|
|
327
135
|
);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
await generate2({
|
|
337
|
-
didFile: canister.didFile,
|
|
338
|
-
outDir,
|
|
339
|
-
// Pass the parent directory; bindgen appends "declarations"
|
|
340
|
-
output: {
|
|
341
|
-
actor: {
|
|
342
|
-
disabled: true
|
|
343
|
-
},
|
|
344
|
-
force: true
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
console.log(
|
|
348
|
-
`[ic-reactor] Declarations generated at ${declarationsDir}`
|
|
136
|
+
const result = await generateDeclarations({
|
|
137
|
+
didFile,
|
|
138
|
+
outDir,
|
|
139
|
+
canisterName: canister.name
|
|
140
|
+
});
|
|
141
|
+
if (!result.success) {
|
|
142
|
+
console.error(
|
|
143
|
+
`[ic-reactor] Failed to generate declarations: ${result.error}`
|
|
349
144
|
);
|
|
350
|
-
} catch (error) {
|
|
351
|
-
console.error(`[ic-reactor] Failed to generate declarations:`, error);
|
|
352
145
|
continue;
|
|
353
146
|
}
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
canister.
|
|
358
|
-
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
fs2.writeFileSync(reactorPath, reactorContent);
|
|
147
|
+
const reactorContent = generateReactorFile({
|
|
148
|
+
canisterName: canister.name,
|
|
149
|
+
didFile,
|
|
150
|
+
clientManagerPath: canister.clientManagerPath ?? options.clientManagerPath
|
|
151
|
+
});
|
|
152
|
+
const reactorPath = path.join(outDir, "index.ts");
|
|
153
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
154
|
+
fs.writeFileSync(reactorPath, reactorContent);
|
|
363
155
|
console.log(`[ic-reactor] Reactor hooks generated at ${reactorPath}`);
|
|
364
156
|
}
|
|
365
157
|
},
|
|
366
158
|
handleHotUpdate({ file, server }) {
|
|
367
159
|
if (file.endsWith(".did")) {
|
|
368
|
-
const canister = options.canisters.find(
|
|
369
|
-
|
|
370
|
-
|
|
160
|
+
const canister = options.canisters.find((c) => {
|
|
161
|
+
if (!c.didFile) return false;
|
|
162
|
+
return path.resolve(c.didFile) === file;
|
|
163
|
+
});
|
|
371
164
|
if (canister) {
|
|
372
165
|
console.log(
|
|
373
166
|
`[ic-reactor] Detected change in ${file}, regenerating...`
|
|
@@ -379,6 +172,5 @@ function icReactorPlugin(options) {
|
|
|
379
172
|
};
|
|
380
173
|
}
|
|
381
174
|
export {
|
|
382
|
-
icReactorAdvancedPlugin,
|
|
383
175
|
icReactorPlugin
|
|
384
176
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ic-reactor/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Vite plugin for zero-config IC reactor generation from Candid files",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -14,12 +14,9 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"src"
|
|
18
19
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --tsconfig tsconfig.json",
|
|
21
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --tsconfig tsconfig.json"
|
|
22
|
-
},
|
|
23
20
|
"keywords": [
|
|
24
21
|
"vite",
|
|
25
22
|
"vite-plugin",
|
|
@@ -31,22 +28,31 @@
|
|
|
31
28
|
],
|
|
32
29
|
"author": "Behrad Deylami",
|
|
33
30
|
"license": "MIT",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/B3Pay/ic-reactor",
|
|
34
|
+
"directory": "packages/vite-plugin"
|
|
37
35
|
},
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"optional": true
|
|
41
|
-
}
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/B3Pay/ic-reactor/issues"
|
|
42
38
|
},
|
|
43
39
|
"dependencies": {
|
|
44
|
-
"
|
|
40
|
+
"@ic-reactor/codegen": "0.3.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
45
44
|
},
|
|
46
45
|
"devDependencies": {
|
|
47
|
-
"@
|
|
46
|
+
"@icp-sdk/bindgen": "^0.2.1",
|
|
47
|
+
"@types/node": "^25.2.3",
|
|
48
48
|
"tsup": "^8.5.1",
|
|
49
|
-
"typescript": "^5.9.
|
|
50
|
-
"vite": "^7.3.1"
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vite": "^7.3.1",
|
|
51
|
+
"vitest": "^4.0.18"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --tsconfig tsconfig.json",
|
|
55
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --tsconfig tsconfig.json",
|
|
56
|
+
"test": "vitest run"
|
|
51
57
|
}
|
|
52
|
-
}
|
|
58
|
+
}
|