Skip to content

Commit 3cccb7d

Browse files
refactor: fix the need to rerun at page change (still need to fix laziness)
1 parent ffc643a commit 3cccb7d

File tree

4 files changed

+190
-150
lines changed

4 files changed

+190
-150
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import semver from "semver";
2+
import { gitHubCache, type GitHubRelease } from "$lib/server/github-cache";
3+
import { discoverer } from "$lib/server/package-discoverer";
4+
import type { Repository } from "$lib/repositories";
5+
import type { Prettify } from "$lib/types";
6+
7+
/**
8+
* Get all the releases for a single package.
9+
*
10+
* @param packageName the package to get the releases for
11+
* @param allPackages all the known packages
12+
* @returns the package's repository alongside its releases, or
13+
* undefined if not found
14+
*/
15+
async function getPackageReleases(
16+
packageName: string,
17+
allPackages: Awaited<ReturnType<typeof discoverer.getOrDiscoverCategorized>>
18+
) {
19+
let currentPackage:
20+
| Prettify<
21+
Omit<Repository, "dataFilter" | "metadataFromTag" | "changelogContentsReplacer"> &
22+
Pick<(typeof allPackages)[number]["packages"][number], "pkg">
23+
>
24+
| undefined = undefined;
25+
const foundVersions = new Set<string>();
26+
const releases: ({ cleanName: string; cleanVersion: string } & GitHubRelease)[] = [];
27+
28+
// Discover releases
29+
console.log("Starting loading releases...");
30+
for (const { category, packages } of allPackages) {
31+
for (const { pkg, ...repo } of packages) {
32+
if (pkg.name.localeCompare(packageName, undefined, { sensitivity: "base" }) !== 0) continue;
33+
34+
// 1. Get releases
35+
const cachedReleases = await gitHubCache.getReleases({ ...repo, category });
36+
console.log(
37+
`${cachedReleases.length} releases found for repo ${repo.owner}/${repo.repoName}`
38+
);
39+
40+
// 2. Filter out invalid ones
41+
const validReleases = cachedReleases
42+
.filter(release => {
43+
const [name] = repo.metadataFromTag(release.tag_name);
44+
return (
45+
(repo.dataFilter?.(release) ?? true) &&
46+
pkg.name.localeCompare(name, undefined, { sensitivity: "base" }) === 0
47+
);
48+
})
49+
.sort((a, b) => {
50+
const [, firstVersion] = repo.metadataFromTag(a.tag_name);
51+
const [, secondVersion] = repo.metadataFromTag(b.tag_name);
52+
return semver.rcompare(firstVersion, secondVersion);
53+
});
54+
console.log("Length after filtering:", validReleases.length);
55+
// Get the releases matching the slug, or all of them if none
56+
// (the latter case for repos with no package in names)
57+
console.log("Final filtered count:", validReleases.length);
58+
59+
// 3. For each release, check if it is already found, searching by versions
60+
const { dataFilter, metadataFromTag, changelogContentsReplacer, ...rest } = repo;
61+
for (const release of validReleases) {
62+
const [cleanName, cleanVersion] = repo.metadataFromTag(release.tag_name);
63+
console.log(`Release ${release.tag_name}, extracted version: ${cleanVersion}`);
64+
if (foundVersions.has(cleanVersion)) continue;
65+
66+
// If not, add its version to the set and itself to the final version
67+
const currentNewestVersion = [...foundVersions].sort(semver.rcompare)[0];
68+
console.log("Current newest version", currentNewestVersion);
69+
foundVersions.add(cleanVersion);
70+
releases.push({ cleanName, cleanVersion, ...release });
71+
72+
// If it is newer than the newest we got, set this repo as the "final repo"
73+
if (!currentNewestVersion || semver.gt(cleanVersion, currentNewestVersion)) {
74+
console.log(
75+
`Current newest version "${currentNewestVersion}" doesn't exist or is lesser than ${cleanVersion}, setting ${rest.owner}/${rest.repoName} as final repo`
76+
);
77+
currentPackage = {
78+
category,
79+
pkg,
80+
...rest
81+
};
82+
}
83+
}
84+
console.log("Done");
85+
}
86+
}
87+
88+
return currentPackage
89+
? {
90+
releasesRepo: currentPackage,
91+
releases: releases.toSorted(
92+
(a, b) =>
93+
new Date(b.published_at ?? b.created_at).getTime() -
94+
new Date(a.published_at ?? a.created_at).getTime()
95+
)
96+
}
97+
: undefined;
98+
}
99+
100+
/**
101+
* Get all the repositories and releases for all the
102+
* known packages.
103+
*
104+
* @param allPackages all the known packages
105+
* @returns a map of package names to their awaitable result
106+
*/
107+
function getAllPackagesReleases(
108+
allPackages: Awaited<ReturnType<typeof discoverer.getOrDiscoverCategorized>>
109+
) {
110+
const packages = allPackages.flatMap(({ packages }) => packages);
111+
112+
return packages.reduce<Record<string, ReturnType<typeof getPackageReleases>>>(
113+
(acc, { pkg: { name } }) => {
114+
acc[name] = getPackageReleases(name, allPackages);
115+
return acc;
116+
},
117+
{}
118+
);
119+
}
120+
121+
/**
122+
* The goal of this load function is to serve any `[...package]`
123+
* page by handing it a bunch of promises, so it can await the one
124+
* it needs. The other ones are for the sidebar badges, so the page
125+
* doesn't have to re-run the data loading every time we switch from
126+
* a package to another.
127+
*/
128+
export async function load() {
129+
// 1. Get all the packages
130+
const categorizedPackages = await discoverer.getOrDiscoverCategorized();
131+
132+
// 2. Use them to get a map of packages to promises of releases
133+
const allReleases = getAllPackagesReleases(categorizedPackages);
134+
135+
// 3. Send all that down to the page's load function
136+
return { allReleases };
137+
}
Lines changed: 13 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,22 @@
11
import { error } from "@sveltejs/kit";
2-
import semver from "semver";
3-
import { gitHubCache, type GitHubRelease } from "$lib/server/github-cache";
4-
import { discoverer } from "$lib/server/package-discoverer";
5-
import type { Repository } from "$lib/repositories";
6-
import type { Prettify } from "$lib/types";
72

