| 59 |
59 |
|
&makeover_build::Emit::default(),
|
| 60 |
60 |
|
REVIEWED_OVERLAPS,
|
| 61 |
61 |
|
);
|
|
62 |
+ |
makeover_build::check_vocabulary_use(
|
|
63 |
+ |
&markup_files(),
|
|
64 |
+ |
&makeover_build::Emit::default(),
|
|
65 |
+ |
DEAD_VOCABULARY_HIGH_WATER,
|
|
66 |
+ |
);
|
| 62 |
67 |
|
|
| 63 |
68 |
|
// --- Static asset fingerprinting ---
|
| 64 |
69 |
|
// Hash the content of key static files to produce a version suffix.
|
| 136 |
141 |
|
write_if_changed(Path::new("templates/_sheet.html"), &sheet_partial);
|
| 137 |
142 |
|
}
|
| 138 |
143 |
|
|
|
144 |
+ |
/// How many generated classes may go unused before the build fails.
|
|
145 |
+ |
///
|
|
146 |
+ |
/// One-sided: over this fails, under it warns and asks for the seal to be
|
|
147 |
+ |
/// lowered. A build that broke on deleting dead CSS would teach the wrong
|
|
148 |
+ |
/// lesson, so the number only ever ratchets down.
|
|
149 |
+ |
///
|
|
150 |
+ |
/// Measured against [`markup_files`]. The stylesheets are deliberately out -- a
|
|
151 |
+ |
/// class in `style.css` is that class being styled, not that class being
|
|
152 |
+ |
/// emitted, and counting them would mark the whole vocabulary used by
|
|
153 |
+ |
/// definition. `static/dist` is out for a subtler reason: it is `tsc` output of
|
|
154 |
+ |
/// `frontend/src`, so including both counts one class-writing line twice, and
|
|
155 |
+ |
/// the source is the half a human edits.
|
|
156 |
+ |
///
|
|
157 |
+ |
/// The server's markup is spread wider than either desktop app's -- 200-odd
|
|
158 |
+ |
/// Askama templates, the hand-written scripts in `static/`, the TypeScript they
|
|
159 |
+ |
/// are being replaced by, and the Rust that writes markup directly -- which is
|
|
160 |
+ |
/// the whole reason this seal took a pass of its own rather than landing beside
|
|
161 |
+ |
/// the check that reads the stylesheets.
|
|
162 |
+ |
const DEAD_VOCABULARY_HIGH_WATER: usize = 18;
|
|
163 |
+ |
|
|
164 |
+ |
/// Every file that can carry a class name.
|
|
165 |
+ |
///
|
|
166 |
+ |
/// Sorted within each group, so two machines read the same set in the same
|
|
167 |
+ |
/// order. It makes no difference to the count and every difference to reading a
|
|
168 |
+ |
/// diff of the warning.
|
|
169 |
+ |
fn markup_files() -> Vec<std::path::PathBuf> {
|
|
170 |
+ |
let mut files = Vec::new();
|
|
171 |
+ |
for (dir, extension) in [
|
|
172 |
+ |
("templates", "html"),
|
|
173 |
+ |
("static", "js"),
|
|
174 |
+ |
("frontend/src", "ts"),
|
|
175 |
+ |
("src", "rs"),
|
|
176 |
+ |
] {
|
|
177 |
+ |
let mut found = Vec::new();
|
|
178 |
+ |
collect(Path::new(dir), extension, &mut found);
|
|
179 |
+ |
found.sort();
|
|
180 |
+ |
files.extend(found);
|
|
181 |
+ |
}
|
|
182 |
+ |
files
|
|
183 |
+ |
}
|
|
184 |
+ |
|
|
185 |
+ |
/// Every file under `dir` with this extension, recursively.
|
|
186 |
+ |
fn collect(dir: &Path, extension: &str, out: &mut Vec<std::path::PathBuf>) {
|
|
187 |
+ |
let Ok(entries) = fs::read_dir(dir) else {
|
|
188 |
+ |
return;
|
|
189 |
+ |
};
|
|
190 |
+ |
for entry in entries.flatten() {
|
|
191 |
+ |
let path = entry.path();
|
|
192 |
+ |
if path.is_dir() {
|
|
193 |
+ |
// The bundler's output, which is `frontend/src` compiled. Reading
|
|
194 |
+ |
// both would count the same line twice.
|
|
195 |
+ |
if path.file_name().is_some_and(|name| name == "dist") {
|
|
196 |
+ |
continue;
|
|
197 |
+ |
}
|
|
198 |
+ |
collect(&path, extension, out);
|
|
199 |
+ |
} else if path.extension().is_some_and(|ext| ext == extension) {
|
|
200 |
+ |
out.push(path);
|
|
201 |
+ |
}
|
|
202 |
+ |
}
|
|
203 |
+ |
}
|
|
204 |
+ |
|
| 139 |
205 |
|
/// The hand-written stylesheets. Ordered, so the guard reports the same way
|
| 140 |
206 |
|
/// twice. `geometry.css` and `layout.css` are excluded: they are generated.
|
| 141 |
207 |
|
const HAND_WRITTEN_CSS: [&str; 3] = [
|