@ic-reactor/codegen 0.2.0 → 0.3.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/dist/index.cjs +61 -409
- package/dist/index.d.cts +17 -171
- package/dist/index.d.ts +17 -171
- package/dist/index.js +60 -397
- package/package.json +4 -12
- package/src/__snapshots__/bindgen.test.ts.snap +18 -0
- package/src/bindgen.test.ts +85 -0
- package/src/bindgen.ts +21 -33
- package/src/index.ts +6 -37
- package/src/templates/__snapshots__/client.test.ts.snap +35 -0
- package/src/templates/__snapshots__/reactor.test.ts.snap +5 -140
- package/src/templates/client.test.ts +20 -0
- package/src/templates/client.ts +37 -0
- package/src/templates/reactor.test.ts +5 -60
- package/src/templates/reactor.ts +36 -227
- package/src/did.test.ts +0 -102
- package/src/did.ts +0 -85
- package/src/templates/__snapshots__/infiniteQuery.test.ts.snap +0 -44
- package/src/templates/__snapshots__/mutation.test.ts.snap +0 -59
- package/src/templates/__snapshots__/query.test.ts.snap +0 -64
- package/src/templates/infiniteQuery.test.ts +0 -42
- package/src/templates/infiniteQuery.ts +0 -74
- package/src/templates/mutation.test.ts +0 -43
- package/src/templates/mutation.ts +0 -51
- package/src/templates/query.test.ts +0 -44
- package/src/templates/query.ts +0 -71
- package/src/types.ts +0 -78
package/src/templates/reactor.ts
CHANGED
|
@@ -1,67 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactor file template generator
|
|
3
3
|
*
|
|
4
|
-
* Generates the reactor instance file for a canister.
|
|
5
|
-
*
|
|
4
|
+
* Generates the reactor instance file for a canister using DisplayReactor.
|
|
5
|
+
* Standardizes the output to include typed hooks and clean imports.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import path from "node:path"
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
import { extractMethods } from "../did.js"
|
|
9
|
+
import { toPascalCase, getReactorName, getServiceTypeName } from "../naming.js"
|
|
10
|
+
|
|
11
|
+
export type ReactorGeneratorOptions = {
|
|
12
|
+
canisterName: string
|
|
13
|
+
didFile: string
|
|
14
|
+
clientManagerPath?: string
|
|
15
|
+
}
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Generate the reactor file content
|
|
20
19
|
*/
|
|
21
20
|
export function generateReactorFile(options: ReactorGeneratorOptions): string {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
advanced = false,
|
|
28
|
-
didContent,
|
|
29
|
-
} = options
|
|
30
|
-
|
|
31
|
-
const pascalName = toPascalCase(canisterName)
|
|
32
|
-
const reactorName = getReactorName(canisterName)
|
|
33
|
-
const serviceName = getServiceTypeName(canisterName)
|
|
34
|
-
const reactorType =
|
|
35
|
-
canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor"
|
|
36
|
-
|
|
37
|
-
const clientManagerPath =
|
|
38
|
-
canisterConfig.clientManagerPath ??
|
|
39
|
-
globalClientManagerPath ??
|
|
40
|
-
"../../lib/client"
|
|
21
|
+
const pascalName = toPascalCase(options.canisterName)
|
|
22
|
+
const reactorName = getReactorName(options.canisterName)
|
|
23
|
+
const serviceName = getServiceTypeName(options.canisterName)
|
|
24
|
+
// Always use DisplayReactor for now
|
|
25
|
+
const reactorType = "DisplayReactor"
|
|
41
26
|
|
|
42
|
-
const didFileName = path.basename(
|
|
43
|
-
const
|
|
27
|
+
const didFileName = path.basename(options.didFile)
|
|
28
|
+
const baseName = didFileName.replace(/\.did$/, "")
|
|
29
|
+
const declarationsPath = `./declarations/${baseName}`
|
|
30
|
+
const clientManagerPath = options.clientManagerPath ?? "../../clients"
|
|
44
31
|
|
|
45
32
|
const vars: TemplateVars = {
|
|
46
|
-
canisterName,
|
|
33
|
+
canisterName: options.canisterName,
|
|
47
34
|
pascalName,
|
|
48
35
|
reactorName,
|
|
49
36
|
serviceName,
|
|
50
37
|
reactorType,
|
|
51
38
|
clientManagerPath,
|
|
52
39
|
declarationsPath,
|
|
53
|
-
useDisplayReactor: canisterConfig.useDisplayReactor !== false,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!hasDeclarations) {
|
|
57
|
-
return generateFallbackReactorFile(vars)
|
|
58
40
|
}
|
|
59
41
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return generateSimpleReactorFile(vars)
|
|
42
|
+
// If we have DID content, we can generate method-specific hooks (clean & typed)
|
|
43
|
+
return generateStandardReactorFile(vars)
|
|
65
44
|
}
|
|
66
45
|
|
|
67
46
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -76,36 +55,39 @@ interface TemplateVars {
|
|
|
76
55
|
reactorType: string
|
|
77
56
|
clientManagerPath: string
|
|
78
57
|
declarationsPath: string
|
|
79
|
-
useDisplayReactor: boolean
|
|
80
58
|
}
|
|
81
59
|
|
|
82
60
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
83
|
-
//
|
|
61
|
+
// STANDARD MODE
|
|
84
62
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
85
63
|
|
|
86
|
-
function
|
|
64
|
+
function generateStandardReactorFile(vars: TemplateVars): string {
|
|
87
65
|
const {
|
|
88
66
|
pascalName,
|
|
89
67
|
reactorName,
|
|
90
68
|
serviceName,
|
|
91
69
|
reactorType,
|
|
70
|
+
clientManagerPath,
|
|
71
|
+
declarationsPath,
|
|
92
72
|
canisterName,
|
|
93
|
-
useDisplayReactor,
|
|
94
73
|
} = vars
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
74
|
+
|
|
75
|
+
return `import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
76
|
+
import { clientManager } from "${clientManagerPath}"
|
|
77
|
+
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
78
|
+
|
|
79
|
+
export type ${serviceName} = _SERVICE
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* ${pascalName} Display Reactor
|
|
98
83
|
*/
|
|
99
84
|
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
100
85
|
clientManager,
|
|
101
86
|
idlFactory,
|
|
102
87
|
name: "${canisterName}",
|
|
103
|
-
})
|
|
104
|
-
}
|
|
88
|
+
})
|
|
105
89
|
|
|
106
|
-
|
|
107
|
-
const { pascalName, reactorName } = vars
|
|
108
|
-
return `const {
|
|
90
|
+
export const {
|
|
109
91
|
useActorQuery: use${pascalName}Query,
|
|
110
92
|
useActorSuspenseQuery: use${pascalName}SuspenseQuery,
|
|
111
93
|
useActorInfiniteQuery: use${pascalName}InfiniteQuery,
|
|
@@ -113,178 +95,5 @@ function actorHooks(vars: TemplateVars): string {
|
|
|
113
95
|
useActorMutation: use${pascalName}Mutation,
|
|
114
96
|
useActorMethod: use${pascalName}Method,
|
|
115
97
|
} = createActorHooks(${reactorName})
|
|
116
|
-
|
|
117
|
-
export {
|
|
118
|
-
use${pascalName}Query,
|
|
119
|
-
use${pascalName}SuspenseQuery,
|
|
120
|
-
use${pascalName}InfiniteQuery,
|
|
121
|
-
use${pascalName}SuspenseInfiniteQuery,
|
|
122
|
-
use${pascalName}Mutation,
|
|
123
|
-
use${pascalName}Method,
|
|
124
|
-
}`
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
128
|
-
// SIMPLE MODE
|
|
129
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
130
|
-
|
|
131
|
-
function generateSimpleReactorFile(vars: TemplateVars): string {
|
|
132
|
-
const {
|
|
133
|
-
pascalName,
|
|
134
|
-
reactorType,
|
|
135
|
-
clientManagerPath,
|
|
136
|
-
declarationsPath,
|
|
137
|
-
serviceName,
|
|
138
|
-
} = vars
|
|
139
|
-
|
|
140
|
-
return `/**
|
|
141
|
-
* ${pascalName} Reactor
|
|
142
|
-
*
|
|
143
|
-
* Auto-generated by @ic-reactor/codegen
|
|
144
|
-
*/
|
|
145
|
-
|
|
146
|
-
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
147
|
-
import { clientManager } from "${clientManagerPath}"
|
|
148
|
-
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
149
|
-
|
|
150
|
-
export type ${serviceName} = _SERVICE
|
|
151
|
-
|
|
152
|
-
${reactorInstance(vars)}
|
|
153
|
-
|
|
154
|
-
${actorHooks(vars)}
|
|
155
|
-
|
|
156
|
-
export { idlFactory }
|
|
157
|
-
`
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
161
|
-
// ADVANCED MODE
|
|
162
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
163
|
-
|
|
164
|
-
function generateAdvancedReactorFile(
|
|
165
|
-
vars: TemplateVars,
|
|
166
|
-
didContent: string
|
|
167
|
-
): string {
|
|
168
|
-
const {
|
|
169
|
-
pascalName,
|
|
170
|
-
reactorName,
|
|
171
|
-
serviceName,
|
|
172
|
-
reactorType,
|
|
173
|
-
clientManagerPath,
|
|
174
|
-
declarationsPath,
|
|
175
|
-
} = vars
|
|
176
|
-
|
|
177
|
-
const methods = extractMethods(didContent)
|
|
178
|
-
|
|
179
|
-
// Determine which extra imports we need based on methods
|
|
180
|
-
const hasQueryWithoutArgs = methods.some(
|
|
181
|
-
(m) => m.type === "query" && !m.hasArgs
|
|
182
|
-
)
|
|
183
|
-
const hasMutationWithoutArgs = methods.some(
|
|
184
|
-
(m) => m.type === "mutation" && !m.hasArgs
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
const extraImports: string[] = []
|
|
188
|
-
if (hasQueryWithoutArgs) extraImports.push("createQuery")
|
|
189
|
-
if (hasMutationWithoutArgs) extraImports.push("createMutation")
|
|
190
|
-
|
|
191
|
-
// Generate per-method static hooks (no-args methods only)
|
|
192
|
-
const perMethodHooks = methods
|
|
193
|
-
.map(({ name, type, hasArgs }) => {
|
|
194
|
-
const camelMethod = toCamelCase(name)
|
|
195
|
-
|
|
196
|
-
if (type === "query") {
|
|
197
|
-
if (!hasArgs) {
|
|
198
|
-
return `
|
|
199
|
-
export const ${camelMethod}Query = createQuery(${reactorName}, {
|
|
200
|
-
functionName: "${name}",
|
|
201
|
-
})`
|
|
202
|
-
}
|
|
203
|
-
return "" // queries with args don't get static instances
|
|
204
|
-
} else {
|
|
205
|
-
if (!hasArgs) {
|
|
206
|
-
return `
|
|
207
|
-
export const ${camelMethod}Mutation = createMutation(${reactorName}, {
|
|
208
|
-
functionName: "${name}",
|
|
209
|
-
})`
|
|
210
|
-
}
|
|
211
|
-
return "" // mutations with args don't get static instances
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
.filter(Boolean)
|
|
215
|
-
|
|
216
|
-
return `/**
|
|
217
|
-
* ${pascalName} Reactor (Advanced)
|
|
218
|
-
*
|
|
219
|
-
* Auto-generated by @ic-reactor/codegen
|
|
220
|
-
* Includes reactor instance, actor hooks, and per-method static hooks.
|
|
221
|
-
*/
|
|
222
|
-
|
|
223
|
-
import {
|
|
224
|
-
${reactorType},
|
|
225
|
-
createActorHooks,${extraImports.length > 0 ? "\n " + extraImports.join(",\n ") + "," : ""}
|
|
226
|
-
} from "@ic-reactor/react"
|
|
227
|
-
import { clientManager } from "${clientManagerPath}"
|
|
228
|
-
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
229
|
-
|
|
230
|
-
type ${serviceName} = _SERVICE
|
|
231
|
-
|
|
232
|
-
${reactorInstance(vars)}
|
|
233
|
-
|
|
234
|
-
${actorHooks(vars)}
|
|
235
|
-
${
|
|
236
|
-
perMethodHooks.length > 0
|
|
237
|
-
? `
|
|
238
|
-
// Per-method static hooks (no-args methods only)
|
|
239
|
-
${perMethodHooks.join("\n")}
|
|
240
|
-
`
|
|
241
|
-
: ""
|
|
242
|
-
}
|
|
243
|
-
export { idlFactory }
|
|
244
|
-
export type { ${serviceName} }
|
|
245
|
-
`
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
249
|
-
// FALLBACK (NO DECLARATIONS)
|
|
250
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
251
|
-
|
|
252
|
-
function generateFallbackReactorFile(vars: TemplateVars): string {
|
|
253
|
-
const {
|
|
254
|
-
canisterName,
|
|
255
|
-
pascalName,
|
|
256
|
-
serviceName,
|
|
257
|
-
reactorType,
|
|
258
|
-
clientManagerPath,
|
|
259
|
-
declarationsPath,
|
|
260
|
-
} = vars
|
|
261
|
-
|
|
262
|
-
return `/**
|
|
263
|
-
* ${pascalName} Reactor
|
|
264
|
-
*
|
|
265
|
-
* Auto-generated by @ic-reactor/codegen
|
|
266
|
-
*
|
|
267
|
-
* ⚠️ Declarations were not generated. Run:
|
|
268
|
-
* npx @icp-sdk/bindgen --input <path-to-did> --output ./${canisterName}/declarations
|
|
269
|
-
* Then uncomment the import below and remove the fallback type.
|
|
270
|
-
*/
|
|
271
|
-
|
|
272
|
-
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
273
|
-
import { clientManager } from "${clientManagerPath}"
|
|
274
|
-
|
|
275
|
-
// TODO: Uncomment after generating declarations:
|
|
276
|
-
// import { idlFactory, type _SERVICE as ${serviceName} } from "${declarationsPath}"
|
|
277
|
-
|
|
278
|
-
// Fallback — replace with generated types
|
|
279
|
-
type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
|
|
280
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
281
|
-
const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
|
|
282
|
-
|
|
283
|
-
${reactorInstance(vars)}
|
|
284
|
-
|
|
285
|
-
${actorHooks(vars)}
|
|
286
|
-
|
|
287
|
-
export { idlFactory }
|
|
288
|
-
export type { ${serviceName} }
|
|
289
98
|
`
|
|
290
99
|
}
|
package/src/did.test.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest"
|
|
2
|
-
import { extractMethods, formatMethodForDisplay } from "./did"
|
|
3
|
-
import type { MethodInfo } from "./types"
|
|
4
|
-
|
|
5
|
-
describe("DID Utilities", () => {
|
|
6
|
-
describe("extractMethods", () => {
|
|
7
|
-
it("extracts query methods", () => {
|
|
8
|
-
const didContent = `
|
|
9
|
-
type User = record { name : text; age : nat };
|
|
10
|
-
type Item = record { name : text; price : nat };
|
|
11
|
-
|
|
12
|
-
service : {
|
|
13
|
-
get_user: (nat) -> (opt User) query;
|
|
14
|
-
list_items: () -> (vec Item) query;
|
|
15
|
-
}`
|
|
16
|
-
const methods = extractMethods(didContent)
|
|
17
|
-
expect(methods).toHaveLength(2)
|
|
18
|
-
expect(methods[0]).toMatchObject({
|
|
19
|
-
name: "get_user",
|
|
20
|
-
type: "query",
|
|
21
|
-
hasArgs: true,
|
|
22
|
-
})
|
|
23
|
-
expect(methods[1]).toMatchObject({
|
|
24
|
-
name: "list_items",
|
|
25
|
-
type: "query",
|
|
26
|
-
hasArgs: false,
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it("extracts mutation (update) methods", () => {
|
|
31
|
-
const didContent = `service : {
|
|
32
|
-
create_user: (User) -> (Result);
|
|
33
|
-
update_item: (nat, Item) -> (Result);
|
|
34
|
-
}`
|
|
35
|
-
const methods = extractMethods(didContent)
|
|
36
|
-
expect(methods).toHaveLength(2)
|
|
37
|
-
expect(methods[0]).toMatchObject({
|
|
38
|
-
name: "create_user",
|
|
39
|
-
type: "mutation",
|
|
40
|
-
hasArgs: true,
|
|
41
|
-
})
|
|
42
|
-
expect(methods[1]).toMatchObject({
|
|
43
|
-
name: "update_item",
|
|
44
|
-
type: "mutation",
|
|
45
|
-
hasArgs: true,
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it("handles composite_query as query", () => {
|
|
50
|
-
const didContent = `service : {
|
|
51
|
-
search: (text) -> (vec Item) composite_query;
|
|
52
|
-
}`
|
|
53
|
-
const methods = extractMethods(didContent)
|
|
54
|
-
expect(methods[0].type).toBe("query")
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it("ignores comments", () => {
|
|
58
|
-
const didContent = `service : {
|
|
59
|
-
// This is a comment
|
|
60
|
-
get_data: () -> (text) query;
|
|
61
|
-
/* create_data: (text) -> (); */
|
|
62
|
-
}`
|
|
63
|
-
const methods = extractMethods(didContent)
|
|
64
|
-
expect(methods).toHaveLength(1)
|
|
65
|
-
expect(methods[0].name).toBe("get_data")
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it("extracts argument and return types correctly", () => {
|
|
69
|
-
const didContent = `service : {
|
|
70
|
-
weird_method: (record { foo: text; bar: nat }) -> (variant { Ok: null; Err: text }) query;
|
|
71
|
-
}`
|
|
72
|
-
const methods = extractMethods(didContent)
|
|
73
|
-
expect(methods[0].name).toBe("weird_method")
|
|
74
|
-
expect(methods[0].argsDescription).toContain(
|
|
75
|
-
"record { foo: text; bar: nat }"
|
|
76
|
-
)
|
|
77
|
-
expect(methods[0].returnDescription).toContain(
|
|
78
|
-
"variant { Ok: null; Err: text }"
|
|
79
|
-
)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe("formatMethodForDisplay", () => {
|
|
84
|
-
it("formats query method with args", () => {
|
|
85
|
-
const method: MethodInfo = {
|
|
86
|
-
name: "search",
|
|
87
|
-
type: "query",
|
|
88
|
-
hasArgs: true,
|
|
89
|
-
}
|
|
90
|
-
expect(formatMethodForDisplay(method)).toBe("search (query, with args)")
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it("formats update method without args", () => {
|
|
94
|
-
const method: MethodInfo = {
|
|
95
|
-
name: "init",
|
|
96
|
-
type: "mutation",
|
|
97
|
-
hasArgs: false,
|
|
98
|
-
}
|
|
99
|
-
expect(formatMethodForDisplay(method)).toBe("init (update, no args)")
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
})
|
package/src/did.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DID file parser
|
|
3
|
-
*
|
|
4
|
-
* Extracts method information from Candid interface definition files.
|
|
5
|
-
* Based on the CLI's parser implementation (with comment stripping).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import fs from "node:fs"
|
|
9
|
-
import type { MethodInfo } from "./types.js"
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Parse a .did file and extract method information
|
|
13
|
-
*/
|
|
14
|
-
export function parseDIDFile(didFilePath: string): MethodInfo[] {
|
|
15
|
-
if (!fs.existsSync(didFilePath)) {
|
|
16
|
-
throw new Error(`DID file not found: ${didFilePath}`)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const content = fs.readFileSync(didFilePath, "utf-8")
|
|
20
|
-
return extractMethods(content)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Extract methods from DID content
|
|
25
|
-
*
|
|
26
|
-
* Handles formats like:
|
|
27
|
-
* - `name : (args) -> (result)`
|
|
28
|
-
* - `name : (args) -> (result) query`
|
|
29
|
-
* - `name : (args) -> (result) composite_query`
|
|
30
|
-
* - `name : func (args) -> (result)`
|
|
31
|
-
*/
|
|
32
|
-
export function extractMethods(didContent: string): MethodInfo[] {
|
|
33
|
-
const methods: MethodInfo[] = []
|
|
34
|
-
|
|
35
|
-
// Remove comments
|
|
36
|
-
const cleanContent = didContent
|
|
37
|
-
.replace(/\/\/.*$/gm, "") // Single line comments
|
|
38
|
-
.replace(/\/\*[\s\S]*?\*\//g, "") // Multi-line comments
|
|
39
|
-
|
|
40
|
-
// Match method definitions
|
|
41
|
-
// Pattern: name : [func] (args) -> (result) [query|composite_query]
|
|
42
|
-
const methodRegex =
|
|
43
|
-
/([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(?:func\s*)?\(([^)]*)\)\s*->\s*\(([^)]*)\)\s*(query|composite_query)?/g
|
|
44
|
-
|
|
45
|
-
let match
|
|
46
|
-
while ((match = methodRegex.exec(cleanContent)) !== null) {
|
|
47
|
-
const name = match[1]
|
|
48
|
-
const args = match[2].trim()
|
|
49
|
-
const returnType = match[3].trim()
|
|
50
|
-
const queryAnnotation = match[4]
|
|
51
|
-
|
|
52
|
-
// Determine if it's a query or mutation
|
|
53
|
-
const isQuery =
|
|
54
|
-
queryAnnotation === "query" || queryAnnotation === "composite_query"
|
|
55
|
-
|
|
56
|
-
methods.push({
|
|
57
|
-
name,
|
|
58
|
-
type: isQuery ? "query" : "mutation",
|
|
59
|
-
hasArgs: args.length > 0 && args !== "",
|
|
60
|
-
argsDescription: args || undefined,
|
|
61
|
-
returnDescription: returnType || undefined,
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return methods
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Get methods by type
|
|
70
|
-
*/
|
|
71
|
-
export function getMethodsByType(
|
|
72
|
-
methods: MethodInfo[],
|
|
73
|
-
type: "query" | "mutation"
|
|
74
|
-
): MethodInfo[] {
|
|
75
|
-
return methods.filter((m) => m.type === type)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Format method info for display
|
|
80
|
-
*/
|
|
81
|
-
export function formatMethodForDisplay(method: MethodInfo): string {
|
|
82
|
-
const typeLabel = method.type === "query" ? "query" : "update"
|
|
83
|
-
const argsLabel = method.hasArgs ? "with args" : "no args"
|
|
84
|
-
return `${method.name} (${typeLabel}, ${argsLabel})`
|
|
85
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`Infinite Query Hook Generation > generates infinite query hook correctly 1`] = `
|
|
4
|
-
"/**
|
|
5
|
-
* Infinite Query: list_items
|
|
6
|
-
*
|
|
7
|
-
* Auto-generated by @ic-reactor/codegen
|
|
8
|
-
*
|
|
9
|
-
* ⚠️ CUSTOMIZATION REQUIRED: Configure getArgs and getNextPageParam below.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* const { data, fetchNextPage, hasNextPage } = listItemsInfiniteQuery.useInfiniteQuery()
|
|
13
|
-
* const allItems = data?.pages.flatMap(page => page.items) ?? []
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { createInfiniteQuery } from "@ic-reactor/react"
|
|
17
|
-
import { myCanisterReactor, type MyCanisterService } from "../reactor"
|
|
18
|
-
|
|
19
|
-
/** Define your pagination cursor type */
|
|
20
|
-
type PageCursor = number
|
|
21
|
-
|
|
22
|
-
export const listItemsInfiniteQuery = createInfiniteQuery(myCanisterReactor, {
|
|
23
|
-
functionName: "list_items",
|
|
24
|
-
|
|
25
|
-
initialPageParam: 0 as PageCursor,
|
|
26
|
-
|
|
27
|
-
/** Convert page param to method arguments — customize for your API */
|
|
28
|
-
getArgs: (pageParam: PageCursor) => {
|
|
29
|
-
return [{ offset: pageParam, limit: 10 }] as Parameters<MyCanisterService["list_items"]>
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
/** Extract next page param — return undefined when no more pages */
|
|
33
|
-
getNextPageParam: (lastPage, allPages, lastPageParam) => {
|
|
34
|
-
// Example: offset-based
|
|
35
|
-
// if (lastPage.items.length < 10) return undefined
|
|
36
|
-
// return lastPageParam + 10
|
|
37
|
-
return undefined
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
/** React hook for paginated list_items */
|
|
42
|
-
export const useListItemsInfiniteQuery = listItemsInfiniteQuery.useInfiniteQuery
|
|
43
|
-
"
|
|
44
|
-
`;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`Mutation Hook Generation > generates hook correctly for mutation with args 1`] = `
|
|
4
|
-
"/**
|
|
5
|
-
* Mutation: create_item
|
|
6
|
-
*
|
|
7
|
-
* Auto-generated by @ic-reactor/codegen
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const { mutate, isPending } = createItemMutation.useMutation()
|
|
11
|
-
* mutate([arg1, arg2])
|
|
12
|
-
*
|
|
13
|
-
* // Direct execution (outside React)
|
|
14
|
-
* const result = await createItemMutation.execute([arg1, arg2])
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { createMutation } from "@ic-reactor/react"
|
|
18
|
-
import { myCanisterReactor } from "../reactor"
|
|
19
|
-
|
|
20
|
-
export const createItemMutation = createMutation(myCanisterReactor, {
|
|
21
|
-
functionName: "create_item",
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
/** React hook for create_item */
|
|
25
|
-
export const useCreateItemMutation = createItemMutation.useMutation
|
|
26
|
-
|
|
27
|
-
/** Execute create_item directly (outside React) */
|
|
28
|
-
export const executeCreateItem = createItemMutation.execute
|
|
29
|
-
"
|
|
30
|
-
`;
|
|
31
|
-
|
|
32
|
-
exports[`Mutation Hook Generation > generates hook correctly for mutation without args 1`] = `
|
|
33
|
-
"/**
|
|
34
|
-
* Mutation: init_system
|
|
35
|
-
*
|
|
36
|
-
* Auto-generated by @ic-reactor/codegen
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* const { mutate, isPending } = initSystemMutation.useMutation()
|
|
40
|
-
* mutate([])
|
|
41
|
-
*
|
|
42
|
-
* // Direct execution (outside React)
|
|
43
|
-
* const result = await initSystemMutation.execute([])
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
import { createMutation } from "@ic-reactor/react"
|
|
47
|
-
import { myCanisterReactor } from "../reactor"
|
|
48
|
-
|
|
49
|
-
export const initSystemMutation = createMutation(myCanisterReactor, {
|
|
50
|
-
functionName: "init_system",
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
/** React hook for init_system */
|
|
54
|
-
export const useInitSystemMutation = initSystemMutation.useMutation
|
|
55
|
-
|
|
56
|
-
/** Execute init_system directly (outside React) */
|
|
57
|
-
export const executeInitSystem = initSystemMutation.execute
|
|
58
|
-
"
|
|
59
|
-
`;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`Query Hook Generation > generates hook for query with arguments (factory) 1`] = `
|
|
4
|
-
"/**
|
|
5
|
-
* Query Factory: get_user
|
|
6
|
-
*
|
|
7
|
-
* Auto-generated by @ic-reactor/codegen
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const { data } = getUserQuery([arg1, arg2]).useQuery()
|
|
11
|
-
* const data = await getUserQuery([arg1, arg2]).fetch()
|
|
12
|
-
* getUserQuery([arg1, arg2]).invalidate()
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { createQueryFactory } from "@ic-reactor/react"
|
|
16
|
-
import { myCanisterReactor } from "../reactor"
|
|
17
|
-
|
|
18
|
-
export const getUserQuery = createQueryFactory(myCanisterReactor, {
|
|
19
|
-
functionName: "get_user",
|
|
20
|
-
})
|
|
21
|
-
"
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
exports[`Query Hook Generation > generates hook for query without arguments (static) 1`] = `
|
|
25
|
-
"/**
|
|
26
|
-
* Query: list_items
|
|
27
|
-
*
|
|
28
|
-
* Auto-generated by @ic-reactor/codegen
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* const { data } = listItemsQuery.useQuery()
|
|
32
|
-
* const data = await listItemsQuery.fetch()
|
|
33
|
-
* listItemsQuery.invalidate()
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
import { createQuery } from "@ic-reactor/react"
|
|
37
|
-
import { myCanisterReactor } from "../reactor"
|
|
38
|
-
|
|
39
|
-
export const listItemsQuery = createQuery(myCanisterReactor, {
|
|
40
|
-
functionName: "list_items",
|
|
41
|
-
})
|
|
42
|
-
"
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
exports[`Query Hook Generation > generates suspense hooks correctly 1`] = `
|
|
46
|
-
"/**
|
|
47
|
-
* Query Factory: get_user
|
|
48
|
-
*
|
|
49
|
-
* Auto-generated by @ic-reactor/codegen
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* const { data } = getUserSuspenseQuery([arg1, arg2]).useSuspenseQuery()
|
|
53
|
-
* const data = await getUserSuspenseQuery([arg1, arg2]).fetch()
|
|
54
|
-
* getUserSuspenseQuery([arg1, arg2]).invalidate()
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
import { createSuspenseQueryFactory } from "@ic-reactor/react"
|
|
58
|
-
import { myCanisterReactor } from "../reactor"
|
|
59
|
-
|
|
60
|
-
export const getUserSuspenseQuery = createSuspenseQueryFactory(myCanisterReactor, {
|
|
61
|
-
functionName: "get_user",
|
|
62
|
-
})
|
|
63
|
-
"
|
|
64
|
-
`;
|