8-
async function getReleases(
9-
packageName: string,
10-
allPackages: Awaited<ReturnType<typeof discoverer.getOrDiscoverCategorized>>
11-
) {
12-
let currentPackage:
13-
| Prettify<
14-
Omit<Repository, "dataFilter" | "metadataFromTag" | "changelogContentsReplacer"> &
15-
Pick<(typeof allPackages)[number]["packages"][number], "pkg">
16-
>
17-
| undefined = undefined;
18-
const foundVersions = new Set<string>();
19-
const releases: ({ cleanName: string; cleanVersion: string } & GitHubRelease)[] = [];
20-
21-
// Discover releases
22-
console.log("Starting loading releases...");
23-
for (const { category, packages } of allPackages) {
24-
for (const { pkg, ...repo } of packages) {
25-
if (pkg.name.localeCompare(packageName, undefined, { sensitivity: "base" }) !== 0) continue;
26-
27-
// 1. Get releases
28-
const cachedReleases = await gitHubCache.getReleases({ ...repo, category });
29-
console.log(
30-
`${cachedReleases.length} releases found for repo ${repo.owner}/${repo.repoName}`
31-
);
32-
33-
// 2. Filter out invalid ones
34-
const validReleases = cachedReleases
35-
.filter(release => {
36-
const [name] = repo.metadataFromTag(release.tag_name);
37-
return (
38-
(repo.dataFilter?.(release) ?? true) &&
39-
pkg.name.localeCompare(name, undefined, { sensitivity: "base" }) === 0
40-
);
41-
})
42-
.sort((a, b) => {
43-
const [, firstVersion] = repo.metadataFromTag(a.tag_name);
44-
const [, secondVersion] = repo.metadataFromTag(b.tag_name);
45-
return semver.rcompare(firstVersion, secondVersion);
46-
});
47-
console.log("Length after filtering:", validReleases.length);
48-
// Get the releases matching the slug, or all of them if none
49-
// (the latter case for repos with no package in names)
50-
console.log("Final filtered count:", validReleases.length);
51-
52-
// 3. For each release, check if it is already found, searching by versions
53-
const { dataFilter, metadataFromTag, changelogContentsReplacer, ...rest } = repo;
54-
for (const release of validReleases) {
55-
const [cleanName, cleanVersion] = repo.metadataFromTag(release.tag_name);
56-
console.log(`Release ${release.tag_name}, extracted version: ${cleanVersion}`);
57-
if (foundVersions.has(cleanVersion)) continue;
58-
59-
// If not, add its version to the set and itself to the final version
60-
const currentNewestVersion = [...foundVersions].sort(semver.rcompare)[0];
61-
console.log("Current newest version", currentNewestVersion);
62-
foundVersions.add(cleanVersion);
63-
releases.push({ cleanName, cleanVersion, ...release });
64-
65-
// If it is newer than the newest we got, set this repo as the "final repo"
66-
if (!currentNewestVersion || semver.gt(cleanVersion, currentNewestVersion)) {
67-
console.log(
68-
`Current newest version "${currentNewestVersion}" doesn't exist or is lesser than ${cleanVersion}, setting ${rest.owner}/${rest.repoName} as final repo`
69-
);
70-
currentPackage = {
71-
category,
72-
pkg,
73-
...rest
74-
};
75-
}
76-
}
77-
console.log("Done");
78-
}
79-
}
80-
81-
return currentPackage
82-
? {
83-
releasesRepo: currentPackage,
84-
releases: releases.toSorted(
85-
(a, b) =>
86-
new Date(b.published_at ?? b.created_at).getTime() -
87-
new Date(a.published_at ?? a.created_at).getTime()
88-
)
89-
}
90-
: undefined;
91-
}
92-
93-
async function getAllOtherReleases(exceptPackage: string) {
94-
const discovery = await discoverer.getOrDiscoverCategorized();
95-
const otherPackages = discovery.flatMap(({ packages }) => packages);
96-
97-
const otherReleases = await Promise.all(
98-
otherPackages.map(async otherPackage => await getReleases(otherPackage.pkg.name, discovery))
99-
);
100-
101-
return otherReleases
102-
.filter(o => o !== undefined)
103-
.filter(
104-
({ releasesRepo: releasesPackage }) =>
105-
releasesPackage.pkg.name.localeCompare(exceptPackage, undefined, {
106-
sensitivity: "base"
107-
}) !== 0
108-
);
109-
}
110-
111-
export async function load({ params }) {
3+
export async function load({ params, parent }) {
1124
const { package: slugPackage } = params;
113-
const categorizedPackages = await discoverer.getOrDiscoverCategorized();
5+
// 1. Get the promise array from the layout
6+
const { allReleases } = await parent();
1147

115-
const computedReleases = await getReleases(slugPackage, categorizedPackages);
8+
// 2. Find the package slug in there
9+
const matchingEntry = Object.entries(allReleases).find(
10+
([p]) => p.localeCompare(slugPackage, undefined, { sensitivity: "base" }) === 0
11+
);
12+
if (!matchingEntry) error(404);
11613

14+
// 3. Try to await the releases
15+
const [, matchingPromise] = matchingEntry;
16+
const computedReleases = await matchingPromise;
11717
if (!computedReleases) error(404);
11818

19+
// 4. Return the right data
11920
const { releasesRepo: currentPackage, releases } = computedReleases;
120-
return {
121-
currentPackage,
122-
releases,
123-
otherReleases: getAllOtherReleases(currentPackage.pkg.name)
124-
};
21+
return { currentPackage, releases };
12522
}

src/routes/package/[...package]/+page.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
headless
8989
packageName={data.currentPackage.pkg.name}
9090
allPackages={data.displayablePackages}
91-
otherReleases={data.otherReleases}
91+
otherReleases={data.allReleases}
9292
bind:showPrereleases
9393
class="my-8"
9494
/>
@@ -135,7 +135,7 @@
135135
<SidePanel
136136
packageName={data.currentPackage.pkg.name}
137137
allPackages={data.displayablePackages}
138-
otherReleases={data.otherReleases}
138+
otherReleases={data.allReleases}
139139
class="hidden h-fit w-140 lg:block"
140140
bind:showPrereleases
141141
/>

0 commit comments

Comments
 (0)