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