oag_node_client/emitters/
bundled.rs1use oag_core::ir::IrSpec;
2
3use crate::emitters;
4
5pub fn emit_bundled(ir: &IrSpec, no_jsdoc: bool) -> String {
8 let types_content = emitters::types::emit_types(ir);
9 let sse_content = emitters::sse::emit_sse();
10 let client_content = emitters::client::emit_client(ir, no_jsdoc);
11
12 let mut output = String::new();
13 output.push_str("// Auto-generated by oag — do not edit (bundled)\n\n");
14
15 output.push_str("// === Types ===\n\n");
17 output.push_str(&strip_auto_generated_header(&types_content));
18 output.push('\n');
19
20 output.push_str("// === SSE Runtime ===\n\n");
22 output.push_str(&strip_auto_generated_header(&sse_content));
23 output.push('\n');
24
25 output.push_str("// === Client ===\n\n");
27 let client_stripped = strip_relative_imports(&strip_auto_generated_header(&client_content));
28 output.push_str(&client_stripped);
29
30 output
31}
32
33fn strip_auto_generated_header(content: &str) -> String {
35 content
36 .lines()
37 .filter(|line| !line.starts_with("// Auto-generated by oag"))
38 .collect::<Vec<_>>()
39 .join("\n")
40}
41
42fn strip_relative_imports(content: &str) -> String {
44 let lines: Vec<&str> = content.lines().collect();
45 let mut result = Vec::new();
46 let mut i = 0;
47
48 let is_relative = |s: &str| {
49 s.contains("\"./types\"") || s.contains("\"./sse\"") || s.contains("\"./client\"")
50 };
51
52 while i < lines.len() {
53 let trimmed = lines[i].trim();
54
55 if trimmed.starts_with("import ") || trimmed.starts_with("import type ") {
56 if trimmed.ends_with(';') {
58 if is_relative(trimmed) {
59 i += 1;
60 continue;
61 }
62 result.push(lines[i]);
63 i += 1;
64 continue;
65 }
66
67 let start = i;
69 i += 1;
70 let mut found_close = false;
71 while i < lines.len() {
72 let t = lines[i].trim();
73 if t.starts_with("} from ") {
74 found_close = true;
75 if is_relative(t) {
76 i += 1;
78 } else {
79 result.extend_from_slice(&lines[start..=i]);
81 i += 1;
82 }
83 break;
84 }
85 i += 1;
86 }
87 if !found_close {
88 result.extend_from_slice(&lines[start..i]);
90 }
91 continue;
92 }
93
94 result.push(lines[i]);
95 i += 1;
96 }
97
98 result.join("\n")
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_strip_relative_imports() {
107 let content = r#"import type {
108 Pet,
109 NewPet,
110} from "./types";
111import { streamSse, type SSEOptions } from "./sse";
112
113export class ApiClient {"#;
114
115 let stripped = strip_relative_imports(content);
116 assert!(!stripped.contains("from \"./types\""));
117 assert!(!stripped.contains("from \"./sse\""));
118 assert!(stripped.contains("export class ApiClient {"));
119 }
120
121 #[test]
122 fn test_strip_auto_generated_header() {
123 let content = "// Auto-generated by oag — do not edit\nexport interface Foo {}";
124 let stripped = strip_auto_generated_header(content);
125 assert!(!stripped.contains("Auto-generated"));
126 assert!(stripped.contains("export interface Foo {}"));
127 }
128}