From 2d42cb3edf816cbb46c6140e75c2e1ca2ca888d0 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Fri, 30 Aug 2024 17:17:34 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20#144=20Tolerate=20badly?= =?UTF-8?q?=20formatted=20URLs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit URL constructor should not throw error anymore. Known special paths are ignored. Closes: #144 --- src/github/url-parse.ts | 13 +++++++++++-- src/github/urls.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/inline/inline.ts | 16 +++++++++++++--- src/inline/view-plugin.ts | 12 ++++++++++-- src/query/column/base.ts | 2 +- src/query/column/issue.ts | 4 +++- 6 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 src/github/urls.ts diff --git a/src/github/url-parse.ts b/src/github/url-parse.ts index 8486490..f63e357 100644 --- a/src/github/url-parse.ts +++ b/src/github/url-parse.ts @@ -1,3 +1,5 @@ +import { logger } from "../plugin"; + export interface ParsedUrl { url: string; host: string; @@ -18,7 +20,7 @@ export function repoAPIToBrowserUrl(urlString: string): string { return urlString.replace(apiRegex, "https://github.com/"); } -export function parseUrl(urlString: string): ParsedUrl { +export function parseUrl(urlString: string): ParsedUrl | null { // Some potential URLs: // https://github.com/nathonius/alloy-theme // https://github.com/nathonius/obsidian-trello/issues/45 @@ -28,7 +30,14 @@ export function parseUrl(urlString: string): ParsedUrl { // https://github.com/nathonius/obsidian-trello/blob/markdown/src/constants.ts#L44-L58 // https://github.com/nathonius/obsidian-trello/commit/7ea069dd0641441ec20fb194f50e746a21abbaf1 // https://github.com/nathonius/obsidian-trello/commit/7ea069dd0641441ec20fb194f50e746a21abbaf1#diff-8fa4b52909f895e8cda060d2035234e0a42ca2c7d3f8f8de1b35a056537bf199R35 - const url = new URL(urlString); + let url: URL; + + try { + url = new URL(urlString); + } catch (err) { + logger.error(`Attempted and failed to parse invalid URL: ${urlString}`); + return null; + } const parsedUrl: ParsedUrl = { url: urlString, host: url.hostname }; const urlParts = url.pathname.split("/"); diff --git a/src/github/urls.ts b/src/github/urls.ts new file mode 100644 index 0000000..c2caeb7 --- /dev/null +++ b/src/github/urls.ts @@ -0,0 +1,39 @@ +/** + * These are paths that begin with `https://github.com` but should not + * be processed by the plugin. + */ +const knownPaths = [ + "about", + "account", + "codespaces", + "dashboard", + "discussions", + "explore", + "gist", + "github-copilot", + "issues", + "logout", + "marketplace", + "mine", + "new", + "organizations", + "projects", + "pulls", + "search", + "security", + "settings", + "sponsors", +]; + +export function isAllowedPath(link: string): boolean { + try { + const url = new URL(link); + const basePath = url.pathname.split("/")[1]; + if (knownPaths.includes(basePath)) { + return false; + } + } catch (err) { + return false; + } + return true; +} diff --git a/src/inline/inline.ts b/src/inline/inline.ts index 4f46a9d..bfc16e2 100644 --- a/src/inline/inline.ts +++ b/src/inline/inline.ts @@ -8,14 +8,18 @@ import { RequestError } from "../util"; import { parseUrl } from "../github/url-parse"; import type { ParsedUrl } from "../github/url-parse"; import { getIssue, getPullRequest } from "../github/github"; +import { isAllowedPath } from "../github/urls"; interface TagConfig { icon: HTMLSpanElement; sections: HTMLElement[]; } -export function createTag(href: string): HTMLAnchorElement { +export function createTag(href: string): HTMLAnchorElement | null { const parsedUrl = parseUrl(href); + if (!parsedUrl) { + return null; + } const container = createEl("a", { cls: "github-link-inline", href, attr: { target: "_blank" } }); const config: TagConfig = { icon: createSpan({ cls: ["github-link-status-icon", "github-link-inline-icon"] }), @@ -162,11 +166,17 @@ function createErrorSection(config: TagConfig, container: HTMLAnchorElement, err } export function InlineRenderer(el: HTMLElement) { - const githubLinks = el.querySelectorAll(`a.external-link[href^="https://github.com"]`); + let githubLinks = Array.from(el.querySelectorAll(`a.external-link[href^="https://github.com"]`)); + + // Filter out some special URLs from github + githubLinks = githubLinks.filter((l) => isAllowedPath(l.href)); + for (const anchor of Array.from(githubLinks)) { if (anchor.href === anchor.innerText) { const container = createTag(anchor.href); - anchor.replaceWith(container); + if (container) { + anchor.replaceWith(container); + } } } } diff --git a/src/inline/view-plugin.ts b/src/inline/view-plugin.ts index 35003d1..9860d0b 100644 --- a/src/inline/view-plugin.ts +++ b/src/inline/view-plugin.ts @@ -3,6 +3,7 @@ import type { DecorationSet, PluginValue, EditorView, ViewUpdate } from "@codemi import { Decoration, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view"; import type { GithubLinkPlugin } from "../plugin"; +import { isAllowedPath } from "../github/urls"; import { createTag } from "./inline"; interface DecoSpec { @@ -28,7 +29,9 @@ class InlineTagWidget extends WidgetType { toDOM(): HTMLElement { const container = createSpan(); const tag = createTag(this.href); - container.appendChild(tag); + if (tag) { + container.appendChild(tag); + } return container; } } @@ -83,6 +86,11 @@ export function createInlineViewPlugin(_plugin: GithubLinkPlugin) { return false; } + // Check if it's not an allowed URL + if (!isAllowedPath(match[0])) { + return false; + } + // Check if we're in a codeblock // Note, codeblock check is more expensive than some others, // But we do it first to ensure this.inCodeblock remains accurate @@ -116,7 +124,7 @@ export function createInlineViewPlugin(_plugin: GithubLinkPlugin) { } // Ignore matches inside a markdown link - // TODO: This could probably be a regex. + // TODO: This can probably be removed now that the regex is updated const input = match.input ?? ""; const index = match.index ?? 0; const matchValue = match[0]; diff --git a/src/query/column/base.ts b/src/query/column/base.ts index d52f184..9657c80 100644 --- a/src/query/column/base.ts +++ b/src/query/column/base.ts @@ -57,7 +57,7 @@ export const CommonIssuePRColumns: ColumnsMap = { el.classList.add("github-link-table-repo"); const url = repoAPIToBrowserUrl((row as IssueListResponse[number]).repository_url); const parsed = parseUrl(url); - el.createEl("a", { text: parsed.repo, href: url, attr: { target: "_blank" } }); + el.createEl("a", { text: parsed?.repo ?? "Repo", href: url, attr: { target: "_blank" } }); }, }, author: { diff --git a/src/query/column/issue.ts b/src/query/column/issue.ts index d7f0833..079ff6f 100644 --- a/src/query/column/issue.ts +++ b/src/query/column/issue.ts @@ -36,7 +36,9 @@ export const IssueColumns: ColumnsMap = { return; } const tag = createTag(pullRequestUrl); - el.appendChild(tag); + if (tag) { + el.appendChild(tag); + } }, }, };