Skip to content

Commit 45600f9

Browse files
authored
chore: resolution debugging tool (#86)
1 parent 2039904 commit 45600f9

File tree

11 files changed

+901
-3
lines changed

11 files changed

+901
-3
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
push:
77
branches: [main]
88
tags:
9-
- '*'
9+
- "*"
1010
workflow_dispatch:
1111

1212
jobs:

.github/workflows/release.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ on:
44
workflow_dispatch:
55
inputs:
66
releaseKind:
7-
description: 'Kind of release'
8-
default: 'minor'
7+
description: "Kind of release"
8+
default: "minor"
99
type: choice
1010
options:
1111
- patch

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ documentation = "https://docs.rs/deno_npm"
99
authors = ["the Deno authors"]
1010
license = "MIT"
1111

12+
[features]
13+
tracing = []
14+
1215
[dependencies]
1316
async-trait = "0.1.68"
1417
deno_semver = "0.7.0"

src/resolution/graph.rs

+113
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ pub struct Graph {
332332
// This will be set when creating from a snapshot, then
333333
// inform the final snapshot creation.
334334
packages_to_copy_index: HashMap<NpmPackageId, u8>,
335+
#[cfg(feature = "tracing")]
336+
traces: Vec<super::tracing::TraceGraphSnapshot>,
335337
}
336338

337339
impl Graph {
@@ -436,6 +438,8 @@ impl Graph {
436438
package_name_versions: Default::default(),
437439
resolved_node_ids: Default::default(),
438440
root_packages: Default::default(),
441+
#[cfg(feature = "tracing")]
442+
traces: Default::default(),
439443
};
440444
let mut created_package_ids =
441445
HashMap::with_capacity(snapshot.packages.len());
@@ -599,6 +603,11 @@ impl Graph {
599603
api: &TNpmRegistryApi,
600604
patch_packages: &HashMap<PackageName, Vec<NpmPackageVersionInfo>>,
601605
) -> Result<NpmResolutionSnapshot, NpmRegistryPackageInfoLoadError> {
606+
#[cfg(feature = "tracing")]
607+
if !self.traces.is_empty() {
608+
super::tracing::output(&self.traces);
609+
}
610+
602611
let packages_to_pkg_ids = self
603612
.nodes
604613
.keys()
@@ -1036,6 +1045,25 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
10361045
}
10371046
};
10381047

1048+
#[cfg(feature = "tracing")]
1049+
{
1050+
self.graph.traces.push(build_trace_graph_snapshot(
1051+
self.graph,
1052+
&self.dep_entry_cache,
1053+
&parent_path.with_id(
1054+
child_id,
1055+
dep.bare_specifier.clone(),
1056+
self
1057+
.graph
1058+
.resolved_node_ids
1059+
.get(child_id)
1060+
.unwrap()
1061+
.nv
1062+
.clone(),
1063+
),
1064+
));
1065+
}
1066+
10391067
if !found_peer {
10401068
found_peer = !self.graph.borrow_node_mut(child_id).no_peers;
10411069
}
@@ -1053,6 +1081,25 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
10531081
&parent_path,
10541082
)?;
10551083

1084+
#[cfg(feature = "tracing")]
1085+
if let Some(child_id) = maybe_new_id {
1086+
self.graph.traces.push(build_trace_graph_snapshot(
1087+
self.graph,
1088+
&self.dep_entry_cache,
1089+
&parent_path.with_id(
1090+
child_id,
1091+
dep.bare_specifier.clone(),
1092+
self
1093+
.graph
1094+
.resolved_node_ids
1095+
.get(child_id)
1096+
.unwrap()
1097+
.nv
1098+
.clone(),
1099+
),
1100+
));
1101+
}
1102+
10561103
// For optional peer dependencies, we want to resolve them if any future
10571104
// same parent version resolves them. So when not resolved, store them to be
10581105
// potentially resolved later.
@@ -1477,6 +1524,71 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
14771524
}
14781525
}
14791526

1527+
#[cfg(feature = "tracing")]
1528+
fn build_trace_graph_snapshot(
1529+
graph: &Graph,
1530+
dep_entry_cache: &DepEntryCache,
1531+
current_path: &GraphPath,
1532+
) -> super::tracing::TraceGraphSnapshot {
1533+
use super::tracing::*;
1534+
1535+
fn build_path(current_path: &GraphPath) -> TraceGraphPath {
1536+
TraceGraphPath {
1537+
specifier: current_path.specifier.to_string(),
1538+
node_id: current_path.node_id().0,
1539+
nv: current_path.nv.to_string(),
1540+
previous: current_path.previous_node.as_ref().and_then(|n| match n {
1541+
GraphPathNodeOrRoot::Node(graph_path) => {
1542+
Some(Box::new(build_path(graph_path)))
1543+
}
1544+
GraphPathNodeOrRoot::Root(_) => None,
1545+
}),
1546+
}
1547+
}
1548+
1549+
TraceGraphSnapshot {
1550+
nodes: graph
1551+
.nodes
1552+
.iter()
1553+
.map(|(node_id, node)| {
1554+
let id = graph.get_npm_pkg_id(*node_id);
1555+
TraceNode {
1556+
id: node_id.0,
1557+
resolved_id: id.as_serialized().to_string(),
1558+
children: node
1559+
.children
1560+
.iter()
1561+
.map(|(k, v)| (k.to_string(), v.0))
1562+
.collect(),
1563+
dependencies: dep_entry_cache
1564+
.get(&id.nv)
1565+
.map(|d| {
1566+
d.iter()
1567+
.map(|dep| TraceNodeDependency {
1568+
kind: format!("{:?}", dep.kind),
1569+
bare_specifier: dep.bare_specifier.to_string(),
1570+
name: dep.name.to_string(),
1571+
version_req: dep.version_req.to_string(),
1572+
peer_dep_version_req: dep
1573+
.peer_dep_version_req
1574+
.as_ref()
1575+
.map(|r| r.to_string()),
1576+
})
1577+
.collect()
1578+
})
1579+
.unwrap_or_default(),
1580+
}
1581+
})
1582+
.collect(),
1583+
roots: graph
1584+
.root_packages
1585+
.iter()
1586+
.map(|(nv, id)| (nv.to_string(), id.0))
1587+
.collect(),
1588+
path: build_path(current_path),
1589+
}
1590+
}
1591+
14801592
#[cfg(test)]
14811593
mod test {
14821594
use std::borrow::Cow;
@@ -2383,6 +2495,7 @@ mod test {
23832495
]
23842496
);
23852497
}
2498+
23862499
#[tokio::test]
23872500
async fn resolve_peer_dep_other_specifier_slot() {
23882501
let api = TestNpmRegistryApi::default();

src/resolution/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ mod collections;
44
mod common;
55
mod graph;
66
mod snapshot;
7+
#[cfg(feature = "tracing")]
8+
mod tracing;
79

810
pub use common::NpmPackageVersionNotFound;
911
pub use common::NpmPackageVersionResolutionError;

src/resolution/tracing/README.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# tracing
2+
3+
This is a tool for debugging the npm resolution.
4+
5+
To use it, compile with `--feature tracing`. For example:
6+
7+
```sh
8+
cargo test grand_child_package_has_self_as_peer_dependency_root --features tracing -- --nocapture
9+
```
10+
11+
This will output something like:
12+
13+
```
14+
==============
15+
Trace output ready! Please open your browser to: file:///.../deno-npm-trace.html
16+
==============
17+
```
18+
19+
Follow that and open your browser to see the output.

src/resolution/tracing/app.css

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
html, body {
2+
margin: 0;
3+
padding: 0;
4+
height: 100%;
5+
display: flex;
6+
flex-direction: column;
7+
overflow: hidden;
8+
}
9+
10+
#container {
11+
display: flex;
12+
flex-direction: column;
13+
height: 100vh;
14+
}
15+
16+
#slider {
17+
display: flex;
18+
align-items: center;
19+
gap: 10px;
20+
padding: 10px;
21+
background: #f0f0f0;
22+
}
23+
24+
#slider input[type="range"] {
25+
flex: 1;
26+
min-width: 0;
27+
}
28+
29+
#stepText {
30+
flex: 0;
31+
white-space: nowrap;
32+
}
33+
34+
#main {
35+
display: flex;
36+
flex: 1;
37+
flex-direction: row;
38+
height: 100vh;
39+
}
40+
41+
#graph {
42+
flex: 1;
43+
overflow: hidden;
44+
}
45+
46+
#info h3 {
47+
padding-top: 0;
48+
margin-top: 0;
49+
}
50+
51+
#info {
52+
padding: 20px;
53+
background: #f0f0f0;
54+
flex: 0 0 400px;
55+
overflow-y: scroll;
56+
}

0 commit comments

Comments
 (0)