@ic-reactor/codegen 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +61 -409
- package/dist/index.d.cts +17 -171
- package/dist/index.d.ts +17 -171
- package/dist/index.js +60 -397
- package/package.json +4 -12
- package/src/__snapshots__/bindgen.test.ts.snap +18 -0
- package/src/bindgen.test.ts +85 -0
- package/src/bindgen.ts +21 -33
- package/src/index.ts +6 -37
- package/src/templates/__snapshots__/client.test.ts.snap +35 -0
- package/src/templates/__snapshots__/reactor.test.ts.snap +5 -140
- package/src/templates/client.test.ts +20 -0
- package/src/templates/client.ts +37 -0
- package/src/templates/reactor.test.ts +5 -60
- package/src/templates/reactor.ts +36 -227
- package/src/did.test.ts +0 -102
- package/src/did.ts +0 -85
- package/src/templates/__snapshots__/infiniteQuery.test.ts.snap +0 -44
- package/src/templates/__snapshots__/mutation.test.ts.snap +0 -59
- package/src/templates/__snapshots__/query.test.ts.snap +0 -64
- package/src/templates/infiniteQuery.test.ts +0 -42
- package/src/templates/infiniteQuery.ts +0 -74
- package/src/templates/mutation.test.ts +0 -43
- package/src/templates/mutation.ts +0 -51
- package/src/templates/query.test.ts +0 -44
- package/src/templates/query.ts +0 -71
- package/src/types.ts +0 -78
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest"
|
|
2
|
-
import {
|
|
3
|
-
generateInfiniteQueryHook,
|
|
4
|
-
InfiniteQueryHookOptions,
|
|
5
|
-
} from "./infiniteQuery"
|
|
6
|
-
import type { MethodInfo } from "../types"
|
|
7
|
-
|
|
8
|
-
describe("Infinite Query Hook Generation", () => {
|
|
9
|
-
const listItems: MethodInfo = {
|
|
10
|
-
name: "list_items",
|
|
11
|
-
type: "query",
|
|
12
|
-
hasArgs: true,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
it("generates infinite query hook correctly", () => {
|
|
16
|
-
const options: InfiniteQueryHookOptions = {
|
|
17
|
-
canisterName: "my_canister",
|
|
18
|
-
method: listItems,
|
|
19
|
-
type: "infiniteQuery",
|
|
20
|
-
}
|
|
21
|
-
const result = generateInfiniteQueryHook(options)
|
|
22
|
-
expect(result).toMatchSnapshot()
|
|
23
|
-
expect(result).toContain("createInfiniteQuery(myCanisterReactor, {")
|
|
24
|
-
expect(result).toContain(
|
|
25
|
-
"export const useListItemsInfiniteQuery = listItemsInfiniteQuery.useInfiniteQuery"
|
|
26
|
-
)
|
|
27
|
-
// Should contain default pagination logic placeholders
|
|
28
|
-
expect(result).toContain("initialPageParam: 0 as PageCursor")
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it("handles suspense infinite query type", () => {
|
|
32
|
-
const options: InfiniteQueryHookOptions = {
|
|
33
|
-
canisterName: "my_canister",
|
|
34
|
-
method: listItems,
|
|
35
|
-
type: "suspenseInfiniteQuery",
|
|
36
|
-
}
|
|
37
|
-
const result = generateInfiniteQueryHook(options)
|
|
38
|
-
expect(result).toContain(
|
|
39
|
-
"export const useListItemsSuspenseInfiniteQuery = listItemsSuspenseInfiniteQuery.useInfiniteQuery"
|
|
40
|
-
)
|
|
41
|
-
})
|
|
42
|
-
})
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Infinite Query hook template generator
|
|
3
|
-
*
|
|
4
|
-
* Generates createInfiniteQuery-based hooks for paginated canister methods.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { MethodInfo, HookType } from "../types.js"
|
|
8
|
-
import {
|
|
9
|
-
getReactorName,
|
|
10
|
-
getServiceTypeName,
|
|
11
|
-
getHookExportName,
|
|
12
|
-
getReactHookName,
|
|
13
|
-
} from "../naming.js"
|
|
14
|
-
|
|
15
|
-
export interface InfiniteQueryHookOptions {
|
|
16
|
-
canisterName: string
|
|
17
|
-
method: MethodInfo
|
|
18
|
-
type?: HookType
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate an infinite query hook file content
|
|
23
|
-
*/
|
|
24
|
-
export function generateInfiniteQueryHook(
|
|
25
|
-
options: InfiniteQueryHookOptions
|
|
26
|
-
): string {
|
|
27
|
-
const { canisterName, method, type = "infiniteQuery" } = options
|
|
28
|
-
|
|
29
|
-
const reactorName = getReactorName(canisterName)
|
|
30
|
-
const serviceName = getServiceTypeName(canisterName)
|
|
31
|
-
const hookExportName = getHookExportName(method.name, type)
|
|
32
|
-
const reactHookName = getReactHookName(method.name, type)
|
|
33
|
-
|
|
34
|
-
return `/**
|
|
35
|
-
* Infinite Query: ${method.name}
|
|
36
|
-
*
|
|
37
|
-
* Auto-generated by @ic-reactor/codegen
|
|
38
|
-
*
|
|
39
|
-
* ⚠️ CUSTOMIZATION REQUIRED: Configure getArgs and getNextPageParam below.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* const { data, fetchNextPage, hasNextPage } = ${hookExportName}.useInfiniteQuery()
|
|
43
|
-
* const allItems = data?.pages.flatMap(page => page.items) ?? []
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
import { createInfiniteQuery } from "@ic-reactor/react"
|
|
47
|
-
import { ${reactorName}, type ${serviceName} } from "../reactor"
|
|
48
|
-
|
|
49
|
-
/** Define your pagination cursor type */
|
|
50
|
-
type PageCursor = number
|
|
51
|
-
|
|
52
|
-
export const ${hookExportName} = createInfiniteQuery(${reactorName}, {
|
|
53
|
-
functionName: "${method.name}",
|
|
54
|
-
|
|
55
|
-
initialPageParam: 0 as PageCursor,
|
|
56
|
-
|
|
57
|
-
/** Convert page param to method arguments — customize for your API */
|
|
58
|
-
getArgs: (pageParam: PageCursor) => {
|
|
59
|
-
return [{ offset: pageParam, limit: 10 }] as Parameters<${serviceName}["${method.name}"]>
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
/** Extract next page param — return undefined when no more pages */
|
|
63
|
-
getNextPageParam: (lastPage, allPages, lastPageParam) => {
|
|
64
|
-
// Example: offset-based
|
|
65
|
-
// if (lastPage.items.length < 10) return undefined
|
|
66
|
-
// return lastPageParam + 10
|
|
67
|
-
return undefined
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
/** React hook for paginated ${method.name} */
|
|
72
|
-
export const ${reactHookName} = ${hookExportName}.useInfiniteQuery
|
|
73
|
-
`
|
|
74
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest"
|
|
2
|
-
import { generateMutationHook, MutationHookOptions } from "./mutation"
|
|
3
|
-
import type { MethodInfo } from "../types"
|
|
4
|
-
|
|
5
|
-
describe("Mutation Hook Generation", () => {
|
|
6
|
-
const methodWithArgs: MethodInfo = {
|
|
7
|
-
name: "create_item",
|
|
8
|
-
type: "mutation",
|
|
9
|
-
hasArgs: true,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const methodNoArgs: MethodInfo = {
|
|
13
|
-
name: "init_system",
|
|
14
|
-
type: "mutation",
|
|
15
|
-
hasArgs: false,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
it("generates hook correctly for mutation with args", () => {
|
|
19
|
-
const options: MutationHookOptions = {
|
|
20
|
-
canisterName: "my_canister",
|
|
21
|
-
method: methodWithArgs,
|
|
22
|
-
}
|
|
23
|
-
const result = generateMutationHook(options)
|
|
24
|
-
expect(result).toMatchSnapshot()
|
|
25
|
-
expect(result).toContain("createMutation(myCanisterReactor, {")
|
|
26
|
-
expect(result).toContain(
|
|
27
|
-
"export const useCreateItemMutation = createItemMutation.useMutation"
|
|
28
|
-
)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it("generates hook correctly for mutation without args", () => {
|
|
32
|
-
const options: MutationHookOptions = {
|
|
33
|
-
canisterName: "my_canister",
|
|
34
|
-
method: methodNoArgs,
|
|
35
|
-
}
|
|
36
|
-
const result = generateMutationHook(options)
|
|
37
|
-
expect(result).toMatchSnapshot()
|
|
38
|
-
expect(result).toContain("createMutation(myCanisterReactor, {")
|
|
39
|
-
expect(result).toContain(
|
|
40
|
-
"export const executeInitSystem = initSystemMutation.execute"
|
|
41
|
-
)
|
|
42
|
-
})
|
|
43
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mutation hook template generator
|
|
3
|
-
*
|
|
4
|
-
* Generates createMutation-based hooks for canister update methods.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { MethodInfo } from "../types.js"
|
|
8
|
-
import { toPascalCase, getHookExportName, getReactorName } from "../naming.js"
|
|
9
|
-
|
|
10
|
-
export interface MutationHookOptions {
|
|
11
|
-
canisterName: string
|
|
12
|
-
method: MethodInfo
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Generate a mutation hook file content
|
|
17
|
-
*/
|
|
18
|
-
export function generateMutationHook(options: MutationHookOptions): string {
|
|
19
|
-
const { canisterName, method } = options
|
|
20
|
-
|
|
21
|
-
const pascalMethod = toPascalCase(method.name)
|
|
22
|
-
const reactorName = getReactorName(canisterName)
|
|
23
|
-
const hookExportName = getHookExportName(method.name, "mutation")
|
|
24
|
-
|
|
25
|
-
return `/**
|
|
26
|
-
* Mutation: ${method.name}
|
|
27
|
-
*
|
|
28
|
-
* Auto-generated by @ic-reactor/codegen
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* const { mutate, isPending } = ${hookExportName}.useMutation()
|
|
32
|
-
* mutate(${method.hasArgs ? "[arg1, arg2]" : "[]"})
|
|
33
|
-
*
|
|
34
|
-
* // Direct execution (outside React)
|
|
35
|
-
* const result = await ${hookExportName}.execute(${method.hasArgs ? "[arg1, arg2]" : "[]"})
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
import { createMutation } from "@ic-reactor/react"
|
|
39
|
-
import { ${reactorName} } from "../reactor"
|
|
40
|
-
|
|
41
|
-
export const ${hookExportName} = createMutation(${reactorName}, {
|
|
42
|
-
functionName: "${method.name}",
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
/** React hook for ${method.name} */
|
|
46
|
-
export const use${pascalMethod}Mutation = ${hookExportName}.useMutation
|
|
47
|
-
|
|
48
|
-
/** Execute ${method.name} directly (outside React) */
|
|
49
|
-
export const execute${pascalMethod} = ${hookExportName}.execute
|
|
50
|
-
`
|
|
51
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest"
|
|
2
|
-
import { generateQueryHook, QueryHookOptions } from "./query"
|
|
3
|
-
import type { MethodInfo } from "../types"
|
|
4
|
-
|
|
5
|
-
describe("Query Hook Generation", () => {
|
|
6
|
-
const methodWithArgs: MethodInfo = {
|
|
7
|
-
name: "get_user",
|
|
8
|
-
type: "query",
|
|
9
|
-
hasArgs: true,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const methodNoArgs: MethodInfo = {
|
|
13
|
-
name: "list_items",
|
|
14
|
-
type: "query",
|
|
15
|
-
hasArgs: false,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
it("generates hook for query with arguments (factory)", () => {
|
|
19
|
-
const options: QueryHookOptions = {
|
|
20
|
-
canisterName: "my_canister",
|
|
21
|
-
method: methodWithArgs,
|
|
22
|
-
}
|
|
23
|
-
expect(generateQueryHook(options)).toMatchSnapshot()
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it("generates hook for query without arguments (static)", () => {
|
|
27
|
-
const options: QueryHookOptions = {
|
|
28
|
-
canisterName: "my_canister",
|
|
29
|
-
method: methodNoArgs,
|
|
30
|
-
}
|
|
31
|
-
expect(generateQueryHook(options)).toMatchSnapshot()
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it("generates suspense hooks correctly", () => {
|
|
35
|
-
const options: QueryHookOptions = {
|
|
36
|
-
canisterName: "my_canister",
|
|
37
|
-
method: methodWithArgs,
|
|
38
|
-
type: "suspenseQuery",
|
|
39
|
-
}
|
|
40
|
-
const result = generateQueryHook(options)
|
|
41
|
-
expect(result).toMatchSnapshot()
|
|
42
|
-
expect(result).toContain("createSuspenseQueryFactory")
|
|
43
|
-
})
|
|
44
|
-
})
|
package/src/templates/query.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Query hook template generator
|
|
3
|
-
*
|
|
4
|
-
* Generates createQuery/createQueryFactory-based hooks for canister query methods.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { MethodInfo, HookType } from "../types.js"
|
|
8
|
-
import { getHookExportName, getReactorName } from "../naming.js"
|
|
9
|
-
|
|
10
|
-
export interface QueryHookOptions {
|
|
11
|
-
canisterName: string
|
|
12
|
-
method: MethodInfo
|
|
13
|
-
type?: HookType
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generate a query hook file content
|
|
18
|
-
*/
|
|
19
|
-
export function generateQueryHook(options: QueryHookOptions): string {
|
|
20
|
-
const { canisterName, method, type = "query" } = options
|
|
21
|
-
|
|
22
|
-
const reactorName = getReactorName(canisterName)
|
|
23
|
-
const hookExportName = getHookExportName(method.name, type)
|
|
24
|
-
|
|
25
|
-
const isSuspense = type === "suspenseQuery"
|
|
26
|
-
const creatorFn = isSuspense ? "createSuspenseQuery" : "createQuery"
|
|
27
|
-
const factoryFn = isSuspense
|
|
28
|
-
? "createSuspenseQueryFactory"
|
|
29
|
-
: "createQueryFactory"
|
|
30
|
-
const hookName = isSuspense ? "useSuspenseQuery" : "useQuery"
|
|
31
|
-
|
|
32
|
-
if (method.hasArgs) {
|
|
33
|
-
return `/**
|
|
34
|
-
* Query Factory: ${method.name}
|
|
35
|
-
*
|
|
36
|
-
* Auto-generated by @ic-reactor/codegen
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* const { data } = ${hookExportName}([arg1, arg2]).${hookName}()
|
|
40
|
-
* const data = await ${hookExportName}([arg1, arg2]).fetch()
|
|
41
|
-
* ${hookExportName}([arg1, arg2]).invalidate()
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
import { ${factoryFn} } from "@ic-reactor/react"
|
|
45
|
-
import { ${reactorName} } from "../reactor"
|
|
46
|
-
|
|
47
|
-
export const ${hookExportName} = ${factoryFn}(${reactorName}, {
|
|
48
|
-
functionName: "${method.name}",
|
|
49
|
-
})
|
|
50
|
-
`
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return `/**
|
|
54
|
-
* Query: ${method.name}
|
|
55
|
-
*
|
|
56
|
-
* Auto-generated by @ic-reactor/codegen
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* const { data } = ${hookExportName}.${hookName}()
|
|
60
|
-
* const data = await ${hookExportName}.fetch()
|
|
61
|
-
* ${hookExportName}.invalidate()
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
|
-
import { ${creatorFn} } from "@ic-reactor/react"
|
|
65
|
-
import { ${reactorName} } from "../reactor"
|
|
66
|
-
|
|
67
|
-
export const ${hookExportName} = ${creatorFn}(${reactorName}, {
|
|
68
|
-
functionName: "${method.name}",
|
|
69
|
-
})
|
|
70
|
-
`
|
|
71
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for IC Reactor code generation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Information about a canister method extracted from a .did file
|
|
7
|
-
*/
|
|
8
|
-
export interface MethodInfo {
|
|
9
|
-
/** Method name as defined in the Candid interface */
|
|
10
|
-
name: string
|
|
11
|
-
/** Whether this method is a query or update (mutation) */
|
|
12
|
-
type: "query" | "mutation"
|
|
13
|
-
/** Whether the method accepts arguments */
|
|
14
|
-
hasArgs: boolean
|
|
15
|
-
/** Human-readable description of the arguments */
|
|
16
|
-
argsDescription?: string
|
|
17
|
-
/** Human-readable description of the return type */
|
|
18
|
-
returnDescription?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Configuration for a single canister
|
|
23
|
-
*/
|
|
24
|
-
export interface CanisterConfig {
|
|
25
|
-
/** Name of the canister (used for variable naming) */
|
|
26
|
-
name?: string
|
|
27
|
-
/** Path to the .did file */
|
|
28
|
-
didFile: string
|
|
29
|
-
/** Output directory (default: ./src/canisters/<name>) */
|
|
30
|
-
outDir?: string
|
|
31
|
-
/** Use DisplayReactor for automatic type transformations (default: true) */
|
|
32
|
-
useDisplayReactor?: boolean
|
|
33
|
-
/**
|
|
34
|
-
* Path to import ClientManager from (relative to generated file).
|
|
35
|
-
* The file at this path should export: { clientManager: ClientManager }
|
|
36
|
-
* Default: "../../lib/client"
|
|
37
|
-
*/
|
|
38
|
-
clientManagerPath?: string
|
|
39
|
-
/** Custom canister ID (optional, uses environment by default) */
|
|
40
|
-
canisterId?: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Hook generation type
|
|
45
|
-
*/
|
|
46
|
-
export type HookType =
|
|
47
|
-
| "query"
|
|
48
|
-
| "suspenseQuery"
|
|
49
|
-
| "infiniteQuery"
|
|
50
|
-
| "suspenseInfiniteQuery"
|
|
51
|
-
| "mutation"
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Options for generating a hook file
|
|
55
|
-
*/
|
|
56
|
-
export interface GeneratorOptions {
|
|
57
|
-
canisterName: string
|
|
58
|
-
methodName: string
|
|
59
|
-
methodType: "query" | "mutation"
|
|
60
|
-
hasArgs: boolean
|
|
61
|
-
outDir: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Options for reactor file generation
|
|
66
|
-
*/
|
|
67
|
-
export interface ReactorGeneratorOptions {
|
|
68
|
-
canisterName: string
|
|
69
|
-
canisterConfig: CanisterConfig
|
|
70
|
-
/** Global default clientManagerPath */
|
|
71
|
-
globalClientManagerPath?: string
|
|
72
|
-
/** Whether declarations have been generated */
|
|
73
|
-
hasDeclarations?: boolean
|
|
74
|
-
/** Whether to generate advanced per-method hooks (default: false) */
|
|
75
|
-
advanced?: boolean
|
|
76
|
-
/** DID content (required when advanced=true) */
|
|
77
|
-
didContent?: string
|
|
78
|
-
}
|