1use clap::{Parser, ValueEnum};
2use markodown::{transform, ComponentImports, OutputFormat};
3use oxc_diagnostics::GraphicalReportHandler;
4use std::fs;
5use std::path::PathBuf;
6use std::process;
7
8#[derive(Debug, Clone, Copy, ValueEnum)]
9enum CliOutputFormat {
10 Html,
11 Marko,
12}
13
14#[derive(Parser)]
15#[command(
16 name = "markodown",
17 about = "A markdown compiler with custom extensions"
18)]
19struct Cli {
20 /// Path to input markodown source
21 file: PathBuf,
22
23 /// Force the output format (auto-detected by default)
24 #[arg(long)]
25 force_format: Option<CliOutputFormat>,
26
27 /// Layout component to wrap the output with. This component will recieve
28 /// the module and a generated table of contents.
29 #[arg(long)]
30 layout_import: Option<String>,
31
32 /// Replace heading elements (h1-h6) with a custom component
33 #[arg(long)]
34 heading_import: Option<String>,
35
36 /// Replace code blocks with a custom component
37 #[arg(long)]
38 code_block_import: Option<String>,
39
40 /// Replace link elements with a custom component
41 #[arg(long)]
42 link_import: Option<String>,
43
44 /// Replace image elements with a custom component
45 #[arg(long)]
46 image_import: Option<String>,
47
48 /// Replace blockquote elements with a custom component
49 #[arg(long)]
50 blockquote_import: Option<String>,
51
52 /// Disable all Marko extensions, treating input as pure Markdown
53 #[arg(long)]
54 markdown_only: bool,
55}
56
57fn main() {
58 let cli = Cli::parse();
59
60 let force = cli.force_format.map(|f| match f {
61 CliOutputFormat::Html => OutputFormat::Html,
62 CliOutputFormat::Marko => OutputFormat::Marko,
63 });
64
65 let layout_import = cli.layout_import;
66
67 // Build component imports from CLI flags
68 let component_imports = ComponentImports {
69 heading: cli.heading_import,
70 code_block: cli.code_block_import,
71 link: cli.link_import,
72 image: cli.image_import,
73 blockquote: cli.blockquote_import,
74 };
75 let component_imports = if component_imports.heading.is_some()
76 || component_imports.code_block.is_some()
77 || component_imports.link.is_some()
78 || component_imports.image.is_some()
79 || component_imports.blockquote.is_some()
80 {
81 Some(component_imports)
82 } else {
83 None
84 };
85
86 let source = match fs::read_to_string(&cli.file) {
87 Ok(s) => s,
88 Err(e) => {
89 eprintln!("error: failed to read {}: {e}", cli.file.display());
90 process::exit(1);
91 }
92 };
93
94 match transform(
95 &source,
96 force,
97 layout_import,
98 component_imports,
99 None,
100 cli.markdown_only,
101 None, // clover_extensions not exposed via CLI
102 ) {
103 Err(errors) => {
104 let handler = GraphicalReportHandler::new();
105 for error in &errors {
106 let report = error
107 .clone()
108 .with_source_code(oxc_diagnostics::NamedSource::new(
109 cli.file.display().to_string(),
110 source.clone(),
111 ));
112 let mut output_str = String::new();
113 handler
114 .render_report(&mut output_str, report.as_ref())
115 .unwrap();
116 eprint!("{output_str}");
117 }
118 process::exit(1);
119 }
120 Ok(output) => match output.format {
121 OutputFormat::Html => print!("[STATIC]\n{}", output.text),
122 OutputFormat::Marko => print!("[MARKO]\n{}", output.text),
123 },
124 }
125
126 // Print any errors to stderr with pretty formatting
127
128 // Print output
129}