oag_node_client/
generator.rs1use oag_core::config::{GeneratorConfig, GeneratorId, OutputLayout, SplitBy, ToolSetting};
2use oag_core::ir::IrSpec;
3use oag_core::{CodeGenerator, GeneratedFile, GeneratorError};
4
5use crate::emitters;
6use crate::emitters::scaffold::{NodeScaffoldConfig, ScaffoldOptions};
7
8pub struct NodeClientGenerator;
10
11impl NodeClientGenerator {
12 pub fn build_scaffold_options(
14 ir: &IrSpec,
15 config: &GeneratorConfig,
16 react: bool,
17 ) -> Option<ScaffoldOptions> {
18 let raw = config.scaffold.as_ref()?;
19 let scaffold: NodeScaffoldConfig = serde_json::from_value(raw.clone()).ok()?;
20 Some(ScaffoldOptions {
21 name: ir.info.title.clone(),
22 package_name: scaffold.package_name,
23 repository: scaffold.repository,
24 formatter: ToolSetting::resolve(scaffold.formatter.as_ref(), "biome").map(String::from),
25 test_runner: ToolSetting::resolve(scaffold.test_runner.as_ref(), "vitest")
26 .map(String::from),
27 bundler: ToolSetting::resolve(scaffold.bundler.as_ref(), "tsdown").map(String::from),
28 react,
29 })
30 }
31}
32
33impl CodeGenerator for NodeClientGenerator {
34 fn id(&self) -> GeneratorId {
35 GeneratorId::NodeClient
36 }
37
38 fn generate(
39 &self,
40 ir: &IrSpec,
41 config: &GeneratorConfig,
42 ) -> Result<Vec<GeneratedFile>, GeneratorError> {
43 let no_jsdoc = config.no_jsdoc.unwrap_or(false);
44 let scaffold_options = Self::build_scaffold_options(ir, config, false);
45
46 let mut files = match config.layout {
47 OutputLayout::Bundled => {
48 let content = emitters::bundled::emit_bundled(ir, no_jsdoc);
49 vec![GeneratedFile {
50 path: "index.ts".to_string(),
51 content,
52 }]
53 }
54 OutputLayout::Modular => {
55 vec![
56 GeneratedFile {
57 path: "types.ts".to_string(),
58 content: emitters::types::emit_types(ir),
59 },
60 GeneratedFile {
61 path: "sse.ts".to_string(),
62 content: emitters::sse::emit_sse(),
63 },
64 GeneratedFile {
65 path: "client.ts".to_string(),
66 content: emitters::client::emit_client(ir, no_jsdoc),
67 },
68 GeneratedFile {
69 path: "index.ts".to_string(),
70 content: emitters::index::emit_index(),
71 },
72 ]
73 }
74 OutputLayout::Split => {
75 let split_by = config.split_by.unwrap_or(SplitBy::Tag);
76 emitters::split::emit_split(ir, no_jsdoc, split_by)
77 }
78 };
79
80 if let Some(ref scaffold) = scaffold_options {
81 files.extend(emitters::scaffold::emit_scaffold(scaffold));
82
83 if scaffold.test_runner.is_some() {
84 files.push(GeneratedFile {
85 path: "client.test.ts".to_string(),
86 content: emitters::tests::emit_client_tests(ir),
87 });
88 }
89 }
90
91 Ok(files)
92 }
93}