Skip to content

Commit 51031d6

Browse files
committed
Remove the crate dependency graph website generation intermediate generation step
1 parent d9214c7 commit 51031d6

7 files changed

Lines changed: 67 additions & 38 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ jobs:
176176
if: github.event_name == 'push'
177177
run: |
178178
mkdir -p website/generated-new
179-
cargo run -p crate-hierarchy-viz -- website/generated-new/crate_hierarchy.dot
179+
cargo run -p crate-hierarchy-viz -- website/generated-new/crate_hierarchy.svg
180180
cargo run -p editor-message-tree -- website/generated-new/hierarchical_message_system_tree.txt
181181
182182
- name: 💿 Obtain cache of auto-generated code docs artifacts, to check if they've changed

.github/workflows/website.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,14 @@ jobs:
6464
rustup update stable
6565
echo "🦀 Latest updated version of Rust:"
6666
rustc --version
67-
cargo run -p crate-hierarchy-viz -- website/generated/crate_hierarchy.dot
67+
cargo run -p crate-hierarchy-viz -- website/generated/crate_hierarchy.svg
6868
cargo run -p editor-message-tree -- website/generated/hierarchical_message_system_tree.txt
6969
70-
- name: 🔧 Build auto-generated code docs artifacts into HTML/SVG
70+
- name: 🔧 Build auto-generated code docs artifacts into HTML
7171
run: |
7272
cd website
7373
npm ci
7474
npm run generate-editor-structure
75-
npm run generate-crate-hierarchy
7675
7776
- name: 📃 Generate node catalog documentation
7877
run: cargo run -p node-docs -- website/content/learn/node-catalog

tools/crate-hierarchy-viz/src/main.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use anyhow::{Context, Result};
22
use serde::Deserialize;
33
use std::collections::{HashMap, HashSet};
44
use std::fs;
5+
use std::io::Write;
56
use std::path::PathBuf;
7+
use std::process::Command;
68

79
#[derive(Debug, Deserialize)]
810
struct WorkspaceToml {
@@ -173,17 +175,76 @@ fn main() -> Result<()> {
173175

174176
remove_transitive_dependencies(&mut crates);
175177

176-
// Generate DOT format and write to output file
178+
// Generate DOT format, convert to SVG, and write to output file
177179
let dot_content = generate_dot(&crates);
180+
let svg_content = dot_to_svg(&dot_content)?;
178181

179182
if let Some(parent) = output_path.parent() {
180183
fs::create_dir_all(parent).with_context(|| format!("Failed to create directory {:?}", parent))?;
181184
}
182-
fs::write(&output_path, &dot_content).with_context(|| format!("Failed to write to {:?}", output_path))?;
185+
fs::write(&output_path, &svg_content).with_context(|| format!("Failed to write to {:?}", output_path))?;
183186

184187
Ok(())
185188
}
186189

190+
/// Convert a DOT graph string to SVG by shelling out to @viz-js/viz via Node.js
191+
fn dot_to_svg(dot: &str) -> Result<String> {
192+
let temp_dir = std::env::temp_dir().join("crate-hierarchy-viz");
193+
fs::create_dir_all(&temp_dir).with_context(|| "Failed to create temp directory")?;
194+
195+
// Install @viz-js/viz into the temp directory if not already present
196+
let node_modules = temp_dir.join("node_modules").join("@viz-js");
197+
if !node_modules.exists() {
198+
let npm = if cfg!(target_os = "windows") { "npm.cmd" } else { "npm" };
199+
let status = Command::new(npm)
200+
.args(["install", "--prefix", &temp_dir.to_string_lossy(), "@viz-js/viz"])
201+
.stdout(std::process::Stdio::null())
202+
.stderr(std::process::Stdio::piped())
203+
.status()
204+
.with_context(|| "Failed to run `npm install`. Is Node.js installed?")?;
205+
if !status.success() {
206+
anyhow::bail!("Executing `npm install @viz-js/viz` failed");
207+
}
208+
}
209+
210+
// Write a small script that reads DOT from stdin and outputs SVG
211+
let script_path = temp_dir.join("convert.mjs");
212+
fs::write(
213+
&script_path,
214+
r#"
215+
import { instance } from "@viz-js/viz";
216+
let dot = "";
217+
for await (const chunk of process.stdin) dot += chunk;
218+
const viz = await instance();
219+
process.stdout.write(viz.renderString(dot, { format: "svg" }));
220+
"#
221+
.trim(),
222+
)?;
223+
224+
let mut child = Command::new("node")
225+
.arg(&script_path)
226+
.stdin(std::process::Stdio::piped())
227+
.stdout(std::process::Stdio::piped())
228+
.stderr(std::process::Stdio::piped())
229+
.spawn()
230+
.with_context(|| "Failed to spawn `node`. Is Node.js installed?")?;
231+
232+
// Write DOT content to stdin then close the pipe
233+
child.stdin.take().unwrap().write_all(dot.as_bytes()).with_context(|| "Failed to write DOT content to stdin")?;
234+
235+
let output = child.wait_with_output().with_context(|| "Failed to wait for `node` process")?;
236+
237+
// Clean up the temp script (node_modules is intentionally kept as a cache)
238+
let _ = fs::remove_file(&script_path);
239+
240+
if !output.status.success() {
241+
let stderr = String::from_utf8_lossy(&output.stderr);
242+
anyhow::bail!("DOT to SVG conversion failed (exit code {:?}):\n{}", output.status.code(), stderr);
243+
}
244+
245+
String::from_utf8(output.stdout).with_context(|| "SVG output was not valid UTF-8")
246+
}
247+
187248
fn generate_dot(crates: &[CrateInfo]) -> String {
188249
let mut out = String::new();
189250
out.push_str("digraph CrateHierarchy {\n");

website/.build-scripts/generate-crate-hierarchy.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

website/package-lock.json

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"scripts": {
1414
"postinstall": "node .build-scripts/install.ts",
1515
"generate-editor-structure": "node .build-scripts/generate-editor-structure.ts generated/hierarchical_message_system_tree.txt generated/hierarchical_message_system_tree.html",
16-
"generate-crate-hierarchy": "node .build-scripts/generate-crate-hierarchy.ts generated/crate_hierarchy.dot generated/crate_hierarchy.svg",
1716
"check": "tsc --noEmit && eslint",
1817
"fix": "eslint --fix"
1918
},
@@ -22,7 +21,6 @@
2221
"@eslint/eslintrc": "^3.3.3",
2322
"@eslint/js": "^9.39.2",
2423
"@types/node": "^25.0.9",
25-
"@viz-js/viz": "^3.25.0",
2624
"eslint": "^9.39.2",
2725
"eslint-config-prettier": "^10.1.8",
2826
"eslint-import-resolver-typescript": "^4.4.4",

website/templates/macros/replacements.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ <h2 class="headline"><a href="{{ article.permalink | safe }}">{{ article.title }
5757

5858
TO TEST IT LOCALLY, FROM THE ROOT OF THE PROJECT, RUN:
5959

60-
cargo run -p crate-hierarchy-viz -- website/generated/crate_hierarchy.dot
61-
cd website
62-
npm run generate-crate-hierarchy</pre>" -%}
60+
cargo run -p crate-hierarchy-viz -- website/generated/crate_hierarchy.svg</pre>" -%}
6361
{{ content | default(value = fallback) | safe }}
6462
{% endmacro crate_hierarchy %}

0 commit comments

Comments
 (0)