@ic-reactor/vite-plugin 0.1.0 → 0.2.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 +24 -23
- package/dist/index.cjs +80 -343
- package/dist/index.d.cts +21 -29
- package/dist/index.d.ts +21 -29
- package/dist/index.js +84 -341
- package/package.json +21 -17
- package/src/index.ts +226 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { CanisterConfig } from '@ic-reactor/codegen';
|
|
3
|
+
export { CanisterConfig } from '@ic-reactor/codegen';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* IC-Reactor Vite Plugin
|
|
7
7
|
*
|
|
8
8
|
* 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.
|
|
9
|
+
* Uses @ic-reactor/codegen for all code generation logic.
|
|
16
10
|
*
|
|
17
11
|
* Usage:
|
|
18
12
|
* ```ts
|
|
@@ -25,7 +19,7 @@ declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugi
|
|
|
25
19
|
* {
|
|
26
20
|
* name: "backend",
|
|
27
21
|
* didFile: "../backend/backend.did",
|
|
28
|
-
* clientManagerPath: "../lib/client"
|
|
22
|
+
* clientManagerPath: "../lib/client"
|
|
29
23
|
* }
|
|
30
24
|
* ]
|
|
31
25
|
* })
|
|
@@ -34,34 +28,32 @@ declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugi
|
|
|
34
28
|
* ```
|
|
35
29
|
*/
|
|
36
30
|
|
|
37
|
-
interface CanisterConfig {
|
|
38
|
-
/** Name of the canister (used for variable naming) */
|
|
39
|
-
name: string;
|
|
40
|
-
/** Path to the .did file */
|
|
41
|
-
didFile: string;
|
|
42
|
-
/** Output directory (default: ./src/canisters/<name>) */
|
|
43
|
-
outDir?: string;
|
|
44
|
-
/** Use DisplayReactor for React-friendly types (default: true) */
|
|
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
|
-
*/
|
|
51
|
-
clientManagerPath?: string;
|
|
52
|
-
}
|
|
53
31
|
interface IcReactorPluginOptions {
|
|
54
32
|
/** List of canisters to generate hooks for */
|
|
55
|
-
canisters: CanisterConfig
|
|
33
|
+
canisters: (CanisterConfig & {
|
|
34
|
+
name: string;
|
|
35
|
+
advanced?: boolean;
|
|
36
|
+
})[];
|
|
56
37
|
/** Base output directory (default: ./src/canisters) */
|
|
57
38
|
outDir?: string;
|
|
58
39
|
/**
|
|
59
40
|
* Path to import ClientManager from (relative to generated file).
|
|
60
|
-
* The file at this path should export: { clientManager: ClientManager }
|
|
61
41
|
* Default: "../../lib/client"
|
|
62
42
|
*/
|
|
63
43
|
clientManagerPath?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Generate advanced per-method hooks with createQuery/createMutation
|
|
46
|
+
* instead of generic actor hooks (default: false).
|
|
47
|
+
* Can be overridden per-canister by setting `advanced` on the individual canister entry.
|
|
48
|
+
*/
|
|
49
|
+
advanced?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Automatically set the `ic_env` cookie in Vite dev server from
|
|
52
|
+
* `.icp/cache/mappings/local.ids.json` (default: true).
|
|
53
|
+
*/
|
|
54
|
+
autoInjectIcEnv?: boolean;
|
|
64
55
|
}
|
|
56
|
+
|
|
65
57
|
declare function icReactorPlugin(options: IcReactorPluginOptions): Plugin;
|
|
66
58
|
|
|
67
|
-
export { type
|
|
59
|
+
export { type IcReactorPluginOptions, icReactorPlugin as default, icReactorPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,372 +1,114 @@
|
|
|
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
|
-
|
|
4
|
+
import {
|
|
5
|
+
generateDeclarations,
|
|
6
|
+
generateReactorFile
|
|
7
|
+
} from "@ic-reactor/codegen";
|
|
8
|
+
var ICP_LOCAL_IDS_PATH = ".icp/cache/mappings/local.ids.json";
|
|
9
|
+
var IC_ROOT_KEY_HEX = "308182301d060d2b0601040182dc7c0503010201060c2b0601040182dc7c050302010361008b52b4994f94c7ce4be1c1542d7c81dc79fea17d49efe8fa42e8566373581d4b969c4a59e96a0ef51b711fe5027ec01601182519d0a788f4bfe388e593b97cd1d7e44904de79422430bca686ac8c21305b3397b5ba4d7037d17877312fb7ee34";
|
|
10
|
+
function loadLocalCanisterIds(rootDir) {
|
|
11
|
+
const idsPath = path.resolve(rootDir, ICP_LOCAL_IDS_PATH);
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(fs.readFileSync(idsPath, "utf-8"));
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
while ((match = methodRegex.exec(didContent)) !== null) {
|
|
22
|
-
const name = match[1];
|
|
23
|
-
const args = match[2].trim();
|
|
24
|
-
const isQuery = !!match[4];
|
|
25
|
-
methods.push({
|
|
26
|
-
name,
|
|
27
|
-
type: isQuery ? "query" : "mutation",
|
|
28
|
-
hasArgs: args.length > 0
|
|
29
|
-
});
|
|
18
|
+
function buildIcEnvCookie(canisterIds) {
|
|
19
|
+
const envParts = [`ic_root_key=${IC_ROOT_KEY_HEX}`];
|
|
20
|
+
for (const [name, id] of Object.entries(canisterIds)) {
|
|
21
|
+
envParts.push(`PUBLIC_CANISTER_ID:${name}=${id}`);
|
|
30
22
|
}
|
|
31
|
-
return
|
|
23
|
+
return encodeURIComponent(envParts.join("&"));
|
|
32
24
|
}
|
|
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
|
-
`;
|
|
25
|
+
function addOrReplaceSetCookie(existing, cookie) {
|
|
26
|
+
const cookieEntries = typeof existing === "string" ? [existing] : Array.isArray(existing) ? existing.filter((value) => typeof value === "string") : [];
|
|
27
|
+
const nonIcEnvCookies = cookieEntries.filter(
|
|
28
|
+
(entry) => !entry.trim().startsWith("ic_env=")
|
|
29
|
+
);
|
|
30
|
+
return [...nonIcEnvCookies, cookie];
|
|
156
31
|
}
|
|
157
|
-
function
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
console.log(
|
|
168
|
-
`[ic-reactor] Generating advanced hooks for ${canister.name} from ${canister.didFile}`
|
|
169
|
-
);
|
|
170
|
-
try {
|
|
171
|
-
await generate({
|
|
172
|
-
didFile: canister.didFile,
|
|
173
|
-
outDir,
|
|
174
|
-
output: {
|
|
175
|
-
actor: {
|
|
176
|
-
disabled: true
|
|
177
|
-
},
|
|
178
|
-
force: true
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
console.log(
|
|
182
|
-
`[ic-reactor] Declarations generated at ${path.join(
|
|
183
|
-
outDir,
|
|
184
|
-
"declarations"
|
|
185
|
-
)}`
|
|
186
|
-
);
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.error(`[ic-reactor] Failed to generate declarations:`, error);
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
const clientManagerPath = canister.clientManagerPath ?? options.clientManagerPath ?? "../../lib/client";
|
|
192
|
-
let didContent = "";
|
|
193
|
-
try {
|
|
194
|
-
didContent = fs.readFileSync(canister.didFile, "utf-8");
|
|
195
|
-
} catch (e) {
|
|
196
|
-
console.warn(
|
|
197
|
-
`[ic-reactor] Could not read DID file at ${canister.didFile}, skipping hook generation.`
|
|
198
|
-
);
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
const reactorContent = generateAdvancedReactorFile(
|
|
202
|
-
canister.name,
|
|
203
|
-
canister.useDisplayReactor ?? true,
|
|
204
|
-
clientManagerPath,
|
|
205
|
-
didContent
|
|
206
|
-
);
|
|
207
|
-
const reactorPath = path.join(outDir, "index.ts");
|
|
208
|
-
fs.writeFileSync(reactorPath, reactorContent);
|
|
209
|
-
console.log(
|
|
210
|
-
`[ic-reactor] Advanced reactor hooks generated at ${reactorPath}`
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
handleHotUpdate({ file, server }) {
|
|
215
|
-
if (file.endsWith(".did")) {
|
|
216
|
-
const canister = options.canisters.find(
|
|
217
|
-
(c) => path.resolve(c.didFile) === file
|
|
32
|
+
function setupIcEnvMiddleware(server) {
|
|
33
|
+
const rootDir = server.config.root || process.cwd();
|
|
34
|
+
const idsPath = path.resolve(rootDir, ICP_LOCAL_IDS_PATH);
|
|
35
|
+
let hasLoggedHint = false;
|
|
36
|
+
server.middlewares.use((req, res, next) => {
|
|
37
|
+
const canisterIds = loadLocalCanisterIds(rootDir);
|
|
38
|
+
if (!canisterIds) {
|
|
39
|
+
if (!hasLoggedHint) {
|
|
40
|
+
server.config.logger.info(
|
|
41
|
+
`[ic-reactor] icp-cli local IDs not found at ${idsPath}. Run \`icp deploy\` to enable automatic ic_env cookie injection.`
|
|
218
42
|
);
|
|
219
|
-
|
|
220
|
-
console.log(
|
|
221
|
-
`[ic-reactor] Detected change in ${file}, regenerating...`
|
|
222
|
-
);
|
|
223
|
-
server.restart();
|
|
224
|
-
}
|
|
43
|
+
hasLoggedHint = true;
|
|
225
44
|
}
|
|
45
|
+
return next();
|
|
226
46
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
`;
|
|
47
|
+
const cookie = `ic_env=${buildIcEnvCookie(canisterIds)}; Path=/; SameSite=Lax;`;
|
|
48
|
+
const current = res.getHeader("Set-Cookie");
|
|
49
|
+
res.setHeader("Set-Cookie", addOrReplaceSetCookie(current, cookie));
|
|
50
|
+
next();
|
|
51
|
+
});
|
|
316
52
|
}
|
|
317
53
|
function icReactorPlugin(options) {
|
|
318
54
|
const baseOutDir = options.outDir ?? "./src/canisters";
|
|
319
55
|
return {
|
|
320
56
|
name: "ic-reactor-plugin",
|
|
57
|
+
configureServer(server) {
|
|
58
|
+
if (options.autoInjectIcEnv ?? true) {
|
|
59
|
+
setupIcEnvMiddleware(server);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
321
62
|
async buildStart() {
|
|
322
63
|
for (const canister of options.canisters) {
|
|
323
|
-
const outDir = canister.outDir ??
|
|
324
|
-
const declarationsDir = path2.join(outDir, "declarations");
|
|
64
|
+
const outDir = canister.outDir ?? path.join(baseOutDir, canister.name);
|
|
325
65
|
console.log(
|
|
326
66
|
`[ic-reactor] Generating hooks for ${canister.name} from ${canister.didFile}`
|
|
327
67
|
);
|
|
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}`
|
|
68
|
+
const result = await generateDeclarations({
|
|
69
|
+
didFile: canister.didFile,
|
|
70
|
+
outDir,
|
|
71
|
+
canisterName: canister.name
|
|
72
|
+
});
|
|
73
|
+
if (!result.success) {
|
|
74
|
+
console.error(
|
|
75
|
+
`[ic-reactor] Failed to generate declarations: ${result.error}`
|
|
349
76
|
);
|
|
350
|
-
} catch (error) {
|
|
351
|
-
console.error(`[ic-reactor] Failed to generate declarations:`, error);
|
|
352
77
|
continue;
|
|
353
78
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
canister.name,
|
|
357
|
-
canister.useDisplayReactor ?? true,
|
|
358
|
-
clientManagerPath
|
|
79
|
+
console.log(
|
|
80
|
+
`[ic-reactor] Declarations generated at ${result.declarationsDir}`
|
|
359
81
|
);
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
82
|
+
const useAdvanced = canister.advanced ?? options.advanced ?? false;
|
|
83
|
+
let didContent;
|
|
84
|
+
if (useAdvanced) {
|
|
85
|
+
try {
|
|
86
|
+
didContent = fs.readFileSync(canister.didFile, "utf-8");
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.warn(
|
|
89
|
+
`[ic-reactor] Could not read DID file at ${canister.didFile}, skipping advanced hook generation for ${canister.name}.`
|
|
90
|
+
);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const reactorContent = generateReactorFile({
|
|
95
|
+
canisterName: canister.name,
|
|
96
|
+
canisterConfig: canister,
|
|
97
|
+
globalClientManagerPath: options.clientManagerPath,
|
|
98
|
+
hasDeclarations: true,
|
|
99
|
+
advanced: useAdvanced,
|
|
100
|
+
didContent
|
|
101
|
+
});
|
|
102
|
+
const reactorPath = path.join(outDir, "index.ts");
|
|
103
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
104
|
+
fs.writeFileSync(reactorPath, reactorContent);
|
|
363
105
|
console.log(`[ic-reactor] Reactor hooks generated at ${reactorPath}`);
|
|
364
106
|
}
|
|
365
107
|
},
|
|
366
108
|
handleHotUpdate({ file, server }) {
|
|
367
109
|
if (file.endsWith(".did")) {
|
|
368
110
|
const canister = options.canisters.find(
|
|
369
|
-
(c) =>
|
|
111
|
+
(c) => path.resolve(c.didFile) === file
|
|
370
112
|
);
|
|
371
113
|
if (canister) {
|
|
372
114
|
console.log(
|
|
@@ -378,7 +120,8 @@ function icReactorPlugin(options) {
|
|
|
378
120
|
}
|
|
379
121
|
};
|
|
380
122
|
}
|
|
123
|
+
var index_default = icReactorPlugin;
|
|
381
124
|
export {
|
|
382
|
-
|
|
125
|
+
index_default as default,
|
|
383
126
|
icReactorPlugin
|
|
384
127
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ic-reactor/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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,29 @@
|
|
|
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.2.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.2",
|
|
48
48
|
"tsup": "^8.5.1",
|
|
49
|
-
"typescript": "^5.9.
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
50
|
"vite": "^7.3.1"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --tsconfig tsconfig.json",
|
|
54
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --tsconfig tsconfig.json"
|
|
51
55
|
}
|
|
52
|
-
}
|
|
56
|
+
}
|