Skip to main content

oag_node_client/emitters/
split.rs

1use oag_core::GeneratedFile;
2use oag_core::config::SplitBy;
3use oag_core::ir::{IrSpec, OperationGroup, group_operations};
4
5use crate::emitters;
6
7/// Emit files for split layout mode.
8///
9/// Produces:
10/// - `types.ts` — centralized types (same as modular)
11/// - `_client-base.ts` — the ApiClient class with the private `request` method
12/// - `{group}.ts` — per-group files with standalone functions
13/// - `sse.ts` — SSE runtime (same as modular)
14/// - `index.ts` — barrel re-export
15pub fn emit_split(ir: &IrSpec, no_jsdoc: bool, split_by: SplitBy) -> Vec<GeneratedFile> {
16    let groups = group_operations(ir, split_by);
17    let mut files = Vec::new();
18
19    // Centralized types
20    files.push(GeneratedFile {
21        path: "types.ts".to_string(),
22        content: emitters::types::emit_types(ir),
23    });
24
25    // SSE runtime
26    files.push(GeneratedFile {
27        path: "sse.ts".to_string(),
28        content: emitters::sse::emit_sse(),
29    });
30
31    // Client base — full client class
32    files.push(GeneratedFile {
33        path: "client.ts".to_string(),
34        content: emitters::client::emit_client(ir, no_jsdoc),
35    });
36
37    // Per-group files — re-export from client for the group's operations
38    let mut group_names = Vec::new();
39    for group in &groups {
40        let group_file_name = format!("{}.ts", group.name.snake_case);
41        let content = emit_group_file(ir, group);
42        group_names.push(group.name.snake_case.clone());
43        files.push(GeneratedFile {
44            path: group_file_name,
45            content,
46        });
47    }
48
49    // Index barrel
50    files.push(GeneratedFile {
51        path: "index.ts".to_string(),
52        content: emit_split_index(&group_names),
53    });
54
55    files
56}
57
58/// Emit a per-group file that re-exports the relevant operations from the client.
59fn emit_group_file(ir: &IrSpec, group: &OperationGroup) -> String {
60    let mut lines = Vec::new();
61    lines.push("// Auto-generated by oag — do not edit".to_string());
62    lines.push(format!("// Operations group: {}", group.name.original));
63    lines.push(String::new());
64
65    // Collect the operation names for this group
66    let op_names: Vec<&str> = group
67        .operation_indices
68        .iter()
69        .map(|&i| ir.operations[i].name.camel_case.as_str())
70        .collect();
71
72    lines.push("// This group contains the following operations:".to_string());
73    for name in &op_names {
74        lines.push(format!("//   - {name}"));
75    }
76    lines.push(String::new());
77    lines.push("// Import the client and call the relevant methods:".to_string());
78    lines.push("// import { ApiClient } from \"./client\";".to_string());
79    lines.push(String::new());
80    lines.push("export { ApiClient } from \"./client\";".to_string());
81    lines.push("export * from \"./types\";".to_string());
82
83    lines.join("\n") + "\n"
84}
85
86/// Emit the barrel index for split mode.
87fn emit_split_index(group_names: &[String]) -> String {
88    let mut lines = vec![
89        "// Auto-generated by oag — do not edit".to_string(),
90        "export * from \"./types\";".to_string(),
91        "export { ApiClient, type ClientConfig, type RequestOptions } from \"./client\";"
92            .to_string(),
93        "export { streamSse, SSEError, type SSEOptions } from \"./sse\";".to_string(),
94    ];
95
96    for name in group_names {
97        lines.push(format!("export * from \"./{name}\";"));
98    }
99
100    lines.join("\n") + "\n"
101}