Skip to content

Commit 1574516

Browse files
authored
fix: make dependency graph more robust (#909)
Fixes #898 Needed for easier debugging (script to clone a jsr package and all its dependencies locally)
1 parent 34e0ead commit 1574516

File tree

4 files changed

+54
-42
lines changed

4 files changed

+54
-42
lines changed

api/src/api/package.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use hyper::Request;
2626
use hyper::Response;
2727
use hyper::StatusCode;
2828
use indexmap::IndexMap;
29+
use indexmap::IndexSet;
2930
use regex::Regex;
3031
use routerify::prelude::RequestExt;
3132
use routerify::Router;
@@ -1849,6 +1850,7 @@ struct GraphDependencyCollector<'a> {
18491850
dependencies: &'a mut IndexMap<DependencyKind, DependencyInfo>,
18501851
exports: &'a IndexMap<String, IndexMap<String, String>>,
18511852
id_index: &'a mut usize,
1853+
visited: IndexSet<DependencyKind>,
18521854
}
18531855

18541856
impl<'a> GraphDependencyCollector<'a> {
@@ -1866,6 +1868,7 @@ impl<'a> GraphDependencyCollector<'a> {
18661868
dependencies,
18671869
exports,
18681870
id_index,
1871+
visited: Default::default(),
18691872
}
18701873
.build_module_info(root_module)
18711874
.unwrap();
@@ -1920,6 +1923,12 @@ impl<'a> GraphDependencyCollector<'a> {
19201923
}
19211924
};
19221925

