diff --git a/index.test.ts b/index.test.ts index 8ac2212..ff2ecd6 100644 --- a/index.test.ts +++ b/index.test.ts @@ -236,6 +236,61 @@ test('getRepositoryInfo', () => { ], } `); + expect(getRepositoryInfoAdapter('https://github.com/refined-github/sandbox/blob/bracket-in-path/foo%2F%5Bbar%5D%2Fbaz')).toMatchInlineSnapshot(` + { + "name": "sandbox", + "nameWithOwner": "refined-github/sandbox", + "owner": "refined-github", + "path": "blob/bracket-in-path/foo/[bar]/baz", + "pathParts": [ + "blob", + "bracket-in-path", + "foo", + "[bar]", + "baz", + ], + } + `); + expect(getRepositoryInfoAdapter('https://github.com/refined-github/sandbox/blob/bracket-in-path/foo%2F%5Bbar%5D%2F%2Fbaz')).toMatchInlineSnapshot(` + { + "name": "sandbox", + "nameWithOwner": "refined-github/sandbox", + "owner": "refined-github", + "path": "blob/bracket-in-path/foo/[bar]/baz", + "pathParts": [ + "blob", + "bracket-in-path", + "foo", + "[bar]", + "baz", + ], + } + `); + expect(getRepositoryInfoAdapter('https://github.com/refined-github/sandbox/blob/bracket-in-path/foo%252F%5Bbar%5D%252Fbaz')).toMatchInlineSnapshot(` + { + "name": "sandbox", + "nameWithOwner": "refined-github/sandbox", + "owner": "refined-github", + "path": "blob/bracket-in-path/foo%2F[bar]%2Fbaz", + "pathParts": [ + "blob", + "bracket-in-path", + "foo%2F[bar]%2Fbaz", + ], + } + `); + expect(getRepositoryInfoAdapter('https://github.com/refined-github/sandbox/tree/%F0%9F%98%B1')).toMatchInlineSnapshot(` + { + "name": "sandbox", + "nameWithOwner": "refined-github/sandbox", + "owner": "refined-github", + "path": "tree/😱", + "pathParts": [ + "tree", + "😱", + ], + } + `); } }); diff --git a/index.ts b/index.ts index 06a0547..c6d63f7 100644 --- a/index.ts +++ b/index.ts @@ -1007,8 +1007,19 @@ TEST: addTests('isNewRepoTemplate', [ /** Get the logged-in user’s username */ const getLoggedInUser = (): string | undefined => $('meta[name="user-login"]')?.getAttribute('content') ?? undefined; -/** Drop all redundant slashes */ -const getCleanPathname = (url: URL | HTMLAnchorElement | Location = location): string => url.pathname.replaceAll(/\/\/+/g, '/').replace(/\/$/, '').slice(1); +/** Drop all redundant slashes. You may want to use `getDecodedPathname` first */ +const getCleanPathname = (url: URL | HTMLAnchorElement | string | Location = location): string => + (typeof url === 'string' ? url : url.pathname) + .replaceAll(/\/\/+/g, '/') + .replace(/\/$/, '') + .slice(1); + +/** Decode all path parts */ +const getDecodedPathname = (url: URL | HTMLAnchorElement | Location = location): string => + url.pathname + .split('/') + .map(part => decodeURIComponent(part)) + .join('/'); const getCleanGistPathname = (url: URL | HTMLAnchorElement | Location = location): string | undefined => { const pathname = getCleanPathname(url); @@ -1081,7 +1092,7 @@ const getRepo = (url?: URL | HTMLAnchorElement | Location | string): RepositoryI return; } - const [owner, name, ...pathParts] = getCleanPathname(url).split('/') as [string, string, string]; + const [owner, name, ...pathParts] = getCleanPathname(getDecodedPathname(url)).split('/') as [string, string, string]; return { owner, name, @@ -1095,6 +1106,7 @@ export const utils = { getOrg, getLoggedInUser, getCleanPathname, + getDecodedPathname, getCleanGistPathname, getRepositoryInfo: getRepo, parseRepoExplorerTitle,