Skip to content

Commit 33cd8eb

Browse files
committed
utils
1 parent f7a50ca commit 33cd8eb

File tree

9 files changed

+219
-254
lines changed

9 files changed

+219
-254
lines changed

packages/coda/src/bin/cli.ts

Lines changed: 4 additions & 254 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,10 @@
11
#!/usr/bin/env node
2-
import { access, readFile } from "node:fs/promises";
2+
import { writeFile } from "node:fs/promises";
33
import { resolve } from "node:path";
4-
import type { AnchorIdl, IdlV00, IdlV01 } from "@codama/nodes-from-anchor";
5-
import {
6-
programNodeFromAnchorV00,
7-
programNodeFromAnchorV01,
8-
} from "@codama/nodes-from-anchor";
94
import { renderESMTypeScriptVisitor } from "@macalinao/codama-renderers-js-esm";
105
import { renderMarkdownVisitor } from "@macalinao/codama-renderers-markdown";
11-
import { createFromRoot, rootNode } from "codama";
126
import { Command } from "commander";
13-
import { glob } from "glob";
14-
import type { CodaConfig } from "../config.js";
15-
16-
async function fileExists(path: string): Promise<boolean> {
17-
try {
18-
await access(path);
19-
return true;
20-
} catch {
21-
return false;
22-
}
23-
}
7+
import { fileExists, processIdls } from "../utils/index.js";
248

259
const program = new Command();
2610

@@ -50,126 +34,9 @@ program
5034
)
5135
.action(async (options: { idl: string; output: string; config: string }) => {
5236
try {
53-
const configPath = resolve(options.config);
54-
55-
// Load config first if it exists
56-
let config: CodaConfig = {};
57-
if (await fileExists(configPath)) {
58-
console.log(`Loading config from ${configPath}...`);
59-
60-
// Dynamic import of the config file
61-
const configUrl = new URL(`file://${configPath}`);
62-
const configModule = (await import(configUrl.href)) as {
63-
default: CodaConfig;
64-
};
65-
config = configModule.default;
66-
}
67-
68-
// Determine IDL paths - use config if provided, otherwise use command line option
69-
const idlPathInput = config.idlPath ?? options.idl;
70-
71-
// Process idlPath - can be string, array, or glob pattern
72-
let resolvedPaths: string[] = [];
73-
74-
if (Array.isArray(idlPathInput)) {
75-
// Handle array of paths (each could be a glob)
76-
for (const path of idlPathInput) {
77-
if (path.includes("*")) {
78-
// It's a glob pattern
79-
const matches = await glob(path);
80-
resolvedPaths.push(...matches.map((p) => resolve(p)));
81-
} else {
82-
// Regular path
83-
resolvedPaths.push(resolve(path));
84-
}
85-
}
86-
}
87-
// Single path (could be glob)
88-
else if (idlPathInput.includes("*")) {
89-
// It's a glob pattern
90-
const matches = await glob(idlPathInput);
91-
resolvedPaths = matches.map((p) => resolve(p));
92-
} else {
93-
// Regular path
94-
resolvedPaths = [resolve(idlPathInput)];
95-
}
96-
97-
// Remove duplicates and sort for consistent ordering
98-
resolvedPaths = [...new Set(resolvedPaths)].sort();
99-
100-
if (resolvedPaths.length === 0) {
101-
console.error(
102-
"Error: No IDL files found matching the specified pattern(s)",
103-
);
104-
process.exit(1);
105-
}
106-
37+
const { codama, config } = await processIdls(options);
10738
const outputPath = resolve(config.outputDir ?? options.output);
10839

109-
// Load all IDLs
110-
const idls: AnchorIdl[] = [];
111-
for (const idlPath of resolvedPaths) {
112-
// Check if IDL file exists
113-
if (!(await fileExists(idlPath))) {
114-
console.error(`Error: IDL file not found at ${idlPath}`);
115-
process.exit(1);
116-
}
117-
118-
// Load the IDL
119-
console.log(`Loading IDL from ${idlPath}...`);
120-
const idlContent = await readFile(idlPath, "utf-8");
121-
const idl = JSON.parse(idlContent) as AnchorIdl;
122-
idls.push(idl);
123-
}
124-
125-
// Ensure we have at least one IDL
126-
if (idls.length === 0) {
127-
console.error("Error: No IDL files were loaded");
128-
process.exit(1);
129-
}
130-
131-
// Create root node from Anchor IDL(s)
132-
console.log(
133-
`Creating Codama nodes from ${idls.length.toString()} Anchor IDL(s)...`,
134-
);
135-
136-
// Create root nodes from IDL(s)
137-
// Note: rootNodeFromAnchor can accept additional IDLs as external programs
138-
const programNodes = idls.map((idl) => {
139-
if (
140-
idl.metadata &&
141-
"spec" in idl.metadata &&
142-
idl.metadata.spec === "0.1.0"
143-
) {
144-
return programNodeFromAnchorV01(idl as unknown as IdlV01);
145-
}
146-
return programNodeFromAnchorV00(idl as unknown as IdlV00);
147-
});
148-
const [firstProgramNode, ...restProgramNodes] = programNodes;
149-
if (!firstProgramNode) {
150-
throw new Error("Unexpected: No program nodes loaded");
151-
}
152-
const root = rootNode(firstProgramNode, restProgramNodes);
153-
const codama = createFromRoot(root);
154-
155-
// Apply additional visitors from config
156-
if (config.visitors) {
157-
// Resolve visitors - either array or function
158-
const visitors =
159-
typeof config.visitors === "function"
160-
? config.visitors({ idls })
161-
: config.visitors;
162-
163-
if (visitors.length > 0) {
164-
console.log(
165-
`Applying ${visitors.length.toLocaleString()} custom visitor(s)...`,
166-
);
167-
for (const visitor of visitors) {
168-
codama.update(visitor);
169-
}
170-
}
171-
}
172-
17340
// Apply the ESM TypeScript visitor
17441
console.log(`Generating client to ${outputPath}...`);
17542
codama.accept(renderESMTypeScriptVisitor(outputPath));
@@ -231,7 +98,6 @@ export default {
23198
`;
23299

233100
// Write config file
234-
const { writeFile } = await import("node:fs/promises");
235101
await writeFile(configPath, configTemplate, "utf-8");
236102

237103
console.log(`✅ Created coda.config.mjs at ${configPath}`);
@@ -261,125 +127,9 @@ program
261127
)
262128
.action(async (options: { idl: string; config: string }) => {
263129
try {
264-
const configPath = resolve(options.config);
265-
266-
// Load config first if it exists
267-
let config: CodaConfig = {};
268-
if (await fileExists(configPath)) {
269-
console.log(`Loading config from ${configPath}...`);
270-
271-
// Dynamic import of the config file
272-
const configUrl = new URL(`file://${configPath}`);
273-
const configModule = (await import(configUrl.href)) as {
274-
default: CodaConfig;
275-
};
276-
config = configModule.default;
277-
}
278-
279-
// Determine IDL paths - use config if provided, otherwise use command line option
280-
const idlPathInput = config.idlPath ?? options.idl;
281-
282-
// Process idlPath - can be string, array, or glob pattern
283-
let resolvedPaths: string[] = [];
284-
285-
if (Array.isArray(idlPathInput)) {
286-
// Handle array of paths (each could be a glob)
287-
for (const path of idlPathInput) {
288-
if (path.includes("*")) {
289-
// It's a glob pattern
290-
const matches = await glob(path);
291-
resolvedPaths.push(...matches.map((p) => resolve(p)));
292-
} else {
293-
// Regular path
294-
resolvedPaths.push(resolve(path));
295-
}
296-
}
297-
}
298-
// Single path (could be glob)
299-
else if (idlPathInput.includes("*")) {
300-
// It's a glob pattern
301-
const matches = await glob(idlPathInput);
302-
resolvedPaths = matches.map((p) => resolve(p));
303-
} else {
304-
// Regular path
305-
resolvedPaths = [resolve(idlPathInput)];
306-
}
307-
308-
// Remove duplicates and sort for consistent ordering
309-
resolvedPaths = [...new Set(resolvedPaths)].sort();
310-
311-
if (resolvedPaths.length === 0) {
312-
console.error(
313-
"Error: No IDL files found matching the specified pattern(s)",
314-
);
315-
process.exit(1);
316-
}
317-
130+
const { codama, config } = await processIdls(options);
318131
const outputPath = resolve(config.docsPath ?? "./docs");
319132

320-
// Load all IDLs
321-
const idls: AnchorIdl[] = [];
322-
for (const idlPath of resolvedPaths) {
323-
// Check if IDL file exists
324-
if (!(await fileExists(idlPath))) {
325-
console.error(`Error: IDL file not found at ${idlPath}`);
326-
process.exit(1);
327-
}
328-
329-
// Load the IDL
330-
console.log(`Loading IDL from ${idlPath}...`);
331-
const idlContent = await readFile(idlPath, "utf-8");
332-
const idl = JSON.parse(idlContent) as AnchorIdl;
333-
idls.push(idl);
334-
}
335-
336-
// Ensure we have at least one IDL
337-
if (idls.length === 0) {
338-
console.error("Error: No IDL files were loaded");
339-
process.exit(1);
340-
}
341-
342-
// Create root node from Anchor IDL(s)
343-
console.log(
344-
`Creating Codama nodes from ${idls.length.toString()} Anchor IDL(s)...`,
345-
);
346-
347-
// Create root nodes from IDL(s)
348-
const programNodes = idls.map((idl) => {
349-
if (
350-
idl.metadata &&
351-
"spec" in idl.metadata &&
352-
idl.metadata.spec === "0.1.0"
353-
) {
354-
return programNodeFromAnchorV01(idl as unknown as IdlV01);
355-
}
356-
return programNodeFromAnchorV00(idl as unknown as IdlV00);
357-
});
358-
const [firstProgramNode, ...restProgramNodes] = programNodes;
359-
if (!firstProgramNode) {
360-
throw new Error("Unexpected: No program nodes loaded");
361-
}
362-
const root = rootNode(firstProgramNode, restProgramNodes);
363-
const codama = createFromRoot(root);
364-
365-
// Apply additional visitors from config
366-
if (config.visitors) {
367-
// Resolve visitors - either array or function
368-
const visitors =
369-
typeof config.visitors === "function"
370-
? config.visitors({ idls })
371-
: config.visitors;
372-
373-
if (visitors.length > 0) {
374-
console.log(
375-
`Applying ${visitors.length.toLocaleString()} custom visitor(s)...`,
376-
);
377-
for (const visitor of visitors) {
378-
codama.update(visitor);
379-
}
380-
}
381-
}
382-
383133
// Apply the markdown visitor
384134
console.log(`Generating documentation to ${outputPath}...`);
385135
codama.accept(renderMarkdownVisitor(outputPath));
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { AnchorIdl } from "@codama/nodes-from-anchor";
2+
import type { Codama } from "codama";
3+
import type { CodaConfig } from "../config.js";
4+
5+
/**
6+
* Apply custom visitors from configuration
7+
*/
8+
export function applyCustomVisitors(
9+
codama: Codama,
10+
config: CodaConfig,
11+
idls: AnchorIdl[],
12+
): void {
13+
if (config.visitors) {
14+
// Resolve visitors - either array or function
15+
const visitors =
16+
typeof config.visitors === "function"
17+
? config.visitors({ idls })
18+
: config.visitors;
19+
20+
if (visitors.length > 0) {
21+
console.log(
22+
`Applying ${visitors.length.toLocaleString()} custom visitor(s)...`,
23+
);
24+
for (const visitor of visitors) {
25+
codama.update(visitor);
26+
}
27+
}
28+
}
29+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { AnchorIdl, IdlV00, IdlV01 } from "@codama/nodes-from-anchor";
2+
import {
3+
programNodeFromAnchorV00,
4+
programNodeFromAnchorV01,
5+
} from "@codama/nodes-from-anchor";
6+
import type { Codama } from "codama";
7+
import { createFromRoot, rootNode } from "codama";
8+
9+
/**
10+
* Create a Codama instance from IDLs
11+
*/
12+
export function createCodamaFromIdls(idls: AnchorIdl[]): Codama {
13+
if (idls.length === 0) {
14+
throw new Error("No IDL files were loaded");
15+
}
16+
17+
console.log(
18+
`Creating Codama nodes from ${idls.length.toString()} Anchor IDL(s)...`,
19+
);
20+
21+
// Create program nodes from IDLs
22+
const programNodes = idls.map((idl) => {
23+
if (
24+
idl.metadata &&
25+
"spec" in idl.metadata &&
26+
idl.metadata.spec === "0.1.0"
27+
) {
28+
return programNodeFromAnchorV01(idl as unknown as IdlV01);
29+
}
30+
return programNodeFromAnchorV00(idl as unknown as IdlV00);
31+
});
32+
33+
const [firstProgramNode, ...restProgramNodes] = programNodes;
34+
if (!firstProgramNode) {
35+
throw new Error("Unexpected: No program nodes loaded");
36+
}
37+
38+
const root = rootNode(firstProgramNode, restProgramNodes);
39+
return createFromRoot(root);
40+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { access } from "node:fs/promises";
2+
3+
/**
4+
* Check if a file exists at the given path
5+
*/
6+
export async function fileExists(path: string): Promise<boolean> {
7+
try {
8+
await access(path);
9+
return true;
10+
} catch {
11+
return false;
12+
}
13+
}

packages/coda/src/utils/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export { applyCustomVisitors } from "./apply-custom-visitors.js";
2+
export { createCodamaFromIdls } from "./create-codama-from-idls.js";
3+
export { fileExists } from "./file-exists.js";
4+
export { loadConfig } from "./load-config.js";
5+
export { loadIdls } from "./load-idls.js";
6+
export { processIdls } from "./process-idls.js";
7+
export { resolveIdlPaths } from "./resolve-idl-paths.js";

0 commit comments

Comments
 (0)