1use zdc_codegen::{Bundle, Options};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct BundleSize {
13 pub name: String,
14 pub client_js: usize,
15 pub boot_js: usize,
16 pub styles_css: usize,
17 pub index_html: usize,
18 pub manifest_json: usize,
19}
20
21impl BundleSize {
22 pub fn total(&self) -> usize {
24 self.client_js + self.boot_js + self.styles_css + self.index_html + self.manifest_json
25 }
26}
27
28pub fn repository_path(relative: &str) -> std::path::PathBuf {
29 std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
30 .join("../..")
31 .join(relative)
32}
33
34pub fn try_compile(source: &str, name: &str) -> Result<Bundle, Vec<String>> {
48 let program = zdc_parser::parse(source).map_err(|e| vec![e.message])?;
49 let hir = zdc_resolve::Resolver::new(&program)
50 .resolve()
51 .map_err(|errors| errors.into_iter().map(|e| e.message).collect::<Vec<_>>())?;
52
53 let split = zdc_graph::split(&hir);
54 if split.has_errors() {
55 return Err(split.errors().map(|error| error.message.clone()).collect());
56 }
57 let verdict = zdc_graph::ifc(&hir, &split);
58 let table = zdc_types::check(&hir, &split)
59 .map_err(|errors| errors.into_iter().map(|e| e.message).collect::<Vec<_>>())?;
60
61 let Some(cleared) = verdict.clearance() else {
64 return Err(verdict
65 .errors()
66 .map(|error| error.message.clone())
67 .collect());
68 };
69
70 let options = Options::new(name, "bench");
71 let inputs = zdc_codegen::Inputs {
72 hir: &hir,
73 split: &split,
74 verdict: &verdict,
75 table: &table,
76 cleared,
77 };
78 zdc_codegen::compile(&inputs, &options)
79 .map_err(|errors| errors.into_iter().map(|e| e.message).collect())
80}
81
82pub fn compile(relative: &str) -> Bundle {
84 let source = std::fs::read_to_string(repository_path(relative))
85 .unwrap_or_else(|e| panic!("reading {relative}: {e}"));
86 try_compile(&source, relative).unwrap_or_else(|errors| {
87 panic!(
88 "{relative} failed to compile:\n{}",
89 errors
90 .iter()
91 .map(|message| format!(" {message}"))
92 .collect::<Vec<_>>()
93 .join("\n")
94 )
95 })
96}
97
98pub fn bundle_sizes() -> Vec<BundleSize> {
100 [
101 "examples/hello.zd",
102 "examples/counter.zd",
103 "crates/zdc-bench/bench/row.zd",
104 ]
105 .into_iter()
106 .map(|relative| {
107 let bundle = compile(relative);
108 BundleSize {
109 name: relative.to_string(),
110 client_js: bundle.client_js.len(),
111 boot_js: bundle.boot_js.as_ref().map_or(0, String::len),
115 styles_css: bundle.styles_css.len(),
116 index_html: bundle.index_html.as_ref().map_or(0, String::len),
119 manifest_json: bundle.manifest_json.len(),
120 }
121 })
122 .collect()
123}
124
125pub fn runtime_sizes() -> Vec<(&'static str, usize)> {
137 let shipped = |source| zdc_runtime::for_mode(source, zdc_runtime::Mode::Release).len();
143 vec![
144 ("runtime/signal.js", shipped(zdc_runtime::SIGNAL_JS)),
145 ("runtime/dom.js", shipped(zdc_runtime::DOM_JS)),
146 (
149 "runtime/foreign.js (a gives-view foreign only)",
150 shipped(zdc_runtime::FOREIGN_JS),
151 ),
152 (
153 "runtime/markup.js (a program with Prose only)",
154 shipped(zdc_runtime::MARKUP_JS),
155 ),
156 (
157 "runtime/list.js (a program with an each only)",
158 shipped(zdc_runtime::LIST_JS),
159 ),
160 ("runtime/base.css", zdc_runtime::BASE_CSS.len()),
161 (
162 "runtime/elements.js (direct emission only)",
163 shipped(zdc_runtime::ELEMENTS_JS),
164 ),
165 ]
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn every_listed_example_compiles_and_is_not_empty() {
174 let sizes = bundle_sizes();
175 assert_eq!(sizes.len(), 3, "three examples are listed: {sizes:?}");
176 for size in sizes {
177 assert!(size.client_js > 0, "{} emitted nothing", size.name);
178 assert!(size.total() > size.client_js);
179 }
180 }
181}