Handle GitHub route links in README rebasing
Alessandro committed
Mar 28, 2026 at 14:47 UTC
baa806558e91b3ca6fc4c60335309d28aad25c95
1 file changed
+57
-19
plugins/_plugin_installer/webui/pluginInstallStore.js
+57
-19
@@ -97,30 +97,68 @@ const model = {
97
.split("/");
98
if (!owner || !repo) return html;
99
100
- const repoBlobBase = `https://github.com/${owner}/${repo}/blob/${branch}`;
100
+ const repoWebBase = `https://github.com/${owner}/${repo}`;
101
+ const repoBlobBase = `${repoWebBase}/blob/${branch}`;
102
+ const repoRawBase = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
103
const doc = new DOMParser().parseFromString(html, "text/html");
104
+ const githubRepoRoutePrefixes = new Set([
105
+ "actions",
106
+ "blob",
107
+ "branches",
108
+ "commit",
109
+ "commits",
110
+ "compare",
111
+ "discussions",
112
+ "issues",
113
+ "labels",
114
+ "milestones",
115
+ "packages",
116
+ "projects",
117
+ "pulls",
118
+ "raw",
119
+ "releases",
120
+ "security",
121
+ "tags",
122
+ "tree",
123
+ "wiki",
124
+ ]);
125
+ const shouldSkipRebase = (value) =>
126
+ !value ||
127
+ value.startsWith("#") ||
128
+ value.startsWith("//") ||
129
+ /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(value);
130
+
131
+ const resolveRepoPath = (value) => {
132
+ if (shouldSkipRebase(value)) return null;
133
+ try {
134
+ const resolved = new URL(value, "https://repo-root.invalid/");
135
+ return `${resolved.pathname.replace(/^\/+/, "")}${resolved.search}${resolved.hash}`;
136
+ } catch {
137
+ return null;
138
+ }
139
+ };
140
+ const isRepoRoutePath = (repoPath) => {
141
+ const pathOnly = repoPath
142
+ .split(/[?#]/, 1)[0]
143
+ .replace(/^\/+|\/+$/g, "");
144
+ if (!pathOnly) return false;
145
+ const firstSegment = pathOnly.split("/")[0].toLowerCase();
146
+ return githubRepoRoutePrefixes.has(firstSegment);
147
+ };
148
149
doc.querySelectorAll("a[href]").forEach((anchor) => {
150
const href = (anchor.getAttribute("href") || "").trim();
105
- if (
106
- !href ||
107
- href.startsWith("#") ||
108
- href.startsWith("//") ||
109
- /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(href)
110
- ) {
111
- return;
112
- }
151
+ const repoPath = resolveRepoPath(href);
152
+ if (!repoPath) return;
153
+ const base = isRepoRoutePath(repoPath) ? repoWebBase : repoBlobBase;
154
+ anchor.setAttribute("href", `${base}/${repoPath}`);
155
+ });
156
114
- try {
115
- const resolved = new URL(href, "https://repo-root.invalid/");
116
- const repoPath = resolved.pathname.replace(/^\/+/, "");
117
- anchor.setAttribute(
118
- "href",
119
- `${repoBlobBase}/${repoPath}${resolved.search}${resolved.hash}`,
120
- );
121
- } catch {
122
- // Leave malformed links unchanged.
123
- }
157
+ doc.querySelectorAll("img[src]").forEach((image) => {
158
+ const src = (image.getAttribute("src") || "").trim();
159
+ const repoPath = resolveRepoPath(src);
160
+ if (!repoPath) return;
161
+ image.setAttribute("src", `${repoRawBase}/${repoPath}`);
162
});
163
164
return doc.body.innerHTML;