@ic-reactor/codegen 0.4.0 → 0.5.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/README.md +45 -0
- package/dist/index.cjs +168 -65
- package/dist/index.d.cts +193 -27
- package/dist/index.d.ts +193 -27
- package/dist/index.js +165 -65
- package/package.json +1 -1
- package/src/bindgen.test.ts +1 -1
- package/src/generators/client.ts +49 -0
- package/src/generators/declarations.ts +112 -0
- package/src/generators/index.ts +18 -0
- package/src/generators/reactor.ts +82 -0
- package/src/index.ts +13 -11
- package/src/naming.test.ts +6 -20
- package/src/naming.ts +21 -43
- package/src/parser.ts +79 -0
- package/src/pipeline.ts +144 -0
- package/src/types.ts +65 -0
- package/src/bindgen.ts +0 -101
- package/src/templates/__snapshots__/client.test.ts.snap +0 -35
- package/src/templates/__snapshots__/reactor.test.ts.snap +0 -28
- package/src/templates/client.test.ts +0 -20
- package/src/templates/client.ts +0 -37
- package/src/templates/reactor.test.ts +0 -23
- package/src/templates/reactor.ts +0 -99
package/src/bindgen.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bindgen utilities
|
|
3
|
-
*
|
|
4
|
-
* Generates TypeScript declarations from Candid files using @ic-reactor/parser.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { didToJs, didToTs } from "@ic-reactor/parser"
|
|
8
|
-
import path from "node:path"
|
|
9
|
-
import fs from "node:fs"
|
|
10
|
-
|
|
11
|
-
export interface BindgenOptions {
|
|
12
|
-
/** Path to the .did file */
|
|
13
|
-
didFile: string
|
|
14
|
-
/** Output directory for generated declarations */
|
|
15
|
-
outDir: string
|
|
16
|
-
/** Canister name (used for naming) */
|
|
17
|
-
canisterName: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface BindgenResult {
|
|
21
|
-
success: boolean
|
|
22
|
-
declarationsDir: string
|
|
23
|
-
error?: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Generate TypeScript declarations from a Candid file
|
|
28
|
-
*
|
|
29
|
-
* This creates:
|
|
30
|
-
* - declarations/<canisterName>.js - IDL factory
|
|
31
|
-
* - declarations/<canisterName>.d.ts - Types
|
|
32
|
-
*/
|
|
33
|
-
export async function generateDeclarations(
|
|
34
|
-
options: BindgenOptions
|
|
35
|
-
): Promise<BindgenResult> {
|
|
36
|
-
const { didFile, outDir } = options
|
|
37
|
-
|
|
38
|
-
// Ensure the .did file exists
|
|
39
|
-
if (!fs.existsSync(didFile)) {
|
|
40
|
-
return {
|
|
41
|
-
success: false,
|
|
42
|
-
declarationsDir: "",
|
|
43
|
-
error: `DID file not found: ${didFile}`,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const declarationsDir = path.join(outDir, "declarations")
|
|
48
|
-
const didFileName = path.basename(didFile) // e.g., "my_canister.did"
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
// Read content first so we can safely delete the directory if didFile is inside it
|
|
52
|
-
const didContent = fs.readFileSync(didFile, "utf-8")
|
|
53
|
-
|
|
54
|
-
// Ensure the output directory exists
|
|
55
|
-
if (!fs.existsSync(outDir)) {
|
|
56
|
-
fs.mkdirSync(outDir, { recursive: true })
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Clean existing declarations before regenerating
|
|
60
|
-
if (fs.existsSync(declarationsDir)) {
|
|
61
|
-
fs.rmSync(declarationsDir, { recursive: true, force: true })
|
|
62
|
-
}
|
|
63
|
-
fs.mkdirSync(declarationsDir, { recursive: true })
|
|
64
|
-
|
|
65
|
-
const jsContent = didToJs(didContent)
|
|
66
|
-
const tsContent = didToTs(didContent)
|
|
67
|
-
|
|
68
|
-
// Write .did, .js and .d.ts
|
|
69
|
-
const baseName = didFileName.replace(/\.did$/, "")
|
|
70
|
-
const jsPath = path.join(declarationsDir, baseName + ".js")
|
|
71
|
-
const dtsPath = path.join(declarationsDir, baseName + ".d.ts")
|
|
72
|
-
const didPath = path.join(declarationsDir, didFileName)
|
|
73
|
-
|
|
74
|
-
fs.writeFileSync(jsPath, jsContent)
|
|
75
|
-
fs.writeFileSync(dtsPath, tsContent)
|
|
76
|
-
fs.writeFileSync(didPath, didContent)
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
success: true,
|
|
80
|
-
declarationsDir,
|
|
81
|
-
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
declarationsDir,
|
|
86
|
-
error: error instanceof Error ? error.message : String(error),
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Check if declarations already exist for a canister
|
|
93
|
-
*/
|
|
94
|
-
export function declarationsExist(
|
|
95
|
-
outDir: string,
|
|
96
|
-
canisterName: string
|
|
97
|
-
): boolean {
|
|
98
|
-
const declarationsDir = path.join(outDir, "declarations")
|
|
99
|
-
const didTsPath = path.join(declarationsDir, `${canisterName}.d.ts`)
|
|
100
|
-
return fs.existsSync(didTsPath)
|
|
101
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`Client File Generation > generates correctly with custom query client 1`] = `
|
|
4
|
-
"import { ClientManager } from "@ic-reactor/react"
|
|
5
|
-
import { queryClient } from "../query-client"
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* IC Reactor Client Manager
|
|
9
|
-
*
|
|
10
|
-
* Auto-generated by @ic-reactor/codegen
|
|
11
|
-
*/
|
|
12
|
-
export const clientManager = new ClientManager({
|
|
13
|
-
queryClient,
|
|
14
|
-
withCanisterEnv: true,
|
|
15
|
-
})
|
|
16
|
-
"
|
|
17
|
-
`;
|
|
18
|
-
|
|
19
|
-
exports[`Client File Generation > generates correctly with default host 1`] = `
|
|
20
|
-
"import { ClientManager } from "@ic-reactor/react"
|
|
21
|
-
import { QueryClient } from "@tanstack/react-query"
|
|
22
|
-
|
|
23
|
-
export const queryClient = new QueryClient()
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* IC Reactor Client Manager
|
|
27
|
-
*
|
|
28
|
-
* Auto-generated by @ic-reactor/codegen
|
|
29
|
-
*/
|
|
30
|
-
export const clientManager = new ClientManager({
|
|
31
|
-
queryClient,
|
|
32
|
-
withCanisterEnv: true,
|
|
33
|
-
})
|
|
34
|
-
"
|
|
35
|
-
`;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`Reactor File Generation > Standard Mode > generates correctly 1`] = `
|
|
4
|
-
"import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
|
|
5
|
-
import { clientManager } from "../../client"
|
|
6
|
-
import { idlFactory, type _SERVICE } from "./declarations/my_canister"
|
|
7
|
-
|
|
8
|
-
export type MyCanisterService = _SERVICE
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* MyCanister Display Reactor
|
|
12
|
-
*/
|
|
13
|
-
export const myCanisterReactor = new DisplayReactor<MyCanisterService>({
|
|
14
|
-
clientManager,
|
|
15
|
-
idlFactory,
|
|
16
|
-
name: "my_canister",
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
export const {
|
|
20
|
-
useActorQuery: useMyCanisterQuery,
|
|
21
|
-
useActorSuspenseQuery: useMyCanisterSuspenseQuery,
|
|
22
|
-
useActorInfiniteQuery: useMyCanisterInfiniteQuery,
|
|
23
|
-
useActorSuspenseInfiniteQuery: useMyCanisterSuspenseInfiniteQuery,
|
|
24
|
-
useActorMutation: useMyCanisterMutation,
|
|
25
|
-
useActorMethod: useMyCanisterMethod,
|
|
26
|
-
} = createActorHooks(myCanisterReactor)
|
|
27
|
-
"
|
|
28
|
-
`;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest"
|
|
2
|
-
import { generateClientFile } from "./client"
|
|
3
|
-
|
|
4
|
-
describe("Client File Generation", () => {
|
|
5
|
-
it("generates correctly with default host", () => {
|
|
6
|
-
const result = generateClientFile()
|
|
7
|
-
expect(result).toMatchSnapshot()
|
|
8
|
-
expect(result).toContain(
|
|
9
|
-
'import { QueryClient } from "@tanstack/react-query'
|
|
10
|
-
)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it("generates correctly with custom query client", () => {
|
|
14
|
-
const result = generateClientFile({
|
|
15
|
-
queryClientPath: "../query-client",
|
|
16
|
-
})
|
|
17
|
-
expect(result).toMatchSnapshot()
|
|
18
|
-
expect(result).toContain('import { queryClient } from "../query-client"')
|
|
19
|
-
})
|
|
20
|
-
})
|
package/src/templates/client.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client Manager Generator
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface ClientGeneratorOptions {
|
|
6
|
-
/** Optional path to an existing queryClient */
|
|
7
|
-
queryClientPath?: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generate a central client manager file
|
|
12
|
-
*/
|
|
13
|
-
export function generateClientFile(
|
|
14
|
-
options: ClientGeneratorOptions = {}
|
|
15
|
-
): string {
|
|
16
|
-
const { queryClientPath } = options
|
|
17
|
-
|
|
18
|
-
return `import { ClientManager } from "@ic-reactor/react"
|
|
19
|
-
${
|
|
20
|
-
queryClientPath
|
|
21
|
-
? `import { queryClient } from "${queryClientPath}"`
|
|
22
|
-
: `import { QueryClient } from "@tanstack/react-query"
|
|
23
|
-
|
|
24
|
-
export const queryClient = new QueryClient()`
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* IC Reactor Client Manager
|
|
29
|
-
*
|
|
30
|
-
* Auto-generated by @ic-reactor/codegen
|
|
31
|
-
*/
|
|
32
|
-
export const clientManager = new ClientManager({
|
|
33
|
-
queryClient,
|
|
34
|
-
withCanisterEnv: true,
|
|
35
|
-
})
|
|
36
|
-
`
|
|
37
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest"
|
|
2
|
-
import { generateReactorFile, type ReactorGeneratorOptions } from "./reactor"
|
|
3
|
-
|
|
4
|
-
describe("Reactor File Generation", () => {
|
|
5
|
-
const baseOptions: ReactorGeneratorOptions = {
|
|
6
|
-
canisterName: "my_canister",
|
|
7
|
-
didFile: "path/to/my_canister.did",
|
|
8
|
-
clientManagerPath: "../../client",
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe("Standard Mode", () => {
|
|
12
|
-
it("generates correctly", () => {
|
|
13
|
-
const result = generateReactorFile(baseOptions)
|
|
14
|
-
expect(result).toMatchSnapshot()
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it("uses default client path if not provided", () => {
|
|
18
|
-
const options = { ...baseOptions, canisterConfig: { didFile: "foo.did" } }
|
|
19
|
-
const result = generateReactorFile(options)
|
|
20
|
-
expect(result).toContain('import { clientManager } from "../../client"')
|
|
21
|
-
})
|
|
22
|
-
})
|
|
23
|
-
})
|
package/src/templates/reactor.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reactor file template generator
|
|
3
|
-
*
|
|
4
|
-
* Generates the reactor instance file for a canister using DisplayReactor.
|
|
5
|
-
* Standardizes the output to include typed hooks and clean imports.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from "node:path"
|
|
9
|
-
import { toPascalCase, getReactorName, getServiceTypeName } from "../naming.js"
|
|
10
|
-
|
|
11
|
-
export type ReactorGeneratorOptions = {
|
|
12
|
-
canisterName: string
|
|
13
|
-
didFile: string
|
|
14
|
-
clientManagerPath?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Generate the reactor file content
|
|
19
|
-
*/
|
|
20
|
-
export function generateReactorFile(options: ReactorGeneratorOptions): string {
|
|
21
|
-
const pascalName = toPascalCase(options.canisterName)
|
|
22
|
-
const reactorName = getReactorName(options.canisterName)
|
|
23
|
-
const serviceName = getServiceTypeName(options.canisterName)
|
|
24
|
-
// Always use DisplayReactor for now
|
|
25
|
-
const reactorType = "DisplayReactor"
|
|
26
|
-
|
|
27
|
-
const didFileName = path.basename(options.didFile)
|
|
28
|
-
const baseName = didFileName.replace(/\.did$/, "")
|
|
29
|
-
const declarationsPath = `./declarations/${baseName}`
|
|
30
|
-
const clientManagerPath = options.clientManagerPath ?? "../../clients"
|
|
31
|
-
|
|
32
|
-
const vars: TemplateVars = {
|
|
33
|
-
canisterName: options.canisterName,
|
|
34
|
-
pascalName,
|
|
35
|
-
reactorName,
|
|
36
|
-
serviceName,
|
|
37
|
-
reactorType,
|
|
38
|
-
clientManagerPath,
|
|
39
|
-
declarationsPath,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// If we have DID content, we can generate method-specific hooks (clean & typed)
|
|
43
|
-
return generateStandardReactorFile(vars)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
47
|
-
// SHARED TYPES
|
|
48
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
49
|
-
|
|
50
|
-
interface TemplateVars {
|
|
51
|
-
canisterName: string
|
|
52
|
-
pascalName: string
|
|
53
|
-
reactorName: string
|
|
54
|
-
serviceName: string
|
|
55
|
-
reactorType: string
|
|
56
|
-
clientManagerPath: string
|
|
57
|
-
declarationsPath: string
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
61
|
-
// STANDARD MODE
|
|
62
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
63
|
-
|
|
64
|
-
function generateStandardReactorFile(vars: TemplateVars): string {
|
|
65
|
-
const {
|
|
66
|
-
pascalName,
|
|
67
|
-
reactorName,
|
|
68
|
-
serviceName,
|
|
69
|
-
reactorType,
|
|
70
|
-
clientManagerPath,
|
|
71
|
-
declarationsPath,
|
|
72
|
-
canisterName,
|
|
73
|
-
} = vars
|
|
74
|
-
|
|
75
|
-
return `import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
76
|
-
import { clientManager } from "${clientManagerPath}"
|
|
77
|
-
import { idlFactory, type _SERVICE } from "${declarationsPath}"
|
|
78
|
-
|
|
79
|
-
export type ${serviceName} = _SERVICE
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* ${pascalName} Display Reactor
|
|
83
|
-
*/
|
|
84
|
-
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
85
|
-
clientManager,
|
|
86
|
-
idlFactory,
|
|
87
|
-
name: "${canisterName}",
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
export const {
|
|
91
|
-
useActorQuery: use${pascalName}Query,
|
|
92
|
-
useActorSuspenseQuery: use${pascalName}SuspenseQuery,
|
|
93
|
-
useActorInfiniteQuery: use${pascalName}InfiniteQuery,
|
|
94
|
-
useActorSuspenseInfiniteQuery: use${pascalName}SuspenseInfiniteQuery,
|
|
95
|
-
useActorMutation: use${pascalName}Mutation,
|
|
96
|
-
useActorMethod: use${pascalName}Method,
|
|
97
|
-
} = createActorHooks(${reactorName})
|
|
98
|
-
`
|
|
99
|
-
}
|