@ic-reactor/codegen 0.7.0 → 0.7.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/README.md +1 -14
- package/dist/index.cjs +6 -42
- package/dist/index.d.cts +3 -15
- package/dist/index.d.ts +3 -15
- package/dist/index.js +6 -42
- package/package.json +1 -1
- package/src/generators/reactor.ts +4 -31
- package/src/index.ts +1 -6
- package/src/pipeline.ts +5 -33
- package/src/types.ts +0 -12
- package/src/__snapshots__/reactor.test.ts.snap +0 -94
- package/src/pipeline.test.ts +0 -163
- package/src/reactor.test.ts +0 -45
package/README.md
CHANGED
|
@@ -17,7 +17,6 @@ import { runCanisterPipeline } from "@ic-reactor/codegen"
|
|
|
17
17
|
await runCanisterPipeline({
|
|
18
18
|
canisterConfig: {
|
|
19
19
|
name: "backend",
|
|
20
|
-
mode: "DisplayReactor",
|
|
21
20
|
didFile: "./backend.did",
|
|
22
21
|
},
|
|
23
22
|
projectRoot: process.cwd(),
|
|
@@ -28,24 +27,12 @@ await runCanisterPipeline({
|
|
|
28
27
|
})
|
|
29
28
|
```
|
|
30
29
|
|
|
31
|
-
## Reactor Class Configuration
|
|
32
|
-
|
|
33
|
-
Set `canisterConfig.mode` to choose the generated reactor class:
|
|
34
|
-
|
|
35
|
-
- `DisplayReactor` (default)
|
|
36
|
-
- `Reactor`
|
|
37
|
-
- `CandidReactor`
|
|
38
|
-
- `CandidDisplayReactor`
|
|
39
|
-
- `MetadataDisplayReactor`
|
|
40
|
-
|
|
41
|
-
Codegen writes a single `index.ts`. It overwrites that file only while it is still recognized as a generated file; if you replace it with your own file, regeneration leaves it untouched.
|
|
42
|
-
|
|
43
30
|
## Generators
|
|
44
31
|
|
|
45
32
|
You can also use individual generators if you need more granular control:
|
|
46
33
|
|
|
47
34
|
- **`generateDeclarations`**: Generates `.js` (factory), `.d.ts` (types), and `.did` copy.
|
|
48
|
-
- **`generateReactorFile`**: Generates the `index.ts` file
|
|
35
|
+
- **`generateReactorFile`**: Generates the `index.ts` file with `DisplayReactor` and hooks.
|
|
49
36
|
- **`generateClientFile`**: Generates a `ClientManager` boilerplate file.
|
|
50
37
|
|
|
51
38
|
## Utilities
|
package/dist/index.cjs
CHANGED
|
@@ -123,32 +123,14 @@ function getServiceTypeName(canisterName) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// src/generators/reactor.ts
|
|
126
|
-
function getReactorClassImportSource(reactorClass) {
|
|
127
|
-
switch (reactorClass) {
|
|
128
|
-
case "Reactor":
|
|
129
|
-
case "DisplayReactor":
|
|
130
|
-
return "@ic-reactor/react";
|
|
131
|
-
case "CandidReactor":
|
|
132
|
-
case "CandidDisplayReactor":
|
|
133
|
-
case "MetadataDisplayReactor":
|
|
134
|
-
return "@ic-reactor/candid";
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
126
|
function generateReactorFile(options) {
|
|
138
|
-
const {
|
|
139
|
-
canisterName,
|
|
140
|
-
didFile,
|
|
141
|
-
clientManagerPath = "../../clients",
|
|
142
|
-
reactorClass = "DisplayReactor"
|
|
143
|
-
} = options;
|
|
127
|
+
const { canisterName, didFile, clientManagerPath = "../../clients" } = options;
|
|
144
128
|
const pascalName = toPascalCase(canisterName);
|
|
145
129
|
const reactorName = getReactorName(canisterName);
|
|
146
130
|
const serviceName = getServiceTypeName(canisterName);
|
|
147
131
|
const baseName = import_node_path2.default.basename(didFile, ".did");
|
|
148
132
|
const declarationsPath = `./declarations/${baseName}`;
|
|
149
|
-
|
|
150
|
-
return `import { createActorHooks } from "@ic-reactor/react"
|
|
151
|
-
import { ${reactorClass} } from "${reactorImportSource}"
|
|
133
|
+
return `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
|
|
152
134
|
import { clientManager } from "${clientManagerPath}"
|
|
153
135
|
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
154
136
|
|
|
@@ -160,7 +142,7 @@ export type ${serviceName} = _SERVICE
|
|
|
160
142
|
* Auto-generated by @ic-reactor/codegen \u2014 do not edit.
|
|
161
143
|
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
162
144
|
*/
|
|
163
|
-
export const ${reactorName} = new
|
|
145
|
+
export const ${reactorName} = new DisplayReactor<${serviceName}>({
|
|
164
146
|
clientManager,
|
|
165
147
|
idlFactory,
|
|
166
148
|
name: "${canisterName}",
|
|
@@ -178,12 +160,6 @@ export const {
|
|
|
178
160
|
}
|
|
179
161
|
|
|
180
162
|
// src/pipeline.ts
|
|
181
|
-
function resolveReactorClass(canisterConfig) {
|
|
182
|
-
return canisterConfig.mode ?? "DisplayReactor";
|
|
183
|
-
}
|
|
184
|
-
function isGeneratedIndexFile(content) {
|
|
185
|
-
return content.includes("Auto-generated by @ic-reactor/codegen") && content.includes("createActorHooks(");
|
|
186
|
-
}
|
|
187
163
|
async function runCanisterPipeline(options) {
|
|
188
164
|
const { canisterConfig, projectRoot, globalConfig } = options;
|
|
189
165
|
const { name, didFile, clientManagerPath } = canisterConfig;
|
|
@@ -223,27 +199,15 @@ async function runCanisterPipeline(options) {
|
|
|
223
199
|
};
|
|
224
200
|
}
|
|
225
201
|
const reactorPath = import_node_path3.default.join(canisterOutDir, "index.ts");
|
|
226
|
-
const reactorClass = resolveReactorClass(canisterConfig);
|
|
227
202
|
try {
|
|
228
203
|
const reactorContent = generateReactorFile({
|
|
229
204
|
canisterName: name,
|
|
230
205
|
didFile: resolvedDidFile,
|
|
231
|
-
clientManagerPath: resolvedClientManagerPath
|
|
232
|
-
reactorClass
|
|
206
|
+
clientManagerPath: resolvedClientManagerPath
|
|
233
207
|
});
|
|
234
208
|
import_node_fs2.default.mkdirSync(canisterOutDir, { recursive: true });
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
files.push({ success: true, filePath: reactorPath });
|
|
238
|
-
} else {
|
|
239
|
-
const existingIndexContent = import_node_fs2.default.readFileSync(reactorPath, "utf-8");
|
|
240
|
-
if (isGeneratedIndexFile(existingIndexContent)) {
|
|
241
|
-
import_node_fs2.default.writeFileSync(reactorPath, reactorContent);
|
|
242
|
-
files.push({ success: true, filePath: reactorPath });
|
|
243
|
-
} else {
|
|
244
|
-
files.push({ success: true, filePath: reactorPath, skipped: true });
|
|
245
|
-
}
|
|
246
|
-
}
|
|
209
|
+
import_node_fs2.default.writeFileSync(reactorPath, reactorContent);
|
|
210
|
+
files.push({ success: true, filePath: reactorPath });
|
|
247
211
|
} catch (err) {
|
|
248
212
|
files.push({
|
|
249
213
|
success: false,
|
package/dist/index.d.cts
CHANGED
|
@@ -19,15 +19,9 @@ interface CanisterConfig {
|
|
|
19
19
|
* Example: "../../clients" → `import { clientManager } from "../../clients"`
|
|
20
20
|
*/
|
|
21
21
|
clientManagerPath?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Reactor class used for generated hooks in this canister.
|
|
24
|
-
* Defaults to DisplayReactor for backward compatibility.
|
|
25
|
-
*/
|
|
26
|
-
mode?: ReactorClassName;
|
|
27
22
|
/** Optional fixed canister ID */
|
|
28
23
|
canisterId?: string;
|
|
29
24
|
}
|
|
30
|
-
type ReactorClassName = "Reactor" | "DisplayReactor" | "CandidReactor" | "CandidDisplayReactor" | "MetadataDisplayReactor";
|
|
31
25
|
/**
|
|
32
26
|
* Top-level codegen / CLI configuration (stored in `ic-reactor.json`).
|
|
33
27
|
*/
|
|
@@ -68,7 +62,7 @@ interface GeneratorResult {
|
|
|
68
62
|
* Pipeline steps (in order):
|
|
69
63
|
* 1. Resolve paths (didFile, outDir)
|
|
70
64
|
* 2. Generate declarations (JS + .d.ts + .did copy)
|
|
71
|
-
* 3. Generate reactor file (
|
|
65
|
+
* 3. Generate reactor file (index.ts)
|
|
72
66
|
*/
|
|
73
67
|
|
|
74
68
|
interface PipelineOptions {
|
|
@@ -210,7 +204,6 @@ declare function declarationsExist(outDir: string, canisterName: string): boolea
|
|
|
210
204
|
* ...
|
|
211
205
|
* } = createActorHooks(backendReactor)
|
|
212
206
|
*/
|
|
213
|
-
|
|
214
207
|
interface ReactorGeneratorOptions {
|
|
215
208
|
/** Canister name (e.g. "backend") */
|
|
216
209
|
canisterName: string;
|
|
@@ -224,14 +217,9 @@ interface ReactorGeneratorOptions {
|
|
|
224
217
|
* Default: "../../clients"
|
|
225
218
|
*/
|
|
226
219
|
clientManagerPath?: string;
|
|
227
|
-
/**
|
|
228
|
-
* Which reactor class should back the generated hooks.
|
|
229
|
-
* Default: "DisplayReactor" (backward compatible)
|
|
230
|
-
*/
|
|
231
|
-
reactorClass?: ReactorClassName;
|
|
232
220
|
}
|
|
233
221
|
/**
|
|
234
|
-
* Generate the content of a canister's `index.ts` file.
|
|
222
|
+
* Generate the content of a canister's `index.ts` reactor file.
|
|
235
223
|
*/
|
|
236
224
|
declare function generateReactorFile(options: ReactorGeneratorOptions): string;
|
|
237
225
|
|
|
@@ -258,4 +246,4 @@ interface ClientGeneratorOptions {
|
|
|
258
246
|
*/
|
|
259
247
|
declare function generateClientFile(options?: ClientGeneratorOptions): string;
|
|
260
248
|
|
|
261
|
-
export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type
|
|
249
|
+
export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type ReactorGeneratorOptions, declarationsExist, extractMethods, generateClientFile, generateDeclarations, generateReactorFile, getReactorName, getServiceTypeName, parseDIDFile, runCanisterPipeline, toPascalCase };
|
package/dist/index.d.ts
CHANGED
|
@@ -19,15 +19,9 @@ interface CanisterConfig {
|
|
|
19
19
|
* Example: "../../clients" → `import { clientManager } from "../../clients"`
|
|
20
20
|
*/
|
|
21
21
|
clientManagerPath?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Reactor class used for generated hooks in this canister.
|
|
24
|
-
* Defaults to DisplayReactor for backward compatibility.
|
|
25
|
-
*/
|
|
26
|
-
mode?: ReactorClassName;
|
|
27
22
|
/** Optional fixed canister ID */
|
|
28
23
|
canisterId?: string;
|
|
29
24
|
}
|
|
30
|
-
type ReactorClassName = "Reactor" | "DisplayReactor" | "CandidReactor" | "CandidDisplayReactor" | "MetadataDisplayReactor";
|
|
31
25
|
/**
|
|
32
26
|
* Top-level codegen / CLI configuration (stored in `ic-reactor.json`).
|
|
33
27
|
*/
|
|
@@ -68,7 +62,7 @@ interface GeneratorResult {
|
|
|
68
62
|
* Pipeline steps (in order):
|
|
69
63
|
* 1. Resolve paths (didFile, outDir)
|
|
70
64
|
* 2. Generate declarations (JS + .d.ts + .did copy)
|
|
71
|
-
* 3. Generate reactor file (
|
|
65
|
+
* 3. Generate reactor file (index.ts)
|
|
72
66
|
*/
|
|
73
67
|
|
|
74
68
|
interface PipelineOptions {
|
|
@@ -210,7 +204,6 @@ declare function declarationsExist(outDir: string, canisterName: string): boolea
|
|
|
210
204
|
* ...
|
|
211
205
|
* } = createActorHooks(backendReactor)
|
|
212
206
|
*/
|
|
213
|
-
|
|
214
207
|
interface ReactorGeneratorOptions {
|
|
215
208
|
/** Canister name (e.g. "backend") */
|
|
216
209
|
canisterName: string;
|
|
@@ -224,14 +217,9 @@ interface ReactorGeneratorOptions {
|
|
|
224
217
|
* Default: "../../clients"
|
|
225
218
|
*/
|
|
226
219
|
clientManagerPath?: string;
|
|
227
|
-
/**
|
|
228
|
-
* Which reactor class should back the generated hooks.
|
|
229
|
-
* Default: "DisplayReactor" (backward compatible)
|
|
230
|
-
*/
|
|
231
|
-
reactorClass?: ReactorClassName;
|
|
232
220
|
}
|
|
233
221
|
/**
|
|
234
|
-
* Generate the content of a canister's `index.ts` file.
|
|
222
|
+
* Generate the content of a canister's `index.ts` reactor file.
|
|
235
223
|
*/
|
|
236
224
|
declare function generateReactorFile(options: ReactorGeneratorOptions): string;
|
|
237
225
|
|
|
@@ -258,4 +246,4 @@ interface ClientGeneratorOptions {
|
|
|
258
246
|
*/
|
|
259
247
|
declare function generateClientFile(options?: ClientGeneratorOptions): string;
|
|
260
248
|
|
|
261
|
-
export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type
|
|
249
|
+
export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type ReactorGeneratorOptions, declarationsExist, extractMethods, generateClientFile, generateDeclarations, generateReactorFile, getReactorName, getServiceTypeName, parseDIDFile, runCanisterPipeline, toPascalCase };
|
package/dist/index.js
CHANGED
|
@@ -78,32 +78,14 @@ function getServiceTypeName(canisterName) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// src/generators/reactor.ts
|
|
81
|
-
function getReactorClassImportSource(reactorClass) {
|
|
82
|
-
switch (reactorClass) {
|
|
83
|
-
case "Reactor":
|
|
84
|
-
case "DisplayReactor":
|
|
85
|
-
return "@ic-reactor/react";
|
|
86
|
-
case "CandidReactor":
|
|
87
|
-
case "CandidDisplayReactor":
|
|
88
|
-
case "MetadataDisplayReactor":
|
|
89
|
-
return "@ic-reactor/candid";
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
81
|
function generateReactorFile(options) {
|
|
93
|
-
const {
|
|
94
|
-
canisterName,
|
|
95
|
-
didFile,
|
|
96
|
-
clientManagerPath = "../../clients",
|
|
97
|
-
reactorClass = "DisplayReactor"
|
|
98
|
-
} = options;
|
|
82
|
+
const { canisterName, didFile, clientManagerPath = "../../clients" } = options;
|
|
99
83
|
const pascalName = toPascalCase(canisterName);
|
|
100
84
|
const reactorName = getReactorName(canisterName);
|
|
101
85
|
const serviceName = getServiceTypeName(canisterName);
|
|
102
86
|
const baseName = path2.basename(didFile, ".did");
|
|
103
87
|
const declarationsPath = `./declarations/${baseName}`;
|
|
104
|
-
|
|
105
|
-
return `import { createActorHooks } from "@ic-reactor/react"
|
|
106
|
-
import { ${reactorClass} } from "${reactorImportSource}"
|
|
88
|
+
return `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
|
|
107
89
|
import { clientManager } from "${clientManagerPath}"
|
|
108
90
|
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
109
91
|
|
|
@@ -115,7 +97,7 @@ export type ${serviceName} = _SERVICE
|
|
|
115
97
|
* Auto-generated by @ic-reactor/codegen \u2014 do not edit.
|
|
116
98
|
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
117
99
|
*/
|
|
118
|
-
export const ${reactorName} = new
|
|
100
|
+
export const ${reactorName} = new DisplayReactor<${serviceName}>({
|
|
119
101
|
clientManager,
|
|
120
102
|
idlFactory,
|
|
121
103
|
name: "${canisterName}",
|
|
@@ -133,12 +115,6 @@ export const {
|
|
|
133
115
|
}
|
|
134
116
|
|
|
135
117
|
// src/pipeline.ts
|
|
136
|
-
function resolveReactorClass(canisterConfig) {
|
|
137
|
-
return canisterConfig.mode ?? "DisplayReactor";
|
|
138
|
-
}
|
|
139
|
-
function isGeneratedIndexFile(content) {
|
|
140
|
-
return content.includes("Auto-generated by @ic-reactor/codegen") && content.includes("createActorHooks(");
|
|
141
|
-
}
|
|
142
118
|
async function runCanisterPipeline(options) {
|
|
143
119
|
const { canisterConfig, projectRoot, globalConfig } = options;
|
|
144
120
|
const { name, didFile, clientManagerPath } = canisterConfig;
|
|
@@ -178,27 +154,15 @@ async function runCanisterPipeline(options) {
|
|
|
178
154
|
};
|
|
179
155
|
}
|
|
180
156
|
const reactorPath = path3.join(canisterOutDir, "index.ts");
|
|
181
|
-
const reactorClass = resolveReactorClass(canisterConfig);
|
|
182
157
|
try {
|
|
183
158
|
const reactorContent = generateReactorFile({
|
|
184
159
|
canisterName: name,
|
|
185
160
|
didFile: resolvedDidFile,
|
|
186
|
-
clientManagerPath: resolvedClientManagerPath
|
|
187
|
-
reactorClass
|
|
161
|
+
clientManagerPath: resolvedClientManagerPath
|
|
188
162
|
});
|
|
189
163
|
fs2.mkdirSync(canisterOutDir, { recursive: true });
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
files.push({ success: true, filePath: reactorPath });
|
|
193
|
-
} else {
|
|
194
|
-
const existingIndexContent = fs2.readFileSync(reactorPath, "utf-8");
|
|
195
|
-
if (isGeneratedIndexFile(existingIndexContent)) {
|
|
196
|
-
fs2.writeFileSync(reactorPath, reactorContent);
|
|
197
|
-
files.push({ success: true, filePath: reactorPath });
|
|
198
|
-
} else {
|
|
199
|
-
files.push({ success: true, filePath: reactorPath, skipped: true });
|
|
200
|
-
}
|
|
201
|
-
}
|
|
164
|
+
fs2.writeFileSync(reactorPath, reactorContent);
|
|
165
|
+
files.push({ success: true, filePath: reactorPath });
|
|
202
166
|
} catch (err) {
|
|
203
167
|
files.push({
|
|
204
168
|
success: false,
|
package/package.json
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
|
|
23
23
|
import path from "node:path"
|
|
24
24
|
import { toPascalCase, getReactorName, getServiceTypeName } from "../naming.js"
|
|
25
|
-
import type { ReactorClassName } from "../types.js"
|
|
26
25
|
|
|
27
26
|
export interface ReactorGeneratorOptions {
|
|
28
27
|
/** Canister name (e.g. "backend") */
|
|
@@ -37,37 +36,13 @@ export interface ReactorGeneratorOptions {
|
|
|
37
36
|
* Default: "../../clients"
|
|
38
37
|
*/
|
|
39
38
|
clientManagerPath?: string
|
|
40
|
-
/**
|
|
41
|
-
* Which reactor class should back the generated hooks.
|
|
42
|
-
* Default: "DisplayReactor" (backward compatible)
|
|
43
|
-
*/
|
|
44
|
-
reactorClass?: ReactorClassName
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getReactorClassImportSource(
|
|
48
|
-
reactorClass: ReactorClassName
|
|
49
|
-
): "@ic-reactor/react" | "@ic-reactor/candid" {
|
|
50
|
-
switch (reactorClass) {
|
|
51
|
-
case "Reactor":
|
|
52
|
-
case "DisplayReactor":
|
|
53
|
-
return "@ic-reactor/react"
|
|
54
|
-
case "CandidReactor":
|
|
55
|
-
case "CandidDisplayReactor":
|
|
56
|
-
case "MetadataDisplayReactor":
|
|
57
|
-
return "@ic-reactor/candid"
|
|
58
|
-
}
|
|
59
39
|
}
|
|
60
40
|
|
|
61
41
|
/**
|
|
62
|
-
* Generate the content of a canister's `index.ts` file.
|
|
42
|
+
* Generate the content of a canister's `index.ts` reactor file.
|
|
63
43
|
*/
|
|
64
44
|
export function generateReactorFile(options: ReactorGeneratorOptions): string {
|
|
65
|
-
const {
|
|
66
|
-
canisterName,
|
|
67
|
-
didFile,
|
|
68
|
-
clientManagerPath = "../../clients",
|
|
69
|
-
reactorClass = "DisplayReactor",
|
|
70
|
-
} = options
|
|
45
|
+
const { canisterName, didFile, clientManagerPath = "../../clients" } = options
|
|
71
46
|
|
|
72
47
|
const pascalName = toPascalCase(canisterName)
|
|
73
48
|
const reactorName = getReactorName(canisterName)
|
|
@@ -76,10 +51,8 @@ export function generateReactorFile(options: ReactorGeneratorOptions): string {
|
|
|
76
51
|
// Derive the declarations import path from the .did filename
|
|
77
52
|
const baseName = path.basename(didFile, ".did")
|
|
78
53
|
const declarationsPath = `./declarations/${baseName}`
|
|
79
|
-
const reactorImportSource = getReactorClassImportSource(reactorClass)
|
|
80
54
|
|
|
81
|
-
return `import { createActorHooks } from "@ic-reactor/react"
|
|
82
|
-
import { ${reactorClass} } from "${reactorImportSource}"
|
|
55
|
+
return `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
|
|
83
56
|
import { clientManager } from "${clientManagerPath}"
|
|
84
57
|
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
85
58
|
|
|
@@ -91,7 +64,7 @@ export type ${serviceName} = _SERVICE
|
|
|
91
64
|
* Auto-generated by @ic-reactor/codegen — do not edit.
|
|
92
65
|
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
93
66
|
*/
|
|
94
|
-
export const ${reactorName} = new
|
|
67
|
+
export const ${reactorName} = new DisplayReactor<${serviceName}>({
|
|
95
68
|
clientManager,
|
|
96
69
|
idlFactory,
|
|
97
70
|
name: "${canisterName}",
|
package/src/index.ts
CHANGED
|
@@ -6,12 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// Core Types
|
|
9
|
-
export type {
|
|
10
|
-
CanisterConfig,
|
|
11
|
-
CodegenConfig,
|
|
12
|
-
GeneratorResult,
|
|
13
|
-
ReactorClassName,
|
|
14
|
-
} from "./types.js"
|
|
9
|
+
export type { CanisterConfig, CodegenConfig, GeneratorResult } from "./types.js"
|
|
15
10
|
|
|
16
11
|
// Pipeline (Primary Entry Point)
|
|
17
12
|
export { runCanisterPipeline } from "./pipeline.js"
|
package/src/pipeline.ts
CHANGED
|
@@ -7,17 +7,12 @@
|
|
|
7
7
|
* Pipeline steps (in order):
|
|
8
8
|
* 1. Resolve paths (didFile, outDir)
|
|
9
9
|
* 2. Generate declarations (JS + .d.ts + .did copy)
|
|
10
|
-
* 3. Generate reactor file (
|
|
10
|
+
* 3. Generate reactor file (index.ts)
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import fs from "node:fs"
|
|
14
14
|
import path from "node:path"
|
|
15
|
-
import type {
|
|
16
|
-
CanisterConfig,
|
|
17
|
-
CodegenConfig,
|
|
18
|
-
GeneratorResult,
|
|
19
|
-
ReactorClassName,
|
|
20
|
-
} from "./types.js"
|
|
15
|
+
import type { CanisterConfig, CodegenConfig, GeneratorResult } from "./types.js"
|
|
21
16
|
import { generateDeclarations } from "./generators/declarations.js"
|
|
22
17
|
import { generateReactorFile } from "./generators/reactor.js"
|
|
23
18
|
|
|
@@ -35,17 +30,6 @@ export interface PipelineOptions {
|
|
|
35
30
|
globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath">
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
function resolveReactorClass(canisterConfig: CanisterConfig): ReactorClassName {
|
|
39
|
-
return canisterConfig.mode ?? "DisplayReactor"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isGeneratedIndexFile(content: string): boolean {
|
|
43
|
-
return (
|
|
44
|
-
content.includes("Auto-generated by @ic-reactor/codegen") &&
|
|
45
|
-
content.includes("createActorHooks(")
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
33
|
export interface PipelineResult {
|
|
50
34
|
canisterName: string
|
|
51
35
|
success: boolean
|
|
@@ -125,30 +109,18 @@ export async function runCanisterPipeline(
|
|
|
125
109
|
// ── Step 2: Reactor file ───────────────────────────────────────────────────
|
|
126
110
|
|
|
127
111
|
const reactorPath = path.join(canisterOutDir, "index.ts")
|
|
128
|
-
const reactorClass = resolveReactorClass(canisterConfig)
|
|
129
112
|
|
|
130
113
|
try {
|
|
131
114
|
const reactorContent = generateReactorFile({
|
|
132
115
|
canisterName: name,
|
|
133
116
|
didFile: resolvedDidFile,
|
|
134
117
|
clientManagerPath: resolvedClientManagerPath,
|
|
135
|
-
reactorClass,
|
|
136
118
|
})
|
|
137
119
|
|
|
138
120
|
fs.mkdirSync(canisterOutDir, { recursive: true })
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
} else {
|
|
143
|
-
const existingIndexContent = fs.readFileSync(reactorPath, "utf-8")
|
|
144
|
-
|
|
145
|
-
if (isGeneratedIndexFile(existingIndexContent)) {
|
|
146
|
-
fs.writeFileSync(reactorPath, reactorContent)
|
|
147
|
-
files.push({ success: true, filePath: reactorPath })
|
|
148
|
-
} else {
|
|
149
|
-
files.push({ success: true, filePath: reactorPath, skipped: true })
|
|
150
|
-
}
|
|
151
|
-
}
|
|
121
|
+
fs.writeFileSync(reactorPath, reactorContent)
|
|
122
|
+
|
|
123
|
+
files.push({ success: true, filePath: reactorPath })
|
|
152
124
|
} catch (err) {
|
|
153
125
|
files.push({
|
|
154
126
|
success: false,
|
package/src/types.ts
CHANGED
|
@@ -24,22 +24,10 @@ export interface CanisterConfig {
|
|
|
24
24
|
* Example: "../../clients" → `import { clientManager } from "../../clients"`
|
|
25
25
|
*/
|
|
26
26
|
clientManagerPath?: string
|
|
27
|
-
/**
|
|
28
|
-
* Reactor class used for generated hooks in this canister.
|
|
29
|
-
* Defaults to DisplayReactor for backward compatibility.
|
|
30
|
-
*/
|
|
31
|
-
mode?: ReactorClassName
|
|
32
27
|
/** Optional fixed canister ID */
|
|
33
28
|
canisterId?: string
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
export type ReactorClassName =
|
|
37
|
-
| "Reactor"
|
|
38
|
-
| "DisplayReactor"
|
|
39
|
-
| "CandidReactor"
|
|
40
|
-
| "CandidDisplayReactor"
|
|
41
|
-
| "MetadataDisplayReactor"
|
|
42
|
-
|
|
43
31
|
/**
|
|
44
32
|
* Top-level codegen / CLI configuration (stored in `ic-reactor.json`).
|
|
45
33
|
*/
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`Reactor generator > keeps default behavior as DisplayReactor > display-reactor-index 1`] = `
|
|
4
|
-
"import { createActorHooks } from "@ic-reactor/react"
|
|
5
|
-
import { DisplayReactor } from "@ic-reactor/react"
|
|
6
|
-
import { clientManager } from "../../clients"
|
|
7
|
-
import { idlFactory, type _SERVICE } from "./declarations/backend"
|
|
8
|
-
|
|
9
|
-
export type BackendService = _SERVICE
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Backend Reactor
|
|
13
|
-
*
|
|
14
|
-
* Auto-generated by @ic-reactor/codegen — do not edit.
|
|
15
|
-
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
16
|
-
*/
|
|
17
|
-
export const backendReactor = new DisplayReactor<BackendService>({
|
|
18
|
-
clientManager,
|
|
19
|
-
idlFactory,
|
|
20
|
-
name: "backend",
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
export const {
|
|
24
|
-
useActorQuery: useBackendQuery,
|
|
25
|
-
useActorSuspenseQuery: useBackendSuspenseQuery,
|
|
26
|
-
useActorInfiniteQuery: useBackendInfiniteQuery,
|
|
27
|
-
useActorSuspenseInfiniteQuery: useBackendSuspenseInfiniteQuery,
|
|
28
|
-
useActorMutation: useBackendMutation,
|
|
29
|
-
useActorMethod: useBackendMethod,
|
|
30
|
-
} = createActorHooks(backendReactor)
|
|
31
|
-
"
|
|
32
|
-
`;
|
|
33
|
-
|
|
34
|
-
exports[`Reactor generator > supports Reactor mode generation > reactor-index 1`] = `
|
|
35
|
-
"import { createActorHooks } from "@ic-reactor/react"
|
|
36
|
-
import { Reactor } from "@ic-reactor/react"
|
|
37
|
-
import { clientManager } from "../../clients"
|
|
38
|
-
import { idlFactory, type _SERVICE } from "./declarations/workflow_engine"
|
|
39
|
-
|
|
40
|
-
export type WorkflowEngineService = _SERVICE
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* WorkflowEngine Reactor
|
|
44
|
-
*
|
|
45
|
-
* Auto-generated by @ic-reactor/codegen — do not edit.
|
|
46
|
-
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
47
|
-
*/
|
|
48
|
-
export const workflowEngineReactor = new Reactor<WorkflowEngineService>({
|
|
49
|
-
clientManager,
|
|
50
|
-
idlFactory,
|
|
51
|
-
name: "workflow_engine",
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
export const {
|
|
55
|
-
useActorQuery: useWorkflowEngineQuery,
|
|
56
|
-
useActorSuspenseQuery: useWorkflowEngineSuspenseQuery,
|
|
57
|
-
useActorInfiniteQuery: useWorkflowEngineInfiniteQuery,
|
|
58
|
-
useActorSuspenseInfiniteQuery: useWorkflowEngineSuspenseInfiniteQuery,
|
|
59
|
-
useActorMutation: useWorkflowEngineMutation,
|
|
60
|
-
useActorMethod: useWorkflowEngineMethod,
|
|
61
|
-
} = createActorHooks(workflowEngineReactor)
|
|
62
|
-
"
|
|
63
|
-
`;
|
|
64
|
-
|
|
65
|
-
exports[`Reactor generator > supports candid reactor subclasses > metadata-display-reactor-index 1`] = `
|
|
66
|
-
"import { createActorHooks } from "@ic-reactor/react"
|
|
67
|
-
import { MetadataDisplayReactor } from "@ic-reactor/candid"
|
|
68
|
-
import { clientManager } from "../../clients"
|
|
69
|
-
import { idlFactory, type _SERVICE } from "./declarations/ledger"
|
|
70
|
-
|
|
71
|
-
export type LedgerService = _SERVICE
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Ledger Reactor
|
|
75
|
-
*
|
|
76
|
-
* Auto-generated by @ic-reactor/codegen — do not edit.
|
|
77
|
-
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
78
|
-
*/
|
|
79
|
-
export const ledgerReactor = new MetadataDisplayReactor<LedgerService>({
|
|
80
|
-
clientManager,
|
|
81
|
-
idlFactory,
|
|
82
|
-
name: "ledger",
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
export const {
|
|
86
|
-
useActorQuery: useLedgerQuery,
|
|
87
|
-
useActorSuspenseQuery: useLedgerSuspenseQuery,
|
|
88
|
-
useActorInfiniteQuery: useLedgerInfiniteQuery,
|
|
89
|
-
useActorSuspenseInfiniteQuery: useLedgerSuspenseInfiniteQuery,
|
|
90
|
-
useActorMutation: useLedgerMutation,
|
|
91
|
-
useActorMethod: useLedgerMethod,
|
|
92
|
-
} = createActorHooks(ledgerReactor)
|
|
93
|
-
"
|
|
94
|
-
`;
|
package/src/pipeline.test.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs"
|
|
2
|
-
import os from "node:os"
|
|
3
|
-
import path from "node:path"
|
|
4
|
-
import { afterEach, describe, expect, it } from "vitest"
|
|
5
|
-
import { runCanisterPipeline } from "./pipeline"
|
|
6
|
-
|
|
7
|
-
describe("Codegen pipeline", () => {
|
|
8
|
-
const tempDirs: string[] = []
|
|
9
|
-
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
for (const dir of tempDirs) {
|
|
12
|
-
fs.rmSync(dir, { recursive: true, force: true })
|
|
13
|
-
}
|
|
14
|
-
tempDirs.length = 0
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
function createTempProject() {
|
|
18
|
-
const projectRoot = fs.mkdtempSync(
|
|
19
|
-
path.join(os.tmpdir(), "ic-reactor-codegen-pipeline-")
|
|
20
|
-
)
|
|
21
|
-
tempDirs.push(projectRoot)
|
|
22
|
-
return projectRoot
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function writeDid(projectRoot: string, fileName: string) {
|
|
26
|
-
fs.writeFileSync(
|
|
27
|
-
path.join(projectRoot, fileName),
|
|
28
|
-
`service : {
|
|
29
|
-
greet: (text) -> (text) query;
|
|
30
|
-
}`
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
it("uses per-canister mode to generate Reactor-based hooks", async () => {
|
|
35
|
-
const projectRoot = createTempProject()
|
|
36
|
-
writeDid(projectRoot, "workflow_engine.did")
|
|
37
|
-
|
|
38
|
-
const result = await runCanisterPipeline({
|
|
39
|
-
canisterConfig: {
|
|
40
|
-
name: "workflow_engine",
|
|
41
|
-
didFile: "workflow_engine.did",
|
|
42
|
-
mode: "Reactor",
|
|
43
|
-
},
|
|
44
|
-
projectRoot,
|
|
45
|
-
globalConfig: {
|
|
46
|
-
outDir: "src/declarations",
|
|
47
|
-
clientManagerPath: "../../clients",
|
|
48
|
-
},
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
expect(result.success).toBe(true)
|
|
52
|
-
|
|
53
|
-
const indexPath = path.join(
|
|
54
|
-
projectRoot,
|
|
55
|
-
"src/declarations/workflow_engine/index.ts"
|
|
56
|
-
)
|
|
57
|
-
const generated = fs.readFileSync(indexPath, "utf-8")
|
|
58
|
-
|
|
59
|
-
expect(generated).toContain("new Reactor<WorkflowEngineService>")
|
|
60
|
-
expect(generated).not.toContain("new DisplayReactor<WorkflowEngineService>")
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it("does not overwrite user-modified index.ts on regenerate", async () => {
|
|
64
|
-
const projectRoot = createTempProject()
|
|
65
|
-
writeDid(projectRoot, "backend.did")
|
|
66
|
-
|
|
67
|
-
const options = {
|
|
68
|
-
canisterConfig: {
|
|
69
|
-
name: "backend",
|
|
70
|
-
didFile: "backend.did",
|
|
71
|
-
},
|
|
72
|
-
projectRoot,
|
|
73
|
-
globalConfig: {
|
|
74
|
-
outDir: "src/declarations",
|
|
75
|
-
clientManagerPath: "../../clients",
|
|
76
|
-
},
|
|
77
|
-
} as const
|
|
78
|
-
|
|
79
|
-
const first = await runCanisterPipeline(options)
|
|
80
|
-
expect(first.success).toBe(true)
|
|
81
|
-
|
|
82
|
-
const indexPath = path.join(
|
|
83
|
-
projectRoot,
|
|
84
|
-
"src/declarations/backend/index.ts"
|
|
85
|
-
)
|
|
86
|
-
fs.writeFileSync(
|
|
87
|
-
indexPath,
|
|
88
|
-
`// user custom canister file
|
|
89
|
-
export const customBackendIndex = true
|
|
90
|
-
`
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
const second = await runCanisterPipeline(options)
|
|
94
|
-
expect(second.success).toBe(true)
|
|
95
|
-
|
|
96
|
-
const wrapper = fs.readFileSync(indexPath, "utf-8")
|
|
97
|
-
expect(wrapper).toContain("customBackendIndex = true")
|
|
98
|
-
expect(second.files).toEqual(
|
|
99
|
-
expect.arrayContaining([
|
|
100
|
-
expect.objectContaining({
|
|
101
|
-
filePath: indexPath,
|
|
102
|
-
skipped: true,
|
|
103
|
-
success: true,
|
|
104
|
-
}),
|
|
105
|
-
])
|
|
106
|
-
)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it("overwrites legacy generated index.ts during regeneration", async () => {
|
|
110
|
-
const projectRoot = createTempProject()
|
|
111
|
-
writeDid(projectRoot, "backend.did")
|
|
112
|
-
|
|
113
|
-
const canisterOutDir = path.join(projectRoot, "src/declarations/backend")
|
|
114
|
-
fs.mkdirSync(canisterOutDir, { recursive: true })
|
|
115
|
-
|
|
116
|
-
// Simulate a generated index.ts content from older versions.
|
|
117
|
-
fs.writeFileSync(
|
|
118
|
-
path.join(canisterOutDir, "index.ts"),
|
|
119
|
-
`import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
|
|
120
|
-
import { clientManager } from "../../clients"
|
|
121
|
-
import { idlFactory, type _SERVICE } from "./declarations/backend"
|
|
122
|
-
|
|
123
|
-
export type BackendService = _SERVICE
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Backend Reactor
|
|
127
|
-
*
|
|
128
|
-
* Auto-generated by @ic-reactor/codegen — do not edit.
|
|
129
|
-
* Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
|
|
130
|
-
*/
|
|
131
|
-
export const backendReactor = new DisplayReactor<BackendService>({
|
|
132
|
-
clientManager,
|
|
133
|
-
idlFactory,
|
|
134
|
-
name: "backend",
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
export const {
|
|
138
|
-
useActorQuery: useBackendQuery,
|
|
139
|
-
} = createActorHooks(backendReactor)
|
|
140
|
-
`
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
const result = await runCanisterPipeline({
|
|
144
|
-
canisterConfig: {
|
|
145
|
-
name: "backend",
|
|
146
|
-
didFile: "backend.did",
|
|
147
|
-
},
|
|
148
|
-
projectRoot,
|
|
149
|
-
globalConfig: {
|
|
150
|
-
outDir: "src/declarations",
|
|
151
|
-
clientManagerPath: "../../clients",
|
|
152
|
-
},
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
expect(result.success).toBe(true)
|
|
156
|
-
|
|
157
|
-
const indexPath = path.join(canisterOutDir, "index.ts")
|
|
158
|
-
const generated = fs.readFileSync(indexPath, "utf-8")
|
|
159
|
-
expect(generated).toContain("new DisplayReactor<BackendService>")
|
|
160
|
-
expect(generated).toContain("useBackendMutation")
|
|
161
|
-
expect(generated).not.toContain('export * from "./index.generated"')
|
|
162
|
-
})
|
|
163
|
-
})
|
package/src/reactor.test.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest"
|
|
2
|
-
import { generateReactorFile } from "./generators"
|
|
3
|
-
|
|
4
|
-
describe("Reactor generator", () => {
|
|
5
|
-
it("keeps default behavior as DisplayReactor", () => {
|
|
6
|
-
const content = generateReactorFile({
|
|
7
|
-
canisterName: "backend",
|
|
8
|
-
didFile: "mock/backend.did",
|
|
9
|
-
clientManagerPath: "../../clients",
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
expect(content).toMatchSnapshot("display-reactor-index")
|
|
13
|
-
expect(content).toContain("new DisplayReactor<BackendService>")
|
|
14
|
-
expect(content).not.toContain("export const BackendReactorMode")
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it("supports Reactor mode generation", () => {
|
|
18
|
-
const content = generateReactorFile({
|
|
19
|
-
canisterName: "workflow_engine",
|
|
20
|
-
didFile: "mock/workflow_engine.did",
|
|
21
|
-
reactorClass: "Reactor",
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
expect(content).toMatchSnapshot("reactor-index")
|
|
25
|
-
expect(content).toContain("new Reactor<WorkflowEngineService>")
|
|
26
|
-
expect(content).not.toContain("createWorkflowEngineDisplayReactor")
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it("supports candid reactor subclasses", () => {
|
|
30
|
-
const content = generateReactorFile({
|
|
31
|
-
canisterName: "ledger",
|
|
32
|
-
didFile: "mock/ledger.did",
|
|
33
|
-
reactorClass: "MetadataDisplayReactor",
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
expect(content).toMatchSnapshot("metadata-display-reactor-index")
|
|
37
|
-
expect(content).toContain(
|
|
38
|
-
'import { createActorHooks } from "@ic-reactor/react"'
|
|
39
|
-
)
|
|
40
|
-
expect(content).toContain(
|
|
41
|
-
'import { MetadataDisplayReactor } from "@ic-reactor/candid"'
|
|
42
|
-
)
|
|
43
|
-
expect(content).toContain("new MetadataDisplayReactor<LedgerService>")
|
|
44
|
-
})
|
|
45
|
-
})
|