1926+
if self.visited.contains(&dependency) {
1927+
return self.dependencies.get(&dependency).map(|dep| dep.id);
1928+
} else {
1929+
self.visited.insert(dependency.clone());
1930+
}
1931+
19231932
if let Some(info) = self.dependencies.get(&dependency) {
19241933
Some(info.id)
19251934
} else {
@@ -1941,24 +1950,27 @@ impl<'a> GraphDependencyCollector<'a> {
19411950
| Module::External(_) => None,
19421951
};
19431952

1944-
let mut children = vec![];
1953+
let id = *self.id_index;
1954+
*self.id_index += 1;
1955+
1956+
let mut children = IndexSet::new();
19451957
match module {
19461958
Module::Js(module) => {
19471959
if let Some(types_dep) = &module.maybe_types_dependency {
19481960
if let Some(child) = self.build_resolved_info(&types_dep.dependency)
19491961
{
1950-
children.push(child);
1962+
children.insert(child);
19511963
}
19521964
}
19531965
for dep in module.dependencies.values() {
19541966
if !dep.maybe_code.is_none() {
19551967
if let Some(child) = self.build_resolved_info(&dep.maybe_code) {
1956-
children.push(child);
1968+
children.insert(child);
19571969
}
19581970
}
19591971
if !dep.maybe_type.is_none() {
19601972
if let Some(child) = self.build_resolved_info(&dep.maybe_type) {
1961-
children.push(child);
1973+
children.insert(child);
19621974
}
19631975
}
19641976
}
@@ -1970,8 +1982,6 @@ impl<'a> GraphDependencyCollector<'a> {
19701982
| Module::External(_) => {}
19711983
}
19721984

1973-
let id = *self.id_index;
1974-
19751985
self.dependencies.insert(
19761986
dependency,
19771987
DependencyInfo {
@@ -1982,8 +1992,6 @@ impl<'a> GraphDependencyCollector<'a> {
19821992
},
19831993
);
19841994

1985-
*self.id_index += 1;
1986-
19871995
Some(id)
19881996
}
19891997
}
@@ -2004,7 +2012,7 @@ impl<'a> GraphDependencyCollector<'a> {
20042012
},
20052013
DependencyInfo {
20062014
id,
2007-
children: vec![],
2015+
children: Default::default(),
20082016
size: None,
20092017
media_type: None,
20102018
},
@@ -2052,7 +2060,7 @@ pub enum DependencyKind {
20522060
#[derive(Debug, Eq, PartialEq)]
20532061
pub struct DependencyInfo {
20542062
pub id: usize,
2055-
pub children: Vec<usize>,
2063+
pub children: IndexSet<usize>,
20562064
pub size: Option<u64>,
20572065
pub media_type: Option<MediaType>,
20582066
}
@@ -2165,6 +2173,7 @@ pub async fn get_score_handler(
21652173
mod test {
21662174
use hyper::Body;
21672175
use hyper::StatusCode;
2176+
use indexmap::IndexSet;
21682177
use serde_json::json;
21692178

21702179
use crate::api::ApiDependency;
@@ -3631,10 +3640,11 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg==
36313640
assert_eq!(
36323641
deps,
36333642
vec![ApiDependencyGraphItem {
3643+
id: 0,
36343644
dependency: super::DependencyKind::Root {
36353645
path: "/mod.ts".to_string(),
36363646
},
3637-
children: vec![],
3647+
children: IndexSet::new(),
36383648
size: Some(155),
36393649
media_type: Some("TypeScript".to_string()),
36403650
}]
@@ -3664,21 +3674,23 @@ ggHohNAjhbzDaY2iBW/m3NC5dehGUP4T2GBo/cwGhg==
36643674
deps,
36653675
vec![
36663676
ApiDependencyGraphItem {
3677+
id: 1,
36673678
dependency: super::DependencyKind::Jsr {
36683679
scope: "scope".to_string(),
36693680
package: "foo".to_string(),
36703681
version: "1.2.3".to_string(),
36713682
entrypoint: super::JsrEntrypoint::Entrypoint(".".to_string())
36723683
},
3673-
children: vec![],
3684+
children: IndexSet::new(),
36743685
size: Some(155),
36753686
media_type: Some("TypeScript".to_string())
36763687
},
36773688
ApiDependencyGraphItem {
3689+
id: 0,
36783690
dependency: super::DependencyKind::Root {
36793691
path: "/mod.ts".to_string()
36803692
},
3681-
children: vec![0],
3693+
children: IndexSet::from([1]),
36823694
size: Some(117),
36833695
media_type: Some("TypeScript".to_string())
36843696
}

api/src/api/types.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ impl From<PublishingTask> for ApiPublishingTask {
8484
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
8585
#[serde(rename_all = "camelCase")]
8686
pub struct ApiDependencyGraphItem {
87+
pub id: usize,
8788
pub dependency: super::package::DependencyKind,
88-
pub children: Vec<usize>,
89+
pub children: indexmap::IndexSet<usize>,
8990
pub size: Option<u64>,
9091
pub media_type: Option<String>,
9192
}
@@ -103,6 +104,7 @@ impl
103104
),
104105
) -> Self {
105106
Self {
107+
id: info.id,
106108
dependency: kind,
107109
children: info.children,
108110
size: info.size,

frontend/routes/package/(_islands)/DependencyGraph.tsx

+25-28
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ interface JsrPackage {
4848
version: string;
4949
}
5050

51-
export function groupDependencies(
51+
function groupDependencies(
5252
items: DependencyGraphItem[],
5353
): GroupedDependencyGraphItem[] {
5454
const referencedBy = new Map<number, Set<number>>();
55-
for (let i = 0; i < items.length; i++) {
56-
for (const child of items[i].children) {
57-
if (!referencedBy.has(child)) {
58-
referencedBy.set(child, new Set());
55+
for (const item of items) {
56+
for (const childId of item.children) {
57+
if (!referencedBy.has(childId)) {
58+
referencedBy.set(childId, new Set());
5959
}
60-
referencedBy.get(child)!.add(i);
60+
referencedBy.get(childId)!.add(item.id);
6161
}
6262
}
6363

@@ -66,16 +66,15 @@ export function groupDependencies(
6666
entrypoints: {
6767
entrypoint: string;
6868
isEntrypoint: boolean;
69-
oldIndex: number;
69+
oldId: number;
7070
}[];
7171
children: number[];
7272
size: number | undefined;
7373
mediaType: string | undefined;
74-
oldIndices: number[];
74+
oldIds: number[];
7575
}>();
7676

77-
for (let i = 0; i < items.length; i++) {
78-
const item = items[i];
77+
for (const item of items) {
7978
if (item.dependency.type === "jsr") {
8079
const groupKey =
8180
`${item.dependency.scope}/${item.dependency.package}@${item.dependency.version}`;
@@ -89,29 +88,28 @@ export function groupDependencies(
8988
children: [],
9089
size: undefined,
9190
mediaType: undefined,
92-
oldIndices: [],
91+
oldIds: [],
9392
};
9493
group.entrypoints.push({
9594
entrypoint: item.dependency.entrypoint.value,
9695
isEntrypoint: item.dependency.entrypoint.type == "entrypoint",
97-
oldIndex: i,
96+
oldId: item.id,
9897
});
9998
group.children.push(...item.children);
10099
if (item.size !== undefined) {
101100
group.size ??= 0;
102101
group.size += item.size;
103102
}
104-
group.oldIndices.push(i);
103+
group.oldIds.push(item.id);
105104
jsrGroups.set(groupKey, group);
106105
}
107106
}
108107

109-
const oldIndexToNewIndex = new Map<number, number>();
108+
const idToIndex = new Map<number, number>();
110109
const placedJsrGroups = new Set<string>();
111110
const out: GroupedDependencyGraphItem[] = [];
112111

113-
for (let i = 0; i < items.length; i++) {
114-
const item = items[i];
112+
for (const item of items) {
115113
if (item.dependency.type === "jsr") {
116114
const groupKey =
117115
`${item.dependency.scope}/${item.dependency.package}@${item.dependency.version}`;
@@ -120,12 +118,11 @@ export function groupDependencies(
120118
if (!placedJsrGroups.has(groupKey)) {
121119
placedJsrGroups.add(groupKey);
122120

123-
const groupIndicesSet = new Set(group.oldIndices);
124-
const filteredEntrypoints = group.entrypoints.filter(({ oldIndex }) => {
125-
const refs = referencedBy.get(oldIndex)!;
126-
121+
const groupIds = new Set(group.oldIds);
122+
const filteredEntrypoints = group.entrypoints.filter(({ oldId }) => {
123+
const refs = referencedBy.get(oldId)!;
127124
for (const ref of refs) {
128-
if (!groupIndicesSet.has(ref)) {
125+
if (!groupIds.has(ref)) {
129126
return true;
130127
}
131128
}
@@ -153,13 +150,13 @@ export function groupDependencies(
153150
mediaType: group.mediaType,
154151
});
155152

156-
for (const oldIdx of group.oldIndices) {
157-
oldIndexToNewIndex.set(oldIdx, newIndex);
153+
for (const oldId of group.oldIds) {
154+
idToIndex.set(oldId, newIndex);
158155
}
159156
} else {
160-
oldIndexToNewIndex.set(
161-
i,
162-
oldIndexToNewIndex.get(jsrGroups.get(groupKey)!.oldIndices[0])!,
157+
idToIndex.set(
158+
item.id,
159+
idToIndex.get(jsrGroups.get(groupKey)!.oldIds[0])!,
163160
);
164161
}
165162
} else {
@@ -169,14 +166,14 @@ export function groupDependencies(
169166
size: item.size,
170167
mediaType: item.mediaType,
171168
});
172-
oldIndexToNewIndex.set(i, out.length - 1);
169+
idToIndex.set(item.id, out.length - 1);
173170
}
174171
}
175172

176173
for (let index = 0; index < out.length; index++) {
177174
const newItem = out[index];
178175
const remappedChildren = newItem.children
179-
.map((childIdx) => oldIndexToNewIndex.get(childIdx)!)
176+
.map((oldId) => idToIndex.get(oldId)!)
180177
.filter((childNewIdx) => childNewIdx !== index);
181178
newItem.children = Array.from(new Set(remappedChildren));
182179
}

frontend/utils/api_types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ export type DependencyGraphKind =
302302
| DependencyGraphKindError;
303303

304304
export interface DependencyGraphItem {
305+
id: number;
305306
dependency: DependencyGraphKind;
306307
children: number[];
307308
size: number | undefined;

0 commit comments

Comments
 (0)