@ic-reactor/codegen 0.1.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/dist/index.cjs +543 -0
- package/dist/index.d.cts +237 -0
- package/dist/index.d.ts +237 -0
- package/dist/index.js +489 -0
- package/package.json +60 -0
- package/src/bindgen.ts +113 -0
- package/src/did.test.ts +102 -0
- package/src/did.ts +85 -0
- package/src/index.ts +51 -0
- package/src/naming.test.ts +59 -0
- package/src/naming.ts +83 -0
- package/src/templates/__snapshots__/infiniteQuery.test.ts.snap +44 -0
- package/src/templates/__snapshots__/mutation.test.ts.snap +59 -0
- package/src/templates/__snapshots__/query.test.ts.snap +64 -0
- package/src/templates/__snapshots__/reactor.test.ts.snap +163 -0
- package/src/templates/infiniteQuery.test.ts +42 -0
- package/src/templates/infiniteQuery.ts +74 -0
- package/src/templates/mutation.test.ts +43 -0
- package/src/templates/mutation.ts +51 -0
- package/src/templates/query.test.ts +44 -0
- package/src/templates/query.ts +71 -0
- package/src/templates/reactor.test.ts +78 -0
- package/src/templates/reactor.ts +290 -0
- package/src/types.ts +78 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
declarationsExist: () => declarationsExist,
|
|
34
|
+
extractMethods: () => extractMethods,
|
|
35
|
+
formatMethodForDisplay: () => formatMethodForDisplay,
|
|
36
|
+
generateDeclarations: () => generateDeclarations,
|
|
37
|
+
generateInfiniteQueryHook: () => generateInfiniteQueryHook,
|
|
38
|
+
generateMutationHook: () => generateMutationHook,
|
|
39
|
+
generateQueryHook: () => generateQueryHook,
|
|
40
|
+
generateReactorFile: () => generateReactorFile,
|
|
41
|
+
getHookExportName: () => getHookExportName,
|
|
42
|
+
getHookFileName: () => getHookFileName,
|
|
43
|
+
getMethodsByType: () => getMethodsByType,
|
|
44
|
+
getReactHookName: () => getReactHookName,
|
|
45
|
+
getReactorName: () => getReactorName,
|
|
46
|
+
getServiceTypeName: () => getServiceTypeName,
|
|
47
|
+
parseDIDFile: () => parseDIDFile,
|
|
48
|
+
saveCandidFile: () => saveCandidFile,
|
|
49
|
+
toCamelCase: () => toCamelCase,
|
|
50
|
+
toPascalCase: () => toPascalCase
|
|
51
|
+
});
|
|
52
|
+
module.exports = __toCommonJS(index_exports);
|
|
53
|
+
|
|
54
|
+
// src/naming.ts
|
|
55
|
+
var import_change_case = require("change-case");
|
|
56
|
+
function toPascalCase(str) {
|
|
57
|
+
return (0, import_change_case.pascalCase)(str);
|
|
58
|
+
}
|
|
59
|
+
function toCamelCase(str) {
|
|
60
|
+
return (0, import_change_case.camelCase)(str);
|
|
61
|
+
}
|
|
62
|
+
function getHookFileName(methodName, hookType) {
|
|
63
|
+
const camelMethod = toCamelCase(methodName);
|
|
64
|
+
const pascalType = toPascalCase(hookType);
|
|
65
|
+
return `${camelMethod}${pascalType}.ts`;
|
|
66
|
+
}
|
|
67
|
+
function getHookExportName(methodName, hookType) {
|
|
68
|
+
const camelMethod = toCamelCase(methodName);
|
|
69
|
+
const pascalType = toPascalCase(hookType);
|
|
70
|
+
return `${camelMethod}${pascalType}`;
|
|
71
|
+
}
|
|
72
|
+
function getReactHookName(methodName, hookType) {
|
|
73
|
+
const pascalMethod = toPascalCase(methodName);
|
|
74
|
+
const pascalType = toPascalCase(hookType);
|
|
75
|
+
return `use${pascalMethod}${pascalType}`;
|
|
76
|
+
}
|
|
77
|
+
function getReactorName(canisterName) {
|
|
78
|
+
return `${toCamelCase(canisterName)}Reactor`;
|
|
79
|
+
}
|
|
80
|
+
function getServiceTypeName(canisterName) {
|
|
81
|
+
return `${toPascalCase(canisterName)}Service`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/did.ts
|
|
85
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
86
|
+
function parseDIDFile(didFilePath) {
|
|
87
|
+
if (!import_node_fs.default.existsSync(didFilePath)) {
|
|
88
|
+
throw new Error(`DID file not found: ${didFilePath}`);
|
|
89
|
+
}
|
|
90
|
+
const content = import_node_fs.default.readFileSync(didFilePath, "utf-8");
|
|
91
|
+
return extractMethods(content);
|
|
92
|
+
}
|
|
93
|
+
function extractMethods(didContent) {
|
|
94
|
+
const methods = [];
|
|
95
|
+
const cleanContent = didContent.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
96
|
+
const methodRegex = /([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(?:func\s*)?\(([^)]*)\)\s*->\s*\(([^)]*)\)\s*(query|composite_query)?/g;
|
|
97
|
+
let match;
|
|
98
|
+
while ((match = methodRegex.exec(cleanContent)) !== null) {
|
|
99
|
+
const name = match[1];
|
|
100
|
+
const args = match[2].trim();
|
|
101
|
+
const returnType = match[3].trim();
|
|
102
|
+
const queryAnnotation = match[4];
|
|
103
|
+
const isQuery = queryAnnotation === "query" || queryAnnotation === "composite_query";
|
|
104
|
+
methods.push({
|
|
105
|
+
name,
|
|
106
|
+
type: isQuery ? "query" : "mutation",
|
|
107
|
+
hasArgs: args.length > 0 && args !== "",
|
|
108
|
+
argsDescription: args || void 0,
|
|
109
|
+
returnDescription: returnType || void 0
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return methods;
|
|
113
|
+
}
|
|
114
|
+
function getMethodsByType(methods, type) {
|
|
115
|
+
return methods.filter((m) => m.type === type);
|
|
116
|
+
}
|
|
117
|
+
function formatMethodForDisplay(method) {
|
|
118
|
+
const typeLabel = method.type === "query" ? "query" : "update";
|
|
119
|
+
const argsLabel = method.hasArgs ? "with args" : "no args";
|
|
120
|
+
return `${method.name} (${typeLabel}, ${argsLabel})`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/bindgen.ts
|
|
124
|
+
var import_core = require("@icp-sdk/bindgen/core");
|
|
125
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
126
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
127
|
+
async function generateDeclarations(options) {
|
|
128
|
+
const { didFile, outDir } = options;
|
|
129
|
+
if (!import_node_fs2.default.existsSync(didFile)) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
declarationsDir: "",
|
|
133
|
+
error: `DID file not found: ${didFile}`
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const declarationsDir = import_node_path.default.join(outDir, "declarations");
|
|
137
|
+
try {
|
|
138
|
+
if (!import_node_fs2.default.existsSync(outDir)) {
|
|
139
|
+
import_node_fs2.default.mkdirSync(outDir, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
if (import_node_fs2.default.existsSync(declarationsDir)) {
|
|
142
|
+
import_node_fs2.default.rmSync(declarationsDir, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
import_node_fs2.default.mkdirSync(declarationsDir, { recursive: true });
|
|
145
|
+
await (0, import_core.generate)({
|
|
146
|
+
didFile,
|
|
147
|
+
outDir,
|
|
148
|
+
// Pass the parent directory; bindgen appends "declarations"
|
|
149
|
+
output: {
|
|
150
|
+
actor: {
|
|
151
|
+
disabled: true
|
|
152
|
+
// We don't need actor creation, we use Reactor
|
|
153
|
+
},
|
|
154
|
+
force: true
|
|
155
|
+
// Overwrite existing files
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
declarationsDir
|
|
161
|
+
};
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
declarationsDir,
|
|
166
|
+
error: error instanceof Error ? error.message : String(error)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function declarationsExist(outDir, canisterName) {
|
|
171
|
+
const declarationsDir = import_node_path.default.join(outDir, "declarations");
|
|
172
|
+
const didTsPath = import_node_path.default.join(declarationsDir, `${canisterName}.did.ts`);
|
|
173
|
+
return import_node_fs2.default.existsSync(didTsPath);
|
|
174
|
+
}
|
|
175
|
+
function saveCandidFile(candidSource, outDir, canisterName) {
|
|
176
|
+
const candidDir = import_node_path.default.join(outDir, "candid");
|
|
177
|
+
if (!import_node_fs2.default.existsSync(candidDir)) {
|
|
178
|
+
import_node_fs2.default.mkdirSync(candidDir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
const candidPath = import_node_path.default.join(candidDir, `${canisterName}.did`);
|
|
181
|
+
import_node_fs2.default.writeFileSync(candidPath, candidSource);
|
|
182
|
+
return candidPath;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/templates/reactor.ts
|
|
186
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
187
|
+
function generateReactorFile(options) {
|
|
188
|
+
const {
|
|
189
|
+
canisterName,
|
|
190
|
+
canisterConfig,
|
|
191
|
+
globalClientManagerPath,
|
|
192
|
+
hasDeclarations = true,
|
|
193
|
+
advanced = false,
|
|
194
|
+
didContent
|
|
195
|
+
} = options;
|
|
196
|
+
const pascalName = toPascalCase(canisterName);
|
|
197
|
+
const reactorName = getReactorName(canisterName);
|
|
198
|
+
const serviceName = getServiceTypeName(canisterName);
|
|
199
|
+
const reactorType = canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor";
|
|
200
|
+
const clientManagerPath = canisterConfig.clientManagerPath ?? globalClientManagerPath ?? "../../lib/client";
|
|
201
|
+
const didFileName = import_node_path2.default.basename(canisterConfig.didFile);
|
|
202
|
+
const declarationsPath = `./declarations/${didFileName}`;
|
|
203
|
+
const vars = {
|
|
204
|
+
canisterName,
|
|
205
|
+
pascalName,
|
|
206
|
+
reactorName,
|
|
207
|
+
serviceName,
|
|
208
|
+
reactorType,
|
|
209
|
+
clientManagerPath,
|
|
210
|
+
declarationsPath,
|
|
211
|
+
useDisplayReactor: canisterConfig.useDisplayReactor !== false
|
|
212
|
+
};
|
|
213
|
+
if (!hasDeclarations) {
|
|
214
|
+
return generateFallbackReactorFile(vars);
|
|
215
|
+
}
|
|
216
|
+
if (advanced && didContent) {
|
|
217
|
+
return generateAdvancedReactorFile(vars, didContent);
|
|
218
|
+
}
|
|
219
|
+
return generateSimpleReactorFile(vars);
|
|
220
|
+
}
|
|
221
|
+
function reactorInstance(vars) {
|
|
222
|
+
const {
|
|
223
|
+
pascalName,
|
|
224
|
+
reactorName,
|
|
225
|
+
serviceName,
|
|
226
|
+
reactorType,
|
|
227
|
+
canisterName,
|
|
228
|
+
useDisplayReactor
|
|
229
|
+
} = vars;
|
|
230
|
+
return `/**
|
|
231
|
+
* ${pascalName} Reactor \u2014 ${useDisplayReactor ? "Display" : "Candid"} mode.
|
|
232
|
+
* ${useDisplayReactor ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
|
|
233
|
+
*/
|
|
234
|
+
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
235
|
+
clientManager,
|
|
236
|
+
idlFactory,
|
|
237
|
+
name: "${canisterName}",
|
|
238
|
+
})`;
|
|
239
|
+
}
|
|
240
|
+
function actorHooks(vars) {
|
|
241
|
+
const { pascalName, reactorName } = vars;
|
|
242
|
+
return `const {
|
|
243
|
+
useActorQuery: use${pascalName}Query,
|
|
244
|
+
useActorSuspenseQuery: use${pascalName}SuspenseQuery,
|
|
245
|
+
useActorInfiniteQuery: use${pascalName}InfiniteQuery,
|
|
246
|
+
useActorSuspenseInfiniteQuery: use${pascalName}SuspenseInfiniteQuery,
|
|
247
|
+
useActorMutation: use${pascalName}Mutation,
|
|
248
|
+
useActorMethod: use${pascalName}Method,
|
|
249
|
+
} = createActorHooks(${reactorName})
|
|
250
|
+
|
|
251
|
+
export {
|
|
252
|
+
use${pascalName}Query,
|
|
253
|
+
use${pascalName}SuspenseQuery,
|
|
254
|
+
use${pascalName}InfiniteQuery,
|
|
255
|
+
use${pascalName}SuspenseInfiniteQuery,
|
|
256
|
+
use${pascalName}Mutation,
|
|
257
|
+
use${pascalName}Method,
|
|
258
|
+
}`;
|
|
259
|
+
}
|
|
260
|
+
function generateSimpleReactorFile(vars) {
|
|
261
|
+
const {
|
|
262
|
+
pascalName,
|
|
263
|
+
reactorType,
|
|
264
|
+
clientManagerPath,
|
|
265
|
+
declarationsPath,
|
|
266
|
+
serviceName
|
|
267
|
+
} = vars;
|
|
268
|
+
return `/**
|
|
269
|
+
* ${pascalName} Reactor
|
|
270
|
+
*
|
|
271
|
+
* Auto-generated by @ic-reactor/codegen
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
275
|
+
import { clientManager } from "${clientManagerPath}"
|
|
276
|
+
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
277
|
+
|
|
278
|
+
export type ${serviceName} = _SERVICE
|
|
279
|
+
|
|
280
|
+
${reactorInstance(vars)}
|
|
281
|
+
|
|
282
|
+
${actorHooks(vars)}
|
|
283
|
+
|
|
284
|
+
export { idlFactory }
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
function generateAdvancedReactorFile(vars, didContent) {
|
|
288
|
+
const {
|
|
289
|
+
pascalName,
|
|
290
|
+
reactorName,
|
|
291
|
+
serviceName,
|
|
292
|
+
reactorType,
|
|
293
|
+
clientManagerPath,
|
|
294
|
+
declarationsPath
|
|
295
|
+
} = vars;
|
|
296
|
+
const methods = extractMethods(didContent);
|
|
297
|
+
const hasQueryWithoutArgs = methods.some(
|
|
298
|
+
(m) => m.type === "query" && !m.hasArgs
|
|
299
|
+
);
|
|
300
|
+
const hasMutationWithoutArgs = methods.some(
|
|
301
|
+
(m) => m.type === "mutation" && !m.hasArgs
|
|
302
|
+
);
|
|
303
|
+
const extraImports = [];
|
|
304
|
+
if (hasQueryWithoutArgs) extraImports.push("createQuery");
|
|
305
|
+
if (hasMutationWithoutArgs) extraImports.push("createMutation");
|
|
306
|
+
const perMethodHooks = methods.map(({ name, type, hasArgs }) => {
|
|
307
|
+
const camelMethod = toCamelCase(name);
|
|
308
|
+
if (type === "query") {
|
|
309
|
+
if (!hasArgs) {
|
|
310
|
+
return `
|
|
311
|
+
export const ${camelMethod}Query = createQuery(${reactorName}, {
|
|
312
|
+
functionName: "${name}",
|
|
313
|
+
})`;
|
|
314
|
+
}
|
|
315
|
+
return "";
|
|
316
|
+
} else {
|
|
317
|
+
if (!hasArgs) {
|
|
318
|
+
return `
|
|
319
|
+
export const ${camelMethod}Mutation = createMutation(${reactorName}, {
|
|
320
|
+
functionName: "${name}",
|
|
321
|
+
})`;
|
|
322
|
+
}
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
}).filter(Boolean);
|
|
326
|
+
return `/**
|
|
327
|
+
* ${pascalName} Reactor (Advanced)
|
|
328
|
+
*
|
|
329
|
+
* Auto-generated by @ic-reactor/codegen
|
|
330
|
+
* Includes reactor instance, actor hooks, and per-method static hooks.
|
|
331
|
+
*/
|
|
332
|
+
|
|
333
|
+
import {
|
|
334
|
+
${reactorType},
|
|
335
|
+
createActorHooks,${extraImports.length > 0 ? "\n " + extraImports.join(",\n ") + "," : ""}
|
|
336
|
+
} from "@ic-reactor/react"
|
|
337
|
+
import { clientManager } from "${clientManagerPath}"
|
|
338
|
+
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
339
|
+
|
|
340
|
+
type ${serviceName} = _SERVICE
|
|
341
|
+
|
|
342
|
+
${reactorInstance(vars)}
|
|
343
|
+
|
|
344
|
+
${actorHooks(vars)}
|
|
345
|
+
${perMethodHooks.length > 0 ? `
|
|
346
|
+
// Per-method static hooks (no-args methods only)
|
|
347
|
+
${perMethodHooks.join("\n")}
|
|
348
|
+
` : ""}
|
|
349
|
+
export { idlFactory }
|
|
350
|
+
export type { ${serviceName} }
|
|
351
|
+
`;
|
|
352
|
+
}
|
|
353
|
+
function generateFallbackReactorFile(vars) {
|
|
354
|
+
const {
|
|
355
|
+
canisterName,
|
|
356
|
+
pascalName,
|
|
357
|
+
serviceName,
|
|
358
|
+
reactorType,
|
|
359
|
+
clientManagerPath,
|
|
360
|
+
declarationsPath
|
|
361
|
+
} = vars;
|
|
362
|
+
return `/**
|
|
363
|
+
* ${pascalName} Reactor
|
|
364
|
+
*
|
|
365
|
+
* Auto-generated by @ic-reactor/codegen
|
|
366
|
+
*
|
|
367
|
+
* \u26A0\uFE0F Declarations were not generated. Run:
|
|
368
|
+
* npx @icp-sdk/bindgen --input <path-to-did> --output ./${canisterName}/declarations
|
|
369
|
+
* Then uncomment the import below and remove the fallback type.
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
373
|
+
import { clientManager } from "${clientManagerPath}"
|
|
374
|
+
|
|
375
|
+
// TODO: Uncomment after generating declarations:
|
|
376
|
+
// import { idlFactory, type _SERVICE as ${serviceName} } from "${declarationsPath}"
|
|
377
|
+
|
|
378
|
+
// Fallback \u2014 replace with generated types
|
|
379
|
+
type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
|
+
const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
|
|
382
|
+
|
|
383
|
+
${reactorInstance(vars)}
|
|
384
|
+
|
|
385
|
+
${actorHooks(vars)}
|
|
386
|
+
|
|
387
|
+
export { idlFactory }
|
|
388
|
+
export type { ${serviceName} }
|
|
389
|
+
`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/templates/query.ts
|
|
393
|
+
function generateQueryHook(options) {
|
|
394
|
+
const { canisterName, method, type = "query" } = options;
|
|
395
|
+
const reactorName = getReactorName(canisterName);
|
|
396
|
+
const hookExportName = getHookExportName(method.name, type);
|
|
397
|
+
const isSuspense = type === "suspenseQuery";
|
|
398
|
+
const creatorFn = isSuspense ? "createSuspenseQuery" : "createQuery";
|
|
399
|
+
const factoryFn = isSuspense ? "createSuspenseQueryFactory" : "createQueryFactory";
|
|
400
|
+
const hookName = isSuspense ? "useSuspenseQuery" : "useQuery";
|
|
401
|
+
if (method.hasArgs) {
|
|
402
|
+
return `/**
|
|
403
|
+
* Query Factory: ${method.name}
|
|
404
|
+
*
|
|
405
|
+
* Auto-generated by @ic-reactor/codegen
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* const { data } = ${hookExportName}([arg1, arg2]).${hookName}()
|
|
409
|
+
* const data = await ${hookExportName}([arg1, arg2]).fetch()
|
|
410
|
+
* ${hookExportName}([arg1, arg2]).invalidate()
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
import { ${factoryFn} } from "@ic-reactor/react"
|
|
414
|
+
import { ${reactorName} } from "../reactor"
|
|
415
|
+
|
|
416
|
+
export const ${hookExportName} = ${factoryFn}(${reactorName}, {
|
|
417
|
+
functionName: "${method.name}",
|
|
418
|
+
})
|
|
419
|
+
`;
|
|
420
|
+
}
|
|
421
|
+
return `/**
|
|
422
|
+
* Query: ${method.name}
|
|
423
|
+
*
|
|
424
|
+
* Auto-generated by @ic-reactor/codegen
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* const { data } = ${hookExportName}.${hookName}()
|
|
428
|
+
* const data = await ${hookExportName}.fetch()
|
|
429
|
+
* ${hookExportName}.invalidate()
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
import { ${creatorFn} } from "@ic-reactor/react"
|
|
433
|
+
import { ${reactorName} } from "../reactor"
|
|
434
|
+
|
|
435
|
+
export const ${hookExportName} = ${creatorFn}(${reactorName}, {
|
|
436
|
+
functionName: "${method.name}",
|
|
437
|
+
})
|
|
438
|
+
`;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/templates/mutation.ts
|
|
442
|
+
function generateMutationHook(options) {
|
|
443
|
+
const { canisterName, method } = options;
|
|
444
|
+
const pascalMethod = toPascalCase(method.name);
|
|
445
|
+
const reactorName = getReactorName(canisterName);
|
|
446
|
+
const hookExportName = getHookExportName(method.name, "mutation");
|
|
447
|
+
return `/**
|
|
448
|
+
* Mutation: ${method.name}
|
|
449
|
+
*
|
|
450
|
+
* Auto-generated by @ic-reactor/codegen
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* const { mutate, isPending } = ${hookExportName}.useMutation()
|
|
454
|
+
* mutate(${method.hasArgs ? "[arg1, arg2]" : "[]"})
|
|
455
|
+
*
|
|
456
|
+
* // Direct execution (outside React)
|
|
457
|
+
* const result = await ${hookExportName}.execute(${method.hasArgs ? "[arg1, arg2]" : "[]"})
|
|
458
|
+
*/
|
|
459
|
+
|
|
460
|
+
import { createMutation } from "@ic-reactor/react"
|
|
461
|
+
import { ${reactorName} } from "../reactor"
|
|
462
|
+
|
|
463
|
+
export const ${hookExportName} = createMutation(${reactorName}, {
|
|
464
|
+
functionName: "${method.name}",
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
/** React hook for ${method.name} */
|
|
468
|
+
export const use${pascalMethod}Mutation = ${hookExportName}.useMutation
|
|
469
|
+
|
|
470
|
+
/** Execute ${method.name} directly (outside React) */
|
|
471
|
+
export const execute${pascalMethod} = ${hookExportName}.execute
|
|
472
|
+
`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// src/templates/infiniteQuery.ts
|
|
476
|
+
function generateInfiniteQueryHook(options) {
|
|
477
|
+
const { canisterName, method, type = "infiniteQuery" } = options;
|
|
478
|
+
const reactorName = getReactorName(canisterName);
|
|
479
|
+
const serviceName = getServiceTypeName(canisterName);
|
|
480
|
+
const hookExportName = getHookExportName(method.name, type);
|
|
481
|
+
const reactHookName = getReactHookName(method.name, type);
|
|
482
|
+
return `/**
|
|
483
|
+
* Infinite Query: ${method.name}
|
|
484
|
+
*
|
|
485
|
+
* Auto-generated by @ic-reactor/codegen
|
|
486
|
+
*
|
|
487
|
+
* \u26A0\uFE0F CUSTOMIZATION REQUIRED: Configure getArgs and getNextPageParam below.
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* const { data, fetchNextPage, hasNextPage } = ${hookExportName}.useInfiniteQuery()
|
|
491
|
+
* const allItems = data?.pages.flatMap(page => page.items) ?? []
|
|
492
|
+
*/
|
|
493
|
+
|
|
494
|
+
import { createInfiniteQuery } from "@ic-reactor/react"
|
|
495
|
+
import { ${reactorName}, type ${serviceName} } from "../reactor"
|
|
496
|
+
|
|
497
|
+
/** Define your pagination cursor type */
|
|
498
|
+
type PageCursor = number
|
|
499
|
+
|
|
500
|
+
export const ${hookExportName} = createInfiniteQuery(${reactorName}, {
|
|
501
|
+
functionName: "${method.name}",
|
|
502
|
+
|
|
503
|
+
initialPageParam: 0 as PageCursor,
|
|
504
|
+
|
|
505
|
+
/** Convert page param to method arguments \u2014 customize for your API */
|
|
506
|
+
getArgs: (pageParam: PageCursor) => {
|
|
507
|
+
return [{ offset: pageParam, limit: 10 }] as Parameters<${serviceName}["${method.name}"]>
|
|
508
|
+
},
|
|
509
|
+
|
|
510
|
+
/** Extract next page param \u2014 return undefined when no more pages */
|
|
511
|
+
getNextPageParam: (lastPage, allPages, lastPageParam) => {
|
|
512
|
+
// Example: offset-based
|
|
513
|
+
// if (lastPage.items.length < 10) return undefined
|
|
514
|
+
// return lastPageParam + 10
|
|
515
|
+
return undefined
|
|
516
|
+
},
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
/** React hook for paginated ${method.name} */
|
|
520
|
+
export const ${reactHookName} = ${hookExportName}.useInfiniteQuery
|
|
521
|
+
`;
|
|
522
|
+
}
|
|
523
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
524
|
+
0 && (module.exports = {
|
|
525
|
+
declarationsExist,
|
|
526
|
+
extractMethods,
|
|
527
|
+
formatMethodForDisplay,
|
|
528
|
+
generateDeclarations,
|
|
529
|
+
generateInfiniteQueryHook,
|
|
530
|
+
generateMutationHook,
|
|
531
|
+
generateQueryHook,
|
|
532
|
+
generateReactorFile,
|
|
533
|
+
getHookExportName,
|
|
534
|
+
getHookFileName,
|
|
535
|
+
getMethodsByType,
|
|
536
|
+
getReactHookName,
|
|
537
|
+
getReactorName,
|
|
538
|
+
getServiceTypeName,
|
|
539
|
+
parseDIDFile,
|
|
540
|
+
saveCandidFile,
|
|
541
|
+
toCamelCase,
|
|
542
|
+
toPascalCase
|
|
543
|
+
});
|