atmx-cli 0.39.0 → 0.41.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/generators/sdk-generator.js +57 -17
- package/dist/index.js +12 -10
- package/package.json +1 -1
- package/src/generators/sdk-generator.ts +72 -18
- package/src/index.ts +87 -75
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateSdk = generateSdk;
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
|
-
function generateSdk(multiIr) {
|
|
5
|
+
function generateSdk(multiIr, isReact = false) {
|
|
6
6
|
const lines = [
|
|
7
7
|
`// GENERATED CODE – DO NOT EDIT.`,
|
|
8
8
|
`import * as models from './models';\n`,
|
|
9
9
|
];
|
|
10
|
+
if (isReact) {
|
|
11
|
+
lines.push(`import { useAxiomQuery, useAxiomMutation } from 'atmx-react';`);
|
|
12
|
+
lines.push(`import type { AxiomQueryDef } from 'atmx-react';\n`);
|
|
13
|
+
}
|
|
10
14
|
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
11
15
|
const pascalNs = (0, utils_1.pascalCase)(ns);
|
|
12
16
|
lines.push(`export class ${pascalNs}Module {`);
|
|
13
|
-
// Support both old array format and new object map format
|
|
14
17
|
const endpointsMap = ir.endpoints || {};
|
|
15
18
|
const endpoints = Array.isArray(endpointsMap)
|
|
16
19
|
? endpointsMap
|
|
17
20
|
: Object.values(endpointsMap);
|
|
18
21
|
endpoints.forEach((ep) => {
|
|
19
|
-
lines.push(generateEndpointMethod(ep, ns, pascalNs));
|
|
22
|
+
lines.push(generateEndpointMethod(ep, ns, pascalNs, isReact));
|
|
20
23
|
});
|
|
21
24
|
lines.push(`}\n`);
|
|
22
25
|
}
|
|
@@ -27,30 +30,67 @@ function generateSdk(multiIr) {
|
|
|
27
30
|
lines.push(`}\nexport const sdk = new AxiomSdk();`);
|
|
28
31
|
return lines.join("\n");
|
|
29
32
|
}
|
|
30
|
-
function generateEndpointMethod(ep, ns, pascalNs) {
|
|
31
|
-
// ✨ FIX: Ensure params is always an Array regardless of the IR structure
|
|
33
|
+
function generateEndpointMethod(ep, ns, pascalNs, isReact) {
|
|
32
34
|
const rawParams = ep.parameters || [];
|
|
33
35
|
const params = Array.isArray(rawParams)
|
|
34
36
|
? rawParams
|
|
35
37
|
: Object.values(rawParams);
|
|
36
|
-
|
|
38
|
+
const argType = params.length > 0
|
|
39
|
+
? `{ ${params.map((p) => `${(0, utils_1.camelCase)(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels((0, utils_1.mapTypeToTs)(p.typeRef, pascalNs))}`).join(", ")} }`
|
|
40
|
+
: "void";
|
|
41
|
+
const isQuery = ep.method ? ep.method.toUpperCase() === "GET" : true;
|
|
42
|
+
const rawReturnType = (0, utils_1.mapTypeToTs)(ep.returnType, pascalNs);
|
|
43
|
+
const returnType = rawReturnType === "void" || rawReturnType === "any"
|
|
44
|
+
? rawReturnType
|
|
45
|
+
: prefixModels(rawReturnType);
|
|
46
|
+
if (isReact) {
|
|
47
|
+
const bodyParam = params.find((p) => p.source === "body");
|
|
48
|
+
const payloadLogic = bodyParam
|
|
49
|
+
? `const payload = (args as any)?.${(0, utils_1.camelCase)(bodyParam.name)};`
|
|
50
|
+
: `const payload = undefined;`;
|
|
51
|
+
const decLogic = generateLambda(ep.returnType, "fromJson", pascalNs);
|
|
52
|
+
const serLogic = bodyParam
|
|
53
|
+
? generateLambda(bodyParam.typeRef, "toJson", pascalNs)
|
|
54
|
+
: `(p: any) => p`;
|
|
37
55
|
return `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
56
|
+
get${(0, utils_1.pascalCase)(ep.name)}Def(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): AxiomQueryDef<${returnType}> {
|
|
57
|
+
${payloadLogic}
|
|
58
|
+
return {
|
|
59
|
+
namespace: "${ns}",
|
|
60
|
+
name: "${ep.name}",
|
|
61
|
+
endpointId: ${ep.id},
|
|
62
|
+
method: "${ep.method ? ep.method.toUpperCase() : "GET"}",
|
|
63
|
+
path: "${ep.path}",
|
|
64
|
+
payload: payload,
|
|
65
|
+
args: args || {},
|
|
66
|
+
decoder: ${decLogic},
|
|
67
|
+
serializer: ${serLogic},
|
|
68
|
+
isStream: ${ep.isStream === true}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
use${(0, utils_1.pascalCase)(ep.name)}${!isQuery ? "Mutation" : ""}(${isQuery ? `args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}, options?: { enabled?: boolean }` : ""}) {
|
|
73
|
+
${isQuery
|
|
74
|
+
? `return useAxiomQuery<${returnType}>(this.get${(0, utils_1.pascalCase)(ep.name)}Def(args), options);`
|
|
75
|
+
: `return useAxiomMutation<${returnType}, ${argType === "void" ? "void | Record<string,any>" : argType}>((args) => this.get${(0, utils_1.pascalCase)(ep.name)}Def(args));`}
|
|
41
76
|
}\n`;
|
|
42
77
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
47
|
-
.join(", ")} }`;
|
|
48
|
-
return `
|
|
49
|
-
/** RPC String Generator for <AxQuery> or <AxMutate> */
|
|
50
|
-
${(0, utils_1.camelCase)(ep.name)}(args?: ${argType}): string {
|
|
78
|
+
else {
|
|
79
|
+
return `
|
|
80
|
+
${(0, utils_1.camelCase)(ep.name)}(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): string {
|
|
51
81
|
const argsStr = args && Object.keys(args).length > 0 ? JSON.stringify(args) : '';
|
|
52
82
|
return \`${ns}.${ep.name}(\${argsStr})\`;
|
|
53
83
|
}\n`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function generateLambda(typeRef, mode, ns) {
|
|
87
|
+
if (!typeRef || !typeRef.kind || typeRef.kind === "void")
|
|
88
|
+
return mode === "fromJson" ? `() => undefined` : `(p: any) => p`;
|
|
89
|
+
if (typeRef.kind === "list" && typeRef.value?.kind === "named")
|
|
90
|
+
return `(data: any[]) => data.map(models.Mappers.${(0, utils_1.camelCase)(ns)}.${(0, utils_1.pascalCase)(typeRef.value.value)}.${mode})`;
|
|
91
|
+
if (typeRef.kind === "named")
|
|
92
|
+
return `models.Mappers.${(0, utils_1.camelCase)(ns)}.${(0, utils_1.pascalCase)(typeRef.value)}.${mode}`;
|
|
93
|
+
return `(data: any) => data`;
|
|
54
94
|
}
|
|
55
95
|
function prefixModels(type) {
|
|
56
96
|
const primitives = [
|
package/dist/index.js
CHANGED
|
@@ -42,13 +42,14 @@ const sdk_generator_1 = require("./generators/sdk-generator");
|
|
|
42
42
|
const utils_1 = require("./generators/utils");
|
|
43
43
|
const program = new commander_1.Command();
|
|
44
44
|
program
|
|
45
|
-
.name(
|
|
46
|
-
.description(
|
|
47
|
-
.version(
|
|
45
|
+
.name("atmx")
|
|
46
|
+
.description("Generate TypeScript SDK from an ATMX Multi-Contract Config")
|
|
47
|
+
.version("0.2.0");
|
|
48
48
|
program
|
|
49
|
-
.command(
|
|
50
|
-
.requiredOption(
|
|
51
|
-
.requiredOption(
|
|
49
|
+
.command("generate")
|
|
50
|
+
.requiredOption("-c, --config <path>", "Path to the atmx.config.json file")
|
|
51
|
+
.requiredOption("-o, --output <dir>", "Output directory for generated files")
|
|
52
|
+
.option("-r, --react", "Generate React Hooks instead of Vanilla JS strings") // ✨ NEW
|
|
52
53
|
.action(async (options) => {
|
|
53
54
|
const configPath = path.resolve(options.config);
|
|
54
55
|
const outputDir = path.resolve(options.output);
|
|
@@ -88,11 +89,12 @@ program
|
|
|
88
89
|
}
|
|
89
90
|
// Ensure output directory exists
|
|
90
91
|
await fs.ensureDir(outputDir);
|
|
91
|
-
// 3. Pass the MultiIR Map to the generators
|
|
92
|
+
// 3. Pass the MultiIR Map to the generators
|
|
92
93
|
const modelsContent = (0, model_generator_1.generateModels)(multiIr);
|
|
93
|
-
await fs.writeFile(path.join(outputDir,
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
await fs.writeFile(path.join(outputDir, "models.ts"), modelsContent);
|
|
95
|
+
// ✨ NEW: Pass the react flag down
|
|
96
|
+
const sdkContent = (0, sdk_generator_1.generateSdk)(multiIr, options.react);
|
|
97
|
+
await fs.writeFile(path.join(outputDir, "sdk.ts"), sdkContent);
|
|
96
98
|
console.log(`\n🎉 ATMX Multi-Contract SDK generated successfully in ${outputDir}`);
|
|
97
99
|
});
|
|
98
100
|
program.parse();
|
package/package.json
CHANGED
|
@@ -2,24 +2,31 @@
|
|
|
2
2
|
import { AxiomEndpoint, MultiIR } from "../types";
|
|
3
3
|
import { pascalCase, camelCase, mapTypeToTs } from "./utils";
|
|
4
4
|
|
|
5
|
-
export function generateSdk(
|
|
5
|
+
export function generateSdk(
|
|
6
|
+
multiIr: MultiIR,
|
|
7
|
+
isReact: boolean = false,
|
|
8
|
+
): string {
|
|
6
9
|
const lines: string[] = [
|
|
7
10
|
`// GENERATED CODE – DO NOT EDIT.`,
|
|
8
11
|
`import * as models from './models';\n`,
|
|
9
12
|
];
|
|
10
13
|
|
|
14
|
+
if (isReact) {
|
|
15
|
+
lines.push(`import { useAxiomQuery, useAxiomMutation } from 'atmx-react';`);
|
|
16
|
+
lines.push(`import type { AxiomQueryDef } from 'atmx-react';\n`);
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
for (const [ns, ir] of Object.entries(multiIr)) {
|
|
12
20
|
const pascalNs = pascalCase(ns);
|
|
13
21
|
lines.push(`export class ${pascalNs}Module {`);
|
|
14
22
|
|
|
15
|
-
// Support both old array format and new object map format
|
|
16
23
|
const endpointsMap = ir.endpoints || {};
|
|
17
24
|
const endpoints = Array.isArray(endpointsMap)
|
|
18
25
|
? endpointsMap
|
|
19
26
|
: Object.values(endpointsMap);
|
|
20
27
|
|
|
21
28
|
endpoints.forEach((ep: any) => {
|
|
22
|
-
lines.push(generateEndpointMethod(ep, ns, pascalNs));
|
|
29
|
+
lines.push(generateEndpointMethod(ep, ns, pascalNs, isReact));
|
|
23
30
|
});
|
|
24
31
|
lines.push(`}\n`);
|
|
25
32
|
}
|
|
@@ -39,33 +46,80 @@ function generateEndpointMethod(
|
|
|
39
46
|
ep: AxiomEndpoint,
|
|
40
47
|
ns: string,
|
|
41
48
|
pascalNs: string,
|
|
49
|
+
isReact: boolean,
|
|
42
50
|
): string {
|
|
43
|
-
// ✨ FIX: Ensure params is always an Array regardless of the IR structure
|
|
44
51
|
const rawParams = ep.parameters || [];
|
|
45
52
|
const params = Array.isArray(rawParams)
|
|
46
53
|
? rawParams
|
|
47
54
|
: Object.values(rawParams);
|
|
48
55
|
|
|
49
|
-
|
|
56
|
+
const argType =
|
|
57
|
+
params.length > 0
|
|
58
|
+
? `{ ${params.map((p: any) => `${camelCase(p.name)}${p.isOptional ? "?" : ""}: ${prefixModels(mapTypeToTs(p.typeRef, pascalNs))}`).join(", ")} }`
|
|
59
|
+
: "void";
|
|
60
|
+
|
|
61
|
+
const isQuery = ep.method ? ep.method.toUpperCase() === "GET" : true;
|
|
62
|
+
const rawReturnType = mapTypeToTs(ep.returnType, pascalNs);
|
|
63
|
+
const returnType =
|
|
64
|
+
rawReturnType === "void" || rawReturnType === "any"
|
|
65
|
+
? rawReturnType
|
|
66
|
+
: prefixModels(rawReturnType);
|
|
67
|
+
|
|
68
|
+
if (isReact) {
|
|
69
|
+
const bodyParam = params.find((p: any) => p.source === "body") as any;
|
|
70
|
+
const payloadLogic = bodyParam
|
|
71
|
+
? `const payload = (args as any)?.${camelCase(bodyParam.name)};`
|
|
72
|
+
: `const payload = undefined;`;
|
|
73
|
+
const decLogic = generateLambda(ep.returnType, "fromJson", pascalNs);
|
|
74
|
+
const serLogic = bodyParam
|
|
75
|
+
? generateLambda(bodyParam.typeRef, "toJson", pascalNs)
|
|
76
|
+
: `(p: any) => p`;
|
|
77
|
+
|
|
50
78
|
return `
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return
|
|
54
|
-
|
|
79
|
+
get${pascalCase(ep.name)}Def(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): AxiomQueryDef<${returnType}> {
|
|
80
|
+
${payloadLogic}
|
|
81
|
+
return {
|
|
82
|
+
namespace: "${ns}",
|
|
83
|
+
name: "${ep.name}",
|
|
84
|
+
endpointId: ${ep.id},
|
|
85
|
+
method: "${ep.method ? ep.method.toUpperCase() : "GET"}",
|
|
86
|
+
path: "${ep.path}",
|
|
87
|
+
payload: payload,
|
|
88
|
+
args: args || {},
|
|
89
|
+
decoder: ${decLogic},
|
|
90
|
+
serializer: ${serLogic},
|
|
91
|
+
isStream: ${ep.isStream === true}
|
|
92
|
+
};
|
|
55
93
|
}
|
|
56
94
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
95
|
+
use${pascalCase(ep.name)}${!isQuery ? "Mutation" : ""}(${isQuery ? `args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}, options?: { enabled?: boolean }` : ""}) {
|
|
96
|
+
${
|
|
97
|
+
isQuery
|
|
98
|
+
? `return useAxiomQuery<${returnType}>(this.get${pascalCase(ep.name)}Def(args), options);`
|
|
99
|
+
: `return useAxiomMutation<${returnType}, ${argType === "void" ? "void | Record<string,any>" : argType}>((args) => this.get${pascalCase(ep.name)}Def(args));`
|
|
100
|
+
}
|
|
101
|
+
}\n`;
|
|
102
|
+
} else {
|
|
103
|
+
return `
|
|
104
|
+
${camelCase(ep.name)}(args${params.length > 0 ? "?" : ""}: ${argType === "void" ? "any" : argType}): string {
|
|
66
105
|
const argsStr = args && Object.keys(args).length > 0 ? JSON.stringify(args) : '';
|
|
67
106
|
return \`${ns}.${ep.name}(\${argsStr})\`;
|
|
68
107
|
}\n`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function generateLambda(
|
|
112
|
+
typeRef: any,
|
|
113
|
+
mode: "fromJson" | "toJson",
|
|
114
|
+
ns: string,
|
|
115
|
+
): string {
|
|
116
|
+
if (!typeRef || !typeRef.kind || typeRef.kind === "void")
|
|
117
|
+
return mode === "fromJson" ? `() => undefined` : `(p: any) => p`;
|
|
118
|
+
if (typeRef.kind === "list" && typeRef.value?.kind === "named")
|
|
119
|
+
return `(data: any[]) => data.map(models.Mappers.${camelCase(ns)}.${pascalCase(typeRef.value.value)}.${mode})`;
|
|
120
|
+
if (typeRef.kind === "named")
|
|
121
|
+
return `models.Mappers.${camelCase(ns)}.${pascalCase(typeRef.value)}.${mode}`;
|
|
122
|
+
return `(data: any) => data`;
|
|
69
123
|
}
|
|
70
124
|
|
|
71
125
|
function prefixModels(type: string): string {
|
package/src/index.ts
CHANGED
|
@@ -1,82 +1,94 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from
|
|
3
|
-
import * as fs from
|
|
4
|
-
import * as path from
|
|
5
|
-
import { AtmxMultiConfig, MultiIR } from
|
|
6
|
-
import { generateModels } from
|
|
7
|
-
import { generateSdk } from
|
|
8
|
-
import { normalizeIr } from
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import * as fs from "fs-extra";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { AtmxMultiConfig, MultiIR } from "./types";
|
|
6
|
+
import { generateModels } from "./generators/model-generator";
|
|
7
|
+
import { generateSdk } from "./generators/sdk-generator";
|
|
8
|
+
import { normalizeIr } from "./generators/utils";
|
|
9
9
|
|
|
10
10
|
const program = new Command();
|
|
11
11
|
|
|
12
12
|
program
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
.name("atmx")
|
|
14
|
+
.description("Generate TypeScript SDK from an ATMX Multi-Contract Config")
|
|
15
|
+
.version("0.2.0");
|
|
16
16
|
|
|
17
17
|
program
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
18
|
+
.command("generate")
|
|
19
|
+
.requiredOption("-c, --config <path>", "Path to the atmx.config.json file")
|
|
20
|
+
.requiredOption("-o, --output <dir>", "Output directory for generated files")
|
|
21
|
+
.option("-r, --react", "Generate React Hooks instead of Vanilla JS strings") // ✨ NEW
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
const configPath = path.resolve(options.config);
|
|
24
|
+
const outputDir = path.resolve(options.output);
|
|
25
|
+
|
|
26
|
+
if (!fs.existsSync(configPath)) {
|
|
27
|
+
console.error(`❌ Error: Config file not found at ${configPath}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 1. Read the Config File
|
|
32
|
+
const rawConfig: AtmxMultiConfig = await fs.readJSON(configPath);
|
|
33
|
+
|
|
34
|
+
if (!rawConfig.contracts || Object.keys(rawConfig.contracts).length === 0) {
|
|
35
|
+
console.error(
|
|
36
|
+
"❌ Error: Invalid config file. Missing 'contracts' dictionary.",
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const configDir = path.dirname(configPath);
|
|
42
|
+
const multiIr: MultiIR = {};
|
|
43
|
+
|
|
44
|
+
// 2. Loop through the contracts and parse the local .axiom files
|
|
45
|
+
for (const [namespace, contract] of Object.entries(rawConfig.contracts)) {
|
|
46
|
+
// Resolve the .axiom file path relative to where the config file is located
|
|
47
|
+
// (e.g., if config is in /public, and file is "./auth.axiom", it looks in /public/auth.axiom)
|
|
48
|
+
const axiomFilePath = path.resolve(configDir, contract.file);
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(axiomFilePath)) {
|
|
51
|
+
console.warn(
|
|
52
|
+
`⚠️ Warning: Contract file not found for namespace '${namespace}' at ${axiomFilePath}. Skipping...`,
|
|
53
|
+
);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const rawFile = await fs.readJSON(axiomFilePath);
|
|
58
|
+
|
|
59
|
+
if (!rawFile.ir) {
|
|
60
|
+
console.warn(
|
|
61
|
+
`⚠️ Warning: Invalid .axiom file for namespace '${namespace}'. Missing 'ir' property. Skipping...`,
|
|
62
|
+
);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Normalize the IR (snake_case -> camelCase) and store it by namespace
|
|
67
|
+
multiIr[namespace] = normalizeIr(rawFile.ir);
|
|
68
|
+
console.log(`✅ Loaded contract: [${namespace}] -> ${contract.file}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (Object.keys(multiIr).length === 0) {
|
|
72
|
+
console.error(
|
|
73
|
+
"❌ Error: No valid contracts were loaded. Aborting generation.",
|
|
74
|
+
);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Ensure output directory exists
|
|
79
|
+
await fs.ensureDir(outputDir);
|
|
80
|
+
|
|
81
|
+
// 3. Pass the MultiIR Map to the generators
|
|
82
|
+
const modelsContent = generateModels(multiIr);
|
|
83
|
+
await fs.writeFile(path.join(outputDir, "models.ts"), modelsContent);
|
|
84
|
+
|
|
85
|
+
// ✨ NEW: Pass the react flag down
|
|
86
|
+
const sdkContent = generateSdk(multiIr, options.react);
|
|
87
|
+
await fs.writeFile(path.join(outputDir, "sdk.ts"), sdkContent);
|
|
88
|
+
|
|
89
|
+
console.log(
|
|
90
|
+
`\n🎉 ATMX Multi-Contract SDK generated successfully in ${outputDir}`,
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
program.parse();
|