|
1 | 1 | #!/usr/bin/env node |
2 | | -import { access, readFile } from "node:fs/promises"; |
| 2 | +import { writeFile } from "node:fs/promises"; |
3 | 3 | 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"; |
9 | 4 | import { renderESMTypeScriptVisitor } from "@macalinao/codama-renderers-js-esm"; |
10 | 5 | import { renderMarkdownVisitor } from "@macalinao/codama-renderers-markdown"; |
11 | | -import { createFromRoot, rootNode } from "codama"; |
12 | 6 | 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"; |
24 | 8 |
|
25 | 9 | const program = new Command(); |
26 | 10 |
|
@@ -50,126 +34,9 @@ program |
50 | 34 | ) |
51 | 35 | .action(async (options: { idl: string; output: string; config: string }) => { |
52 | 36 | 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); |
107 | 38 | const outputPath = resolve(config.outputDir ?? options.output); |
108 | 39 |
|
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 | | - |
173 | 40 | // Apply the ESM TypeScript visitor |
174 | 41 | console.log(`Generating client to ${outputPath}...`); |
175 | 42 | codama.accept(renderESMTypeScriptVisitor(outputPath)); |
@@ -231,7 +98,6 @@ export default { |
231 | 98 | `; |
232 | 99 |
|
233 | 100 | // Write config file |
234 | | - const { writeFile } = await import("node:fs/promises"); |
235 | 101 | await writeFile(configPath, configTemplate, "utf-8"); |
236 | 102 |
|
237 | 103 | console.log(`✅ Created coda.config.mjs at ${configPath}`); |
@@ -261,125 +127,9 @@ program |
261 | 127 | ) |
262 | 128 | .action(async (options: { idl: string; config: string }) => { |
263 | 129 | 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); |
318 | 131 | const outputPath = resolve(config.docsPath ?? "./docs"); |
319 | 132 |
|
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 | | - |
383 | 133 | // Apply the markdown visitor |
384 | 134 | console.log(`Generating documentation to ${outputPath}...`); |
385 | 135 | codama.accept(renderMarkdownVisitor(outputPath)); |
|
0 commit comments