create-codama-clients 1.0.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 ADDED
@@ -0,0 +1,90 @@
1
+ # create-codama-clients
2
+
3
+ Zero config, ero installation tool that makes TypeScript clients from your Solana program IDLs.
4
+
5
+ Just run `npx create-codama-clients`. This package will download, find all IDL files in your project, and generate TypeScript clients for all your programs with one command.
6
+
7
+ ## The problem this solves
8
+
9
+ The Codama CLI (`codama run`) requires:
10
+
11
+ - A `codama.json` config file for each program
12
+ - Manual configuration of IDL paths and output directories
13
+ - Separate commands or shell scripts to handle multiple programs
14
+
15
+ With `create-codama-clients`, you can generate clients for all your programs in one command, with zero configuration.
16
+
17
+ ## Usage
18
+
19
+ ### Basic usage (recommended)
20
+
21
+ ```bash
22
+ npx create-codama-clients
23
+ ```
24
+
25
+ This will:
26
+
27
+ 1. Look for IDL files in `target/idl/` (Anchor's default location)
28
+ 2. Generate a TypeScript client for each IDL found
29
+ 3. Output clients to `dist/<idl-name>-client/` folders (e.g., `dist/election-client/`)
30
+
31
+ ### Custom directories
32
+
33
+ ```bash
34
+ npx create-codama-clients --idl-dir ./idls --output-dir ./clients
35
+ ```
36
+
37
+ ### Options
38
+
39
+ ```
40
+ -i, --idl-dir <dir> Directory containing IDL files (default: target/idl)
41
+ -o, --output-dir <dir> Directory for generated clients (default: dist)
42
+ -h, --help Show this help message
43
+ ```
44
+
45
+ ## Example output
46
+
47
+ ```
48
+ šŸ” Searching for IDL files in target/idl...
49
+ šŸ“¦ Found 2 IDL file(s)
50
+ āœ… Generated client for election at dist/election-client
51
+ āœ… Generated client for voting at dist/voting-client
52
+
53
+ šŸŽ‰ Successfully generated 2 client(s)
54
+ ```
55
+
56
+ Each generated client folder (e.g., `dist/election-client/`) contains:
57
+
58
+ - TypeScript types for your program's accounts and instructions
59
+ - Helper functions for interacting with your program
60
+ - Full compatibility with `@solana/kit`
61
+
62
+ ## Requirements
63
+
64
+ Your project should have IDL files (JSON format) from:
65
+
66
+ - Anchor programs (`anchor build` generates these in `target/idl/`)
67
+ - Shank-based programs
68
+ - Any other Codama-compatible IDL format
69
+
70
+ ## How it works
71
+
72
+ For each IDL file found:
73
+
74
+ 1. Loads the IDL JSON
75
+ 2. Converts it to a Codama root node using `@codama/nodes-from-anchor`
76
+ 3. Generates TypeScript client code using `@codama/renderers-js`
77
+ 4. Outputs to `<output-dir>/<idl-filename>-client/` (the `-client` suffix is automatically appended)
78
+
79
+ ## Comparison with Codama CLI
80
+
81
+ | Feature | `create-codama-clients` | `codama run` |
82
+ | ----------------- | ----------------------- | ------------------------- |
83
+ | Installation | None (npx) | Requires local install |
84
+ | Configuration | Zero config | Requires codama.json |
85
+ | Multiple programs | Automatic | Manual config per program |
86
+ | Discovery | Auto-finds IDLs | Manual paths |
87
+
88
+ ## License
89
+
90
+ ISC
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import { createFromRoot } from "codama";
3
+ import { rootNodeFromAnchor } from "@codama/nodes-from-anchor";
4
+ import { renderVisitor } from "@codama/renderers-js";
5
+ import path from "path";
6
+ import { promises as fs } from "fs";
7
+ import { glob } from "glob";
8
+ const parseArgs = () => {
9
+ const args = process.argv.slice(2);
10
+ const options = {
11
+ idlDir: "target/idl",
12
+ outputDir: "dist",
13
+ };
14
+ for (let i = 0; i < args.length; i++) {
15
+ const arg = args[i];
16
+ if (!arg)
17
+ continue;
18
+ if (arg === "--idl-dir" || arg === "-i") {
19
+ const nextArg = args[i + 1];
20
+ if (!nextArg) {
21
+ throw new Error(`${arg} requires a value`);
22
+ }
23
+ options.idlDir = nextArg;
24
+ i++;
25
+ }
26
+ else if (arg === "--output-dir" || arg === "-o") {
27
+ const nextArg = args[i + 1];
28
+ if (!nextArg) {
29
+ throw new Error(`${arg} requires a value`);
30
+ }
31
+ options.outputDir = nextArg;
32
+ i++;
33
+ }
34
+ else if (arg === "--help" || arg === "-h") {
35
+ console.log(`
36
+ Usage: npx create-codama-clients [options]
37
+
38
+ Options:
39
+ -i, --idl-dir <dir> Directory containing IDL files (default: target/idl)
40
+ -o, --output-dir <dir> Directory for generated clients (default: dist)
41
+ -h, --help Show this help message
42
+
43
+ Examples:
44
+ npx create-codama-clients
45
+ npx create-codama-clients --idl-dir ./idls --output-dir ./clients
46
+ `);
47
+ process.exit(0);
48
+ }
49
+ else {
50
+ throw new Error(`Unknown option: ${arg}`);
51
+ }
52
+ }
53
+ return options;
54
+ };
55
+ const findIDLFiles = async (idlDir) => {
56
+ const pattern = path.join(idlDir, "*.json");
57
+ const files = await glob(pattern);
58
+ if (files.length === 0) {
59
+ throw new Error(`No JSON files found in ${idlDir}`);
60
+ }
61
+ return files;
62
+ };
63
+ const loadIDL = async (filePath) => {
64
+ try {
65
+ const content = await fs.readFile(filePath, "utf-8");
66
+ return JSON.parse(content);
67
+ }
68
+ catch (error) {
69
+ throw new Error(`Failed to load IDL from ${filePath}: ${error}`);
70
+ }
71
+ };
72
+ const generateClient = async (idlPath, outputDir) => {
73
+ const idl = await loadIDL(idlPath);
74
+ const fileName = path.basename(idlPath, ".json");
75
+ const clientOutputPath = path.join(outputDir, `${fileName}-client`);
76
+ const codama = createFromRoot(rootNodeFromAnchor(idl));
77
+ codama.accept(renderVisitor(clientOutputPath));
78
+ console.log(`āœ… Generated client for ${fileName} at ${clientOutputPath}`);
79
+ };
80
+ const main = async () => {
81
+ try {
82
+ const options = parseArgs();
83
+ console.log(`šŸ” Searching for IDL files in ${options.idlDir}...`);
84
+ const idlFiles = await findIDLFiles(options.idlDir);
85
+ console.log(`šŸ“¦ Found ${idlFiles.length} IDL file(s)`);
86
+ for (const idlFile of idlFiles) {
87
+ await generateClient(idlFile, options.outputDir);
88
+ }
89
+ console.log(`\nšŸŽ‰ Successfully generated ${idlFiles.length} client(s)`);
90
+ }
91
+ catch (error) {
92
+ console.error(`āŒ Error: ${error instanceof Error ? error.message : error}`);
93
+ process.exit(1);
94
+ }
95
+ };
96
+ main();
97
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "create-codama-clients",
3
+ "version": "1.0.0",
4
+ "description": "Auto-discover and batch-generate Codama TypeScript clients from Solana program IDLs",
5
+ "main": "dist/cli.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-codama-clients": "./dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "solana",
16
+ "codama",
17
+ "idl",
18
+ "client-generation",
19
+ "anchor",
20
+ "typescript"
21
+ ],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "@types/node": "^25.0.9",
26
+ "typescript": "^5.9.3"
27
+ },
28
+ "dependencies": {
29
+ "@codama/nodes-from-anchor": "^1.3.8",
30
+ "@codama/renderers-js": "^1.5.5",
31
+ "codama": "^1.5.0",
32
+ "glob": "^13.0.0"
33
+ }
34
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createFromRoot } from "codama";
4
+ import { rootNodeFromAnchor, type AnchorIdl } from "@codama/nodes-from-anchor";
5
+ import { renderVisitor } from "@codama/renderers-js";
6
+ import path from "path";
7
+ import { promises as fs } from "fs";
8
+ import { glob } from "glob";
9
+
10
+ interface CLIOptions {
11
+ idlDir: string;
12
+ outputDir: string;
13
+ }
14
+
15
+ const parseArgs = (): CLIOptions => {
16
+ const args = process.argv.slice(2);
17
+ const options: CLIOptions = {
18
+ idlDir: "target/idl",
19
+ outputDir: "dist",
20
+ };
21
+
22
+ for (let i = 0; i < args.length; i++) {
23
+ const arg = args[i];
24
+ if (!arg) continue;
25
+
26
+ if (arg === "--idl-dir" || arg === "-i") {
27
+ const nextArg = args[i + 1];
28
+ if (!nextArg) {
29
+ throw new Error(`${arg} requires a value`);
30
+ }
31
+ options.idlDir = nextArg;
32
+ i++;
33
+ } else if (arg === "--output-dir" || arg === "-o") {
34
+ const nextArg = args[i + 1];
35
+ if (!nextArg) {
36
+ throw new Error(`${arg} requires a value`);
37
+ }
38
+ options.outputDir = nextArg;
39
+ i++;
40
+ } else if (arg === "--help" || arg === "-h") {
41
+ console.log(`
42
+ Usage: npx create-codama-clients [options]
43
+
44
+ Options:
45
+ -i, --idl-dir <dir> Directory containing IDL files (default: target/idl)
46
+ -o, --output-dir <dir> Directory for generated clients (default: dist)
47
+ -h, --help Show this help message
48
+
49
+ Examples:
50
+ npx create-codama-clients
51
+ npx create-codama-clients --idl-dir ./idls --output-dir ./clients
52
+ `);
53
+ process.exit(0);
54
+ } else {
55
+ throw new Error(`Unknown option: ${arg}`);
56
+ }
57
+ }
58
+
59
+ return options;
60
+ };
61
+
62
+ const findIDLFiles = async (idlDir: string): Promise<Array<string>> => {
63
+ const pattern = path.join(idlDir, "*.json");
64
+ const files = await glob(pattern);
65
+
66
+ if (files.length === 0) {
67
+ throw new Error(`No JSON files found in ${idlDir}`);
68
+ }
69
+
70
+ return files;
71
+ };
72
+
73
+ const loadIDL = async (filePath: string): Promise<AnchorIdl> => {
74
+ try {
75
+ const content = await fs.readFile(filePath, "utf-8");
76
+ return JSON.parse(content) as AnchorIdl;
77
+ } catch (error) {
78
+ throw new Error(`Failed to load IDL from ${filePath}: ${error}`);
79
+ }
80
+ };
81
+
82
+ const generateClient = async (
83
+ idlPath: string,
84
+ outputDir: string
85
+ ): Promise<void> => {
86
+ const idl = await loadIDL(idlPath);
87
+ const fileName = path.basename(idlPath, ".json");
88
+ const clientOutputPath = path.join(outputDir, `${fileName}-client`);
89
+
90
+ const codama = createFromRoot(rootNodeFromAnchor(idl));
91
+ codama.accept(renderVisitor(clientOutputPath));
92
+
93
+ console.log(`āœ… Generated client for ${fileName} at ${clientOutputPath}`);
94
+ };
95
+
96
+ const main = async (): Promise<void> => {
97
+ try {
98
+ const options = parseArgs();
99
+
100
+ console.log(`šŸ” Searching for IDL files in ${options.idlDir}...`);
101
+ const idlFiles = await findIDLFiles(options.idlDir);
102
+ console.log(`šŸ“¦ Found ${idlFiles.length} IDL file(s)`);
103
+
104
+ for (const idlFile of idlFiles) {
105
+ await generateClient(idlFile, options.outputDir);
106
+ }
107
+
108
+ console.log(`\nšŸŽ‰ Successfully generated ${idlFiles.length} client(s)`);
109
+ } catch (error) {
110
+ console.error(`āŒ Error: ${error instanceof Error ? error.message : error}`);
111
+ process.exit(1);
112
+ }
113
+ };
114
+
115
+ main();
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "./src",
4
+ "outDir": "./dist",
5
+ "module": "nodenext",
6
+ "target": "esnext",
7
+ "lib": ["esnext"],
8
+ "types": ["node"],
9
+ "sourceMap": true,
10
+ "declaration": true,
11
+ "declarationMap": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "exactOptionalPropertyTypes": true,
14
+ "strict": true,
15
+ "verbatimModuleSyntax": true,
16
+ "isolatedModules": true,
17
+ "noUncheckedSideEffectImports": true,
18
+ "moduleDetection": "force",
19
+ "skipLibCheck": true
20
+ },
21
+ "include": ["src/**/*"]
22
+ }