@ic-reactor/cli 0.0.0-dev1 → 0.0.0-dev3
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.js +267 -60
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -46,12 +46,9 @@ function saveConfig(config, configPath = path.join(process.cwd(), CONFIG_FILE_NA
|
|
|
46
46
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
47
47
|
}
|
|
48
48
|
function getProjectRoot() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return currentDir;
|
|
53
|
-
}
|
|
54
|
-
currentDir = path.dirname(currentDir);
|
|
49
|
+
const configPath = findConfigFile();
|
|
50
|
+
if (configPath) {
|
|
51
|
+
return path.dirname(configPath);
|
|
55
52
|
}
|
|
56
53
|
return process.cwd();
|
|
57
54
|
}
|
|
@@ -230,8 +227,8 @@ export const clientManager = new ClientManager({
|
|
|
230
227
|
|
|
231
228
|
// src/commands/add.ts
|
|
232
229
|
import * as p2 from "@clack/prompts";
|
|
233
|
-
import
|
|
234
|
-
import
|
|
230
|
+
import fs5 from "fs";
|
|
231
|
+
import path4 from "path";
|
|
235
232
|
import pc2 from "picocolors";
|
|
236
233
|
|
|
237
234
|
// src/parsers/did.ts
|
|
@@ -302,14 +299,15 @@ function getServiceTypeName(canisterName) {
|
|
|
302
299
|
|
|
303
300
|
// src/generators/reactor.ts
|
|
304
301
|
function generateReactorFile(options) {
|
|
305
|
-
const { canisterName, canisterConfig } = options;
|
|
302
|
+
const { canisterName, canisterConfig, hasDeclarations = true } = options;
|
|
306
303
|
const pascalName = toPascalCase(canisterName);
|
|
307
304
|
const reactorName = getReactorName(canisterName);
|
|
308
305
|
const serviceName = getServiceTypeName(canisterName);
|
|
309
306
|
const reactorType = canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor";
|
|
310
307
|
const clientManagerPath = canisterConfig.clientManagerPath ?? "../../lib/client";
|
|
311
308
|
const declarationsPath = `./declarations/${canisterName}.did`;
|
|
312
|
-
|
|
309
|
+
if (hasDeclarations) {
|
|
310
|
+
return `/**
|
|
313
311
|
* ${pascalName} Reactor
|
|
314
312
|
*
|
|
315
313
|
* Auto-generated by @ic-reactor/cli
|
|
@@ -368,6 +366,78 @@ export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clie
|
|
|
368
366
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
369
367
|
|
|
370
368
|
export { idlFactory }
|
|
369
|
+
`;
|
|
370
|
+
}
|
|
371
|
+
return `/**
|
|
372
|
+
* ${pascalName} Reactor
|
|
373
|
+
*
|
|
374
|
+
* Auto-generated by @ic-reactor/cli
|
|
375
|
+
* This file provides the shared reactor instance for the ${canisterName} canister.
|
|
376
|
+
*
|
|
377
|
+
* You can customize this file to add global configuration.
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
|
|
381
|
+
import { clientManager } from "${clientManagerPath}"
|
|
382
|
+
|
|
383
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
384
|
+
// DECLARATIONS
|
|
385
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
386
|
+
|
|
387
|
+
// TODO: Generate proper types by running:
|
|
388
|
+
// npx @icp-sdk/bindgen --input <path-to-did> --output ./${canisterName}/declarations
|
|
389
|
+
|
|
390
|
+
// Then uncomment:
|
|
391
|
+
// import { idlFactory, type _SERVICE as ${serviceName} } from "${declarationsPath}"
|
|
392
|
+
|
|
393
|
+
// Fallback generic type - replace with generated types
|
|
394
|
+
type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
|
|
395
|
+
|
|
396
|
+
// You'll need to define idlFactory here or import from declarations
|
|
397
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
398
|
+
const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
|
|
399
|
+
|
|
400
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
401
|
+
// REACTOR INSTANCE
|
|
402
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* ${pascalName} Reactor with ${canisterConfig.useDisplayReactor !== false ? "Display" : "Candid"} type transformations.
|
|
406
|
+
* ${canisterConfig.useDisplayReactor !== false ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
|
|
407
|
+
*/
|
|
408
|
+
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
409
|
+
clientManager,
|
|
410
|
+
idlFactory,
|
|
411
|
+
name: "${canisterName}",
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
415
|
+
// BASE HOOKS
|
|
416
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Base actor hooks - use these directly or import method-specific hooks.
|
|
420
|
+
*/
|
|
421
|
+
export const {
|
|
422
|
+
useActorQuery,
|
|
423
|
+
useActorMutation,
|
|
424
|
+
useActorSuspenseQuery,
|
|
425
|
+
useActorInfiniteQuery,
|
|
426
|
+
useActorSuspenseInfiniteQuery,
|
|
427
|
+
useActorMethod,
|
|
428
|
+
} = createActorHooks(${reactorName})
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Auth hooks for the client manager.
|
|
432
|
+
*/
|
|
433
|
+
export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
|
|
434
|
+
|
|
435
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
436
|
+
// RE-EXPORTS
|
|
437
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
438
|
+
|
|
439
|
+
export { idlFactory }
|
|
440
|
+
export type { ${serviceName} }
|
|
371
441
|
`;
|
|
372
442
|
}
|
|
373
443
|
|
|
@@ -762,6 +832,46 @@ export const invalidate${pascalMethod}Pages = ${camelMethod}InfiniteQuery.invali
|
|
|
762
832
|
`;
|
|
763
833
|
}
|
|
764
834
|
|
|
835
|
+
// src/utils/bindgen.ts
|
|
836
|
+
import { generate } from "@icp-sdk/bindgen/core";
|
|
837
|
+
import path3 from "path";
|
|
838
|
+
import fs4 from "fs";
|
|
839
|
+
async function generateDeclarations(options) {
|
|
840
|
+
const { didFile, outDir } = options;
|
|
841
|
+
if (!fs4.existsSync(didFile)) {
|
|
842
|
+
return {
|
|
843
|
+
success: false,
|
|
844
|
+
declarationsDir: "",
|
|
845
|
+
error: `DID file not found: ${didFile}`
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
const declarationsDir = path3.join(outDir, "declarations");
|
|
849
|
+
try {
|
|
850
|
+
await generate({
|
|
851
|
+
didFile,
|
|
852
|
+
outDir: declarationsDir,
|
|
853
|
+
output: {
|
|
854
|
+
actor: {
|
|
855
|
+
disabled: true
|
|
856
|
+
// We don't need actor creation, we use Reactor
|
|
857
|
+
},
|
|
858
|
+
force: true
|
|
859
|
+
// Overwrite existing files
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
return {
|
|
863
|
+
success: true,
|
|
864
|
+
declarationsDir
|
|
865
|
+
};
|
|
866
|
+
} catch (error) {
|
|
867
|
+
return {
|
|
868
|
+
success: false,
|
|
869
|
+
declarationsDir,
|
|
870
|
+
error: error instanceof Error ? error.message : String(error)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
765
875
|
// src/commands/add.ts
|
|
766
876
|
async function addCommand(options) {
|
|
767
877
|
console.log();
|
|
@@ -824,7 +934,7 @@ async function addCommand(options) {
|
|
|
824
934
|
p2.log.error(`Canister ${pc2.yellow(selectedCanister)} not found in config.`);
|
|
825
935
|
process.exit(1);
|
|
826
936
|
}
|
|
827
|
-
const didFilePath =
|
|
937
|
+
const didFilePath = path4.resolve(projectRoot, canisterConfig.didFile);
|
|
828
938
|
let methods;
|
|
829
939
|
try {
|
|
830
940
|
methods = parseDIDFile(didFilePath);
|
|
@@ -949,26 +1059,42 @@ ${error.message}`
|
|
|
949
1059
|
p2.log.warn("All methods were skipped. Nothing to generate.");
|
|
950
1060
|
process.exit(0);
|
|
951
1061
|
}
|
|
952
|
-
const canisterOutDir =
|
|
953
|
-
const hooksOutDir =
|
|
1062
|
+
const canisterOutDir = path4.join(projectRoot, config.outDir, selectedCanister);
|
|
1063
|
+
const hooksOutDir = path4.join(canisterOutDir, "hooks");
|
|
954
1064
|
ensureDir(hooksOutDir);
|
|
955
1065
|
const spinner4 = p2.spinner();
|
|
956
1066
|
spinner4.start("Generating hooks...");
|
|
957
1067
|
const generatedFiles = [];
|
|
958
|
-
const reactorPath =
|
|
1068
|
+
const reactorPath = path4.join(canisterOutDir, "reactor.ts");
|
|
959
1069
|
if (!fileExists(reactorPath)) {
|
|
1070
|
+
spinner4.message("Generating TypeScript declarations...");
|
|
1071
|
+
const bindgenResult = await generateDeclarations({
|
|
1072
|
+
didFile: didFilePath,
|
|
1073
|
+
outDir: canisterOutDir,
|
|
1074
|
+
canisterName: selectedCanister
|
|
1075
|
+
});
|
|
1076
|
+
if (bindgenResult.success) {
|
|
1077
|
+
generatedFiles.push("declarations/");
|
|
1078
|
+
} else {
|
|
1079
|
+
p2.log.warn(`Could not generate declarations: ${bindgenResult.error}`);
|
|
1080
|
+
p2.log.info(
|
|
1081
|
+
`You can manually run: npx @icp-sdk/bindgen --input ${didFilePath} --output ${canisterOutDir}/declarations`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
spinner4.message("Generating reactor...");
|
|
960
1085
|
const reactorContent = generateReactorFile({
|
|
961
1086
|
canisterName: selectedCanister,
|
|
962
1087
|
canisterConfig,
|
|
963
1088
|
config,
|
|
964
|
-
outDir: canisterOutDir
|
|
1089
|
+
outDir: canisterOutDir,
|
|
1090
|
+
hasDeclarations: bindgenResult.success
|
|
965
1091
|
});
|
|
966
|
-
|
|
1092
|
+
fs5.writeFileSync(reactorPath, reactorContent);
|
|
967
1093
|
generatedFiles.push("reactor.ts");
|
|
968
1094
|
}
|
|
969
1095
|
for (const { method, hookType } of methodsWithHookTypes) {
|
|
970
1096
|
const fileName = getHookFileName(method.name, hookType);
|
|
971
|
-
const filePath =
|
|
1097
|
+
const filePath = path4.join(hooksOutDir, fileName);
|
|
972
1098
|
let content;
|
|
973
1099
|
switch (hookType) {
|
|
974
1100
|
case "query":
|
|
@@ -995,12 +1121,12 @@ ${error.message}`
|
|
|
995
1121
|
});
|
|
996
1122
|
break;
|
|
997
1123
|
}
|
|
998
|
-
|
|
999
|
-
generatedFiles.push(
|
|
1124
|
+
fs5.writeFileSync(filePath, content);
|
|
1125
|
+
generatedFiles.push(path4.join("hooks", fileName));
|
|
1000
1126
|
}
|
|
1001
|
-
const indexPath =
|
|
1127
|
+
const indexPath = path4.join(hooksOutDir, "index.ts");
|
|
1002
1128
|
const indexContent = generateIndexFile(methodsWithHookTypes);
|
|
1003
|
-
|
|
1129
|
+
fs5.writeFileSync(indexPath, indexContent);
|
|
1004
1130
|
generatedFiles.push("hooks/index.ts");
|
|
1005
1131
|
config.generatedHooks[selectedCanister] = [
|
|
1006
1132
|
.../* @__PURE__ */ new Set([
|
|
@@ -1013,7 +1139,7 @@ ${error.message}`
|
|
|
1013
1139
|
console.log();
|
|
1014
1140
|
p2.note(
|
|
1015
1141
|
generatedFiles.map((f) => pc2.green(`\u2713 ${f}`)).join("\n"),
|
|
1016
|
-
`Generated in ${pc2.dim(
|
|
1142
|
+
`Generated in ${pc2.dim(path4.relative(projectRoot, canisterOutDir))}`
|
|
1017
1143
|
);
|
|
1018
1144
|
p2.outro(pc2.green("\u2713 Done!"));
|
|
1019
1145
|
}
|
|
@@ -1032,8 +1158,8 @@ async function promptForNewCanister(projectRoot) {
|
|
|
1032
1158
|
placeholder: "./backend.did",
|
|
1033
1159
|
validate: (value) => {
|
|
1034
1160
|
if (!value) return "DID file path is required";
|
|
1035
|
-
const fullPath =
|
|
1036
|
-
if (!
|
|
1161
|
+
const fullPath = path4.resolve(projectRoot, value);
|
|
1162
|
+
if (!fs5.existsSync(fullPath)) {
|
|
1037
1163
|
return `File not found: ${value}`;
|
|
1038
1164
|
}
|
|
1039
1165
|
return void 0;
|
|
@@ -1066,8 +1192,8 @@ ${exports.join("\n")}
|
|
|
1066
1192
|
|
|
1067
1193
|
// src/commands/sync.ts
|
|
1068
1194
|
import * as p3 from "@clack/prompts";
|
|
1069
|
-
import
|
|
1070
|
-
import
|
|
1195
|
+
import fs6 from "fs";
|
|
1196
|
+
import path5 from "path";
|
|
1071
1197
|
import pc3 from "picocolors";
|
|
1072
1198
|
async function syncCommand(options) {
|
|
1073
1199
|
console.log();
|
|
@@ -1119,7 +1245,7 @@ async function syncCommand(options) {
|
|
|
1119
1245
|
if (generatedMethods.length === 0) {
|
|
1120
1246
|
continue;
|
|
1121
1247
|
}
|
|
1122
|
-
const didFilePath =
|
|
1248
|
+
const didFilePath = path5.resolve(projectRoot, canisterConfig.didFile);
|
|
1123
1249
|
let methods;
|
|
1124
1250
|
try {
|
|
1125
1251
|
methods = parseDIDFile(didFilePath);
|
|
@@ -1144,17 +1270,17 @@ async function syncCommand(options) {
|
|
|
1144
1270
|
`${canisterName}: New methods available: ${pc3.cyan(newMethods.map((m) => m.name).join(", "))}`
|
|
1145
1271
|
);
|
|
1146
1272
|
}
|
|
1147
|
-
const canisterOutDir =
|
|
1148
|
-
const reactorPath =
|
|
1273
|
+
const canisterOutDir = path5.join(projectRoot, config.outDir, canisterName);
|
|
1274
|
+
const reactorPath = path5.join(canisterOutDir, "reactor.ts");
|
|
1149
1275
|
const reactorContent = generateReactorFile({
|
|
1150
1276
|
canisterName,
|
|
1151
1277
|
canisterConfig,
|
|
1152
1278
|
config,
|
|
1153
1279
|
outDir: canisterOutDir
|
|
1154
1280
|
});
|
|
1155
|
-
|
|
1281
|
+
fs6.writeFileSync(reactorPath, reactorContent);
|
|
1156
1282
|
totalUpdated++;
|
|
1157
|
-
const hooksOutDir =
|
|
1283
|
+
const hooksOutDir = path5.join(canisterOutDir, "hooks");
|
|
1158
1284
|
ensureDir(hooksOutDir);
|
|
1159
1285
|
for (const methodName of generatedMethods) {
|
|
1160
1286
|
const method = methods.find((m) => m.name === methodName);
|
|
@@ -1167,7 +1293,7 @@ async function syncCommand(options) {
|
|
|
1167
1293
|
const infiniteQueryFileName = getHookFileName(methodName, "infiniteQuery");
|
|
1168
1294
|
let content;
|
|
1169
1295
|
let fileName;
|
|
1170
|
-
if (
|
|
1296
|
+
if (fs6.existsSync(path5.join(hooksOutDir, infiniteQueryFileName))) {
|
|
1171
1297
|
fileName = infiniteQueryFileName;
|
|
1172
1298
|
totalSkipped++;
|
|
1173
1299
|
continue;
|
|
@@ -1186,7 +1312,7 @@ async function syncCommand(options) {
|
|
|
1186
1312
|
config
|
|
1187
1313
|
});
|
|
1188
1314
|
}
|
|
1189
|
-
|
|
1315
|
+
fs6.writeFileSync(path5.join(hooksOutDir, fileName), content);
|
|
1190
1316
|
totalUpdated++;
|
|
1191
1317
|
}
|
|
1192
1318
|
}
|
|
@@ -1209,7 +1335,7 @@ Skipped: ${pc3.dim(totalSkipped.toString())} files (preserved customizations)`,
|
|
|
1209
1335
|
|
|
1210
1336
|
// src/commands/list.ts
|
|
1211
1337
|
import * as p4 from "@clack/prompts";
|
|
1212
|
-
import
|
|
1338
|
+
import path6 from "path";
|
|
1213
1339
|
import pc4 from "picocolors";
|
|
1214
1340
|
async function listCommand(options) {
|
|
1215
1341
|
console.log();
|
|
@@ -1258,7 +1384,7 @@ async function listCommand(options) {
|
|
|
1258
1384
|
p4.log.error(`Canister ${pc4.yellow(selectedCanister)} not found in config.`);
|
|
1259
1385
|
process.exit(1);
|
|
1260
1386
|
}
|
|
1261
|
-
const didFilePath =
|
|
1387
|
+
const didFilePath = path6.resolve(projectRoot, canisterConfig.didFile);
|
|
1262
1388
|
try {
|
|
1263
1389
|
const methods = parseDIDFile(didFilePath);
|
|
1264
1390
|
if (methods.length === 0) {
|
|
@@ -1319,8 +1445,8 @@ ${error.message}`
|
|
|
1319
1445
|
|
|
1320
1446
|
// src/commands/fetch.ts
|
|
1321
1447
|
import * as p5 from "@clack/prompts";
|
|
1322
|
-
import
|
|
1323
|
-
import
|
|
1448
|
+
import fs7 from "fs";
|
|
1449
|
+
import path7 from "path";
|
|
1324
1450
|
import pc5 from "picocolors";
|
|
1325
1451
|
|
|
1326
1452
|
// src/utils/network.ts
|
|
@@ -1586,7 +1712,7 @@ async function fetchCommand(options) {
|
|
|
1586
1712
|
let configPath = findConfigFile();
|
|
1587
1713
|
let config;
|
|
1588
1714
|
if (!configPath) {
|
|
1589
|
-
configPath =
|
|
1715
|
+
configPath = path7.join(projectRoot, CONFIG_FILE_NAME);
|
|
1590
1716
|
config = { ...DEFAULT_CONFIG };
|
|
1591
1717
|
p5.log.info(`Creating ${pc5.yellow(CONFIG_FILE_NAME)}`);
|
|
1592
1718
|
} else {
|
|
@@ -1599,28 +1725,44 @@ async function fetchCommand(options) {
|
|
|
1599
1725
|
canisterId
|
|
1600
1726
|
};
|
|
1601
1727
|
config.canisters[canisterName] = canisterConfig;
|
|
1602
|
-
const canisterOutDir =
|
|
1603
|
-
const hooksOutDir =
|
|
1604
|
-
const candidDir =
|
|
1728
|
+
const canisterOutDir = path7.join(projectRoot, config.outDir, canisterName);
|
|
1729
|
+
const hooksOutDir = path7.join(canisterOutDir, "hooks");
|
|
1730
|
+
const candidDir = path7.join(projectRoot, "candid");
|
|
1605
1731
|
ensureDir(hooksOutDir);
|
|
1606
1732
|
ensureDir(candidDir);
|
|
1607
1733
|
const genSpinner = p5.spinner();
|
|
1608
1734
|
genSpinner.start("Generating hooks...");
|
|
1609
1735
|
const generatedFiles = [];
|
|
1610
|
-
const candidPath =
|
|
1611
|
-
|
|
1736
|
+
const candidPath = path7.join(candidDir, `${canisterName}.did`);
|
|
1737
|
+
fs7.writeFileSync(candidPath, candidSource);
|
|
1612
1738
|
generatedFiles.push(`candid/${canisterName}.did`);
|
|
1613
|
-
|
|
1739
|
+
genSpinner.message("Generating TypeScript declarations...");
|
|
1740
|
+
const bindgenResult = await generateDeclarations({
|
|
1741
|
+
didFile: candidPath,
|
|
1742
|
+
outDir: canisterOutDir,
|
|
1743
|
+
canisterName
|
|
1744
|
+
});
|
|
1745
|
+
if (bindgenResult.success) {
|
|
1746
|
+
generatedFiles.push("declarations/");
|
|
1747
|
+
} else {
|
|
1748
|
+
p5.log.warn(`Could not generate declarations: ${bindgenResult.error}`);
|
|
1749
|
+
p5.log.info(
|
|
1750
|
+
`You can manually run: npx @icp-sdk/bindgen --input ${candidPath} --output ${canisterOutDir}/declarations`
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
genSpinner.message("Generating reactor...");
|
|
1754
|
+
const reactorPath = path7.join(canisterOutDir, "reactor.ts");
|
|
1614
1755
|
const reactorContent = generateReactorFileForFetch({
|
|
1615
1756
|
canisterName,
|
|
1616
1757
|
canisterConfig,
|
|
1617
|
-
canisterId
|
|
1758
|
+
canisterId,
|
|
1759
|
+
hasDeclarations: bindgenResult.success
|
|
1618
1760
|
});
|
|
1619
|
-
|
|
1761
|
+
fs7.writeFileSync(reactorPath, reactorContent);
|
|
1620
1762
|
generatedFiles.push("reactor.ts");
|
|
1621
1763
|
for (const { method, hookType } of methodsWithHookTypes) {
|
|
1622
1764
|
const fileName = getHookFileName(method.name, hookType);
|
|
1623
|
-
const filePath =
|
|
1765
|
+
const filePath = path7.join(hooksOutDir, fileName);
|
|
1624
1766
|
let content;
|
|
1625
1767
|
switch (hookType) {
|
|
1626
1768
|
case "query":
|
|
@@ -1647,12 +1789,12 @@ async function fetchCommand(options) {
|
|
|
1647
1789
|
});
|
|
1648
1790
|
break;
|
|
1649
1791
|
}
|
|
1650
|
-
|
|
1651
|
-
generatedFiles.push(
|
|
1792
|
+
fs7.writeFileSync(filePath, content);
|
|
1793
|
+
generatedFiles.push(path7.join("hooks", fileName));
|
|
1652
1794
|
}
|
|
1653
|
-
const indexPath =
|
|
1795
|
+
const indexPath = path7.join(hooksOutDir, "index.ts");
|
|
1654
1796
|
const indexContent = generateIndexFile2(methodsWithHookTypes);
|
|
1655
|
-
|
|
1797
|
+
fs7.writeFileSync(indexPath, indexContent);
|
|
1656
1798
|
generatedFiles.push("hooks/index.ts");
|
|
1657
1799
|
config.generatedHooks[canisterName] = [
|
|
1658
1800
|
.../* @__PURE__ */ new Set([
|
|
@@ -1665,7 +1807,7 @@ async function fetchCommand(options) {
|
|
|
1665
1807
|
console.log();
|
|
1666
1808
|
p5.note(
|
|
1667
1809
|
generatedFiles.map((f) => pc5.green(`\u2713 ${f}`)).join("\n"),
|
|
1668
|
-
`Generated in ${pc5.dim(
|
|
1810
|
+
`Generated in ${pc5.dim(path7.relative(projectRoot, canisterOutDir))}`
|
|
1669
1811
|
);
|
|
1670
1812
|
console.log();
|
|
1671
1813
|
p5.note(
|
|
@@ -1678,13 +1820,14 @@ Methods: ${pc5.dim(selectedMethods.map((m) => m.name).join(", "))}`,
|
|
|
1678
1820
|
p5.outro(pc5.green("\u2713 Done!"));
|
|
1679
1821
|
}
|
|
1680
1822
|
function generateReactorFileForFetch(options) {
|
|
1681
|
-
const { canisterName, canisterConfig, canisterId } = options;
|
|
1823
|
+
const { canisterName, canisterConfig, canisterId, hasDeclarations } = options;
|
|
1682
1824
|
const pascalName = canisterName.charAt(0).toUpperCase() + canisterName.slice(1);
|
|
1683
1825
|
const reactorName = `${canisterName}Reactor`;
|
|
1684
1826
|
const serviceName = `${pascalName}Service`;
|
|
1685
1827
|
const reactorType = canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor";
|
|
1686
1828
|
const clientManagerPath = canisterConfig.clientManagerPath ?? "../../lib/client";
|
|
1687
|
-
|
|
1829
|
+
if (hasDeclarations) {
|
|
1830
|
+
return `/**
|
|
1688
1831
|
* ${pascalName} Reactor
|
|
1689
1832
|
*
|
|
1690
1833
|
* Auto-generated by @ic-reactor/cli
|
|
@@ -1697,18 +1840,82 @@ import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/r
|
|
|
1697
1840
|
import { clientManager } from "${clientManagerPath}"
|
|
1698
1841
|
|
|
1699
1842
|
// Import generated declarations
|
|
1700
|
-
|
|
1701
|
-
// For now, we use a generic service type
|
|
1702
|
-
import { idlFactory } from "./declarations/${canisterName}.did"
|
|
1843
|
+
import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${canisterName}.did"
|
|
1703
1844
|
|
|
1704
1845
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1705
|
-
//
|
|
1846
|
+
// REACTOR INSTANCE
|
|
1847
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1848
|
+
|
|
1849
|
+
/**
|
|
1850
|
+
* ${pascalName} Reactor
|
|
1851
|
+
*
|
|
1852
|
+
* Canister ID: ${canisterId}
|
|
1853
|
+
*/
|
|
1854
|
+
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
1855
|
+
clientManager,
|
|
1856
|
+
idlFactory,
|
|
1857
|
+
canisterId: "${canisterId}",
|
|
1858
|
+
name: "${canisterName}",
|
|
1859
|
+
})
|
|
1860
|
+
|
|
1861
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1862
|
+
// BASE HOOKS
|
|
1706
1863
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1707
1864
|
|
|
1708
|
-
|
|
1709
|
-
|
|
1865
|
+
/**
|
|
1866
|
+
* Base actor hooks - use these directly or import method-specific hooks.
|
|
1867
|
+
*/
|
|
1868
|
+
export const {
|
|
1869
|
+
useActorQuery,
|
|
1870
|
+
useActorMutation,
|
|
1871
|
+
useActorSuspenseQuery,
|
|
1872
|
+
useActorInfiniteQuery,
|
|
1873
|
+
useActorSuspenseInfiniteQuery,
|
|
1874
|
+
useActorMethod,
|
|
1875
|
+
} = createActorHooks(${reactorName})
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* Auth hooks for the client manager.
|
|
1879
|
+
*/
|
|
1880
|
+
export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
|
|
1881
|
+
|
|
1882
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1883
|
+
// RE-EXPORTS
|
|
1884
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1885
|
+
|
|
1886
|
+
export { idlFactory }
|
|
1887
|
+
export type { ${serviceName} }
|
|
1888
|
+
`;
|
|
1889
|
+
}
|
|
1890
|
+
return `/**
|
|
1891
|
+
* ${pascalName} Reactor
|
|
1892
|
+
*
|
|
1893
|
+
* Auto-generated by @ic-reactor/cli
|
|
1894
|
+
* Fetched from canister: ${canisterId}
|
|
1895
|
+
*
|
|
1896
|
+
* You can customize this file to add global configuration.
|
|
1897
|
+
*/
|
|
1898
|
+
|
|
1899
|
+
import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
|
|
1900
|
+
import { clientManager } from "${clientManagerPath}"
|
|
1901
|
+
|
|
1902
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1903
|
+
// DECLARATIONS
|
|
1904
|
+
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1905
|
+
|
|
1906
|
+
// TODO: Generate proper types by running:
|
|
1907
|
+
// npx @icp-sdk/bindgen --input ./candid/${canisterName}.did --output ./${canisterName}/declarations
|
|
1908
|
+
|
|
1909
|
+
// For now, import just the IDL factory (you may need to create this manually)
|
|
1910
|
+
// import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${canisterName}.did"
|
|
1911
|
+
|
|
1912
|
+
// Fallback generic type - replace with generated types
|
|
1710
1913
|
type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
|
|
1711
1914
|
|
|
1915
|
+
// You'll need to define idlFactory here or import from declarations
|
|
1916
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1917
|
+
const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
|
|
1918
|
+
|
|
1712
1919
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1713
1920
|
// REACTOR INSTANCE
|
|
1714
1921
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ic-reactor/cli",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-dev3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI tool to generate shadcn-style React hooks for ICP canisters",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"homepage": "https://github.com/B3Pay/ic-reactor#readme",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@clack/prompts": "^0.9.1",
|
|
37
|
+
"@icp-sdk/bindgen": "^0.2.1",
|
|
37
38
|
"@icp-sdk/core": "^5.0.0",
|
|
38
39
|
"commander": "^13.1.0",
|
|
39
40
|
"picocolors": "^1.1.1"
|