summaryrefslogtreecommitdiff
path: root/source/github-helpers
diff options
context:
space:
mode:
Diffstat (limited to 'source/github-helpers')
-rw-r--r--source/github-helpers/get-current-git-ref.test.ts114
-rw-r--r--source/github-helpers/get-current-git-ref.ts32
-rw-r--r--source/github-helpers/get-default-branch.ts42
-rw-r--r--source/github-helpers/github-url.ts4
-rw-r--r--source/github-helpers/index.ts50
-rw-r--r--source/github-helpers/is-default-branch.ts13
6 files changed, 177 insertions, 78 deletions
diff --git a/source/github-helpers/get-current-git-ref.test.ts b/source/github-helpers/get-current-git-ref.test.ts
new file mode 100644
index 00000000..9581f5db
--- /dev/null
+++ b/source/github-helpers/get-current-git-ref.test.ts
@@ -0,0 +1,114 @@
+import {assert, test} from 'vitest';
+
+// @ts-expect-error JS only
+import {navigateToBranch} from '../../test/fixtures/globals.js';
+import getCurrentGitRef from './get-current-git-ref.js';
+
+// The titles supplied here listed here are real, not guessed, except the error tester
+test('getCurrentGitRef', () => {
+ // Error testing
+ assert.equal(getCurrentGitRef(
+ '/',
+ 'some page title',
+ ), undefined, 'It should never throw with valid input');
+ assert.throws(() => getCurrentGitRef(
+ 'https://github.com',
+ 'github.com',
+ ));
+
+ // Root
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint',
+ 'typescript-eslint/typescript-eslint: Monorepo for all the tooling which enables ESLint to support TypeScript',
+ ), undefined);
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/tree/chore/lerna-4',
+ 'typescript-eslint/typescript-eslint at chore/lerna-4',
+ ), 'chore/lerna-4');
+
+ // Sub folder
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/tree/master/docs',
+ 'typescript-eslint/docs at master · typescript-eslint/typescript-eslint',
+ ), 'master');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/tree/chore/lerna-4/docs',
+ 'typescript-eslint/docs at chore/lerna-4 · typescript-eslint/typescript-eslint',
+ ), 'chore/lerna-4');
+
+ // Sub sub folder
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/tree/master/docs/getting-started',
+ 'typescript-eslint/docs/getting-started at master · typescript-eslint/typescript-eslint',
+ ), 'master');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/tree/chore/lerna-4/docs/getting-started',
+ 'typescript-eslint/docs/getting-started at chore/lerna-4 · typescript-eslint/typescript-eslint',
+ ), 'chore/lerna-4');
+
+ // File
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/README.md',
+ 'typescript-eslint/README.md at master · typescript-eslint/typescript-eslint',
+ ), 'master');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/blob/chore/lerna-4/docs/getting-started/README.md',
+ 'typescript-eslint/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint',
+ ), 'chore/lerna-4');
+
+ // Editing file
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/edit/master/docs/getting-started/README.md',
+ 'Editing typescript-eslint/README.md at master · typescript-eslint/typescript-eslint',
+ ), 'master');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/edit/chore/lerna-4/docs/getting-started/README.md',
+ 'Editing typescript-eslint/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint',
+ ), 'chore/lerna-4');
+
+ // Blame
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/blame/master/docs/getting-started/README.md',
+ 'typescript-eslint/docs/getting-started/README.md at master · typescript-eslint/typescript-eslint',
+ ), 'master');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/blame/chore/lerna-4/docs/getting-started/README.md',
+ 'typescript-eslint/docs/getting-started/README.md at chore/lerna-4 · typescript-eslint/typescript-eslint',
+ ), 'chore/lerna-4');
+
+ // Commits
+ navigateToBranch('master');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/commits/master/docs/getting-started/README.md',
+ 'History for docs/getting-started/README.md - typescript-eslint/typescript-eslint',
+ ), 'master');
+
+ navigateToBranch('chore/lerna-4');
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/commits/chore/lerna-4/docs/getting-started/README.md',
+ 'History for docs/getting-started/README.md - typescript-eslint/typescript-eslint',
+ ), 'chore/lerna-4');
+
+ navigateToBranch('this/branch/has/many/slashes');
+ assert.equal(getCurrentGitRef(
+ '/yakov116/TestR/commits/this/branch/has/many/slashes',
+ 'Commits · yakov116/TestR',
+ ), 'this/branch/has/many/slashes');
+
+ // Single commit
+ assert.equal(getCurrentGitRef(
+ '/typescript-eslint/typescript-eslint/commit/795fd1c529ee58e97283c9ddf8463703517b50ab',
+ 'chore: add markdownlint (#1889) · typescript-eslint/typescript-eslint@795fd1c',
+ ), '795fd1c529ee58e97283c9ddf8463703517b50ab');
+
+ // Branch includes period
+ assert.equal(getCurrentGitRef(
+ '/anggrayudi/SimpleStorage/tree/release/0.8.0',
+ 'anggrayudi/SimpleStorage at release/0.8.0',
+ ), 'release/0.8.0');
+
+ assert.equal(getCurrentGitRef(
+ '/ksh-code/repository/tree/h.l.o.o',
+ 'ksh-code/repository at h.l.o.o',
+ ), 'h.l.o.o');
+});
diff --git a/source/github-helpers/get-current-git-ref.ts b/source/github-helpers/get-current-git-ref.ts
new file mode 100644
index 00000000..4eefbabb
--- /dev/null
+++ b/source/github-helpers/get-current-git-ref.ts
@@ -0,0 +1,32 @@
+import {getCurrentBranchFromFeed} from './index.js';
+
+const typesWithGitRef = new Set(['tree', 'blob', 'blame', 'edit', 'commit', 'commits', 'compare']);
+const titleWithGitRef = / at (?<branch>[.\w-/]+)( · [\w-]+\/[\w-]+)?$/i;
+
+/** This only works with URL and page title. Must not be async because it's used by GitHubURL */
+export default function getCurrentGitRef(pathname = location.pathname, title = document.title): string | undefined {
+ if (!pathname.startsWith('/')) {
+ throw new TypeError(`Expected pathname starting with /, got "${pathname}"`);
+ }
+
+ const [type, gitRefIfNonSlashed] = pathname.split('/').slice(3);
+ if (!type || !typesWithGitRef.has(type)) {
+ // Root; or piece of information not applicable to the page
+ return;
+ }
+
+ // Slashed branches on `commits`
+ if (type === 'commits') {
+ return getCurrentBranchFromFeed()!;
+ }
+
+ // Slashed branches on `blob` and `tree`
+ const parsedTitle = titleWithGitRef.exec(title);
+ if (parsedTitle) {
+ return parsedTitle.groups!.branch;
+ }
+
+ // Couldn't ensure it's not slashed, so we'll return the first piece whether correct or not
+ // TODO: extract from `ref-selector` if available https://github.com/refined-github/refined-github/issues/6557
+ return gitRefIfNonSlashed;
+}
diff --git a/source/github-helpers/get-default-branch.ts b/source/github-helpers/get-default-branch.ts
index 34f5bd11..efbbdd4c 100644
--- a/source/github-helpers/get-default-branch.ts
+++ b/source/github-helpers/get-default-branch.ts
@@ -1,44 +1,24 @@
import cache from 'webext-storage-cache';
-import select from 'select-dom';
import elementReady from 'element-ready';
import * as pageDetect from 'github-url-detection';
import * as api from './api.js';
-import {getRepo, getCurrentBranchFromFeed} from './index.js';
+import {getRepo} from './index.js';
import {branchSelector} from './selectors.js';
-// This regex should match all of these combinations:
-// "This branch is even with master."
-// "This branch is 1 commit behind master."
-// "This branch is 1 commit ahead of master."
-// "This branch is 1 commit ahead, 27 commits behind master."
-const branchInfoRegex = /([^ ]+)\.$/;
+const isCurrentRepo = ({nameWithOwner}: pageDetect.RepositoryInfo): boolean => Boolean(getRepo()?.nameWithOwner === nameWithOwner);
// DO NOT use optional arguments/defaults in "cached functions" because they can't be memoized effectively
// https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1864
-const _getDefaultBranch = cache.function('default-branch', async function (repository: pageDetect.RepositoryInfo): Promise<string> {
- if (arguments.length === 0 || JSON.stringify(repository) === JSON.stringify(getRepo())) {
- if (pageDetect.isRepoHome()) {
- const branchPicker = await elementReady(branchSelector);
- if (branchPicker) {
- return branchPicker.title === 'Switch branches or tags'
- ? branchPicker.textContent!.trim()
- : branchPicker.title;
- }
- }
-
- const defaultBranch = getCurrentBranchFromFeed();
- if (defaultBranch) {
- return defaultBranch;
- }
-
- if (!pageDetect.isForkedRepo()) {
- // We can find the name in the infobar, available in folder views
- const branchInfo = select('.branch-infobar')?.textContent!.trim();
- const defaultBranch = branchInfoRegex.exec(branchInfo!)?.[1];
- if (defaultBranch) {
- return defaultBranch;
- }
+const _getDefaultBranch = cache.function('default-branch', async (repository: pageDetect.RepositoryInfo): Promise<string> => {
+ // TODO: extract from `ref-selector` if available https://github.com/refined-github/refined-github/issues/6557
+ if (isCurrentRepo(repository) && ['', 'commits'].includes(repository.path)) {
+ // We're on the default branch, so we can extract it from the current page. This usually happens on the pages:
+ // @example /user/repo
+ // @example /user/repo/commits (without further path)
+ const branchPicker = await elementReady(branchSelector);
+ if (branchPicker) {
+ return branchPicker.textContent!.trim();
}
}
diff --git a/source/github-helpers/github-url.ts b/source/github-helpers/github-url.ts
index 2f592f50..e31b307c 100644
--- a/source/github-helpers/github-url.ts
+++ b/source/github-helpers/github-url.ts
@@ -1,4 +1,4 @@
-import {getCurrentCommittish} from './index.js';
+import getCurrentGitRef from './get-current-git-ref.js';
export default class GitHubURL {
user = '';
@@ -37,7 +37,7 @@ export default class GitHubURL {
const filePath = ambiguousReference.slice(1).join('/');
- const currentBranch = getCurrentCommittish();
+ const currentBranch = getCurrentGitRef();
const currentBranchSections = currentBranch?.split('/');
if (
!currentBranch // Current branch could not be determined (1/2)
diff --git a/source/github-helpers/index.ts b/source/github-helpers/index.ts
index c38acc83..883b10df 100644
--- a/source/github-helpers/index.ts
+++ b/source/github-helpers/index.ts
@@ -20,13 +20,12 @@ export function getConversationNumber(): number | undefined {
return undefined;
}
-export function getCurrentBranchFromFeed(): string | void {
- // Not `isRepoCommitList` because this works exclusively on the default branch
- if (getRepo()!.path !== 'commits') {
- return;
+export function getCurrentBranchFromFeed(): string {
+ const feedLink = select('link[type="application/atom+xml"]');
+ if (!feedLink) {
+ throw new Error('getCurrentBranchFromFeed() is only available on commit lists');
}
- const feedLink = select('link[type="application/atom+xml"]')!;
return new URL(feedLink.href)
.pathname
.split('/')
@@ -35,45 +34,6 @@ export function getCurrentBranchFromFeed(): string | void {
.replace(/\.atom$/, '');
}
-const typesWithCommittish = new Set(['tree', 'blob', 'blame', 'edit', 'commit', 'commits', 'compare']);
-const titleWithCommittish = / at (?<branch>[.\w-/]+)( · [\w-]+\/[\w-]+)?$/i;
-export const getCurrentCommittish = (pathname = location.pathname, title = document.title): string | undefined => {
- if (!pathname.startsWith('/')) {
- throw new TypeError(`Expected pathname starting with /, got "${pathname}"`);
- }
-
- const [type, unslashedCommittish] = pathname.split('/').slice(3);
- if (!type || !typesWithCommittish.has(type)) {
- // Root; or piece of information not applicable to the page
- return;
- }
-
- // Handle slashed branches in commits pages
- if (type === 'commits') {
- if (!unslashedCommittish) {
- return getCurrentBranchFromFeed()!;
- }
-
- const branchAndFilepath = pathname.split('/').slice(4).join('/');
-
- // List of all commits of current branch (no filename)
- if (title.startsWith('Commits · ')) {
- return branchAndFilepath;
- }
-
- // List of commits touching a particular file ("History")
- const filepath = /^History for ([^ ]+) - /.exec(title)![1];
- return branchAndFilepath.slice(0, branchAndFilepath.lastIndexOf('/' + filepath));
- }
-
- const parsedTitle = titleWithCommittish.exec(title);
- if (parsedTitle) {
- return parsedTitle.groups!.branch;
- }
-
- return unslashedCommittish;
-};
-
export const isMac = navigator.userAgent.includes('Macintosh');
type Not<Yes, Not> = Yes extends Not ? never : Yes;
@@ -139,7 +99,7 @@ const cachePerPage = {
/** Is tag or commit, with elementReady */
export const isPermalink = mem(async () => {
- // No need for getCurrentCommittish(), it's a simple and exact check
+ // No need for getCurrentGitRef(), it's a simple and exact check
if (/^[\da-f]{40}$/.test(location.pathname.split('/')[4])) {
// It's a commit
return true;
diff --git a/source/github-helpers/is-default-branch.ts b/source/github-helpers/is-default-branch.ts
new file mode 100644
index 00000000..82ef0be1
--- /dev/null
+++ b/source/github-helpers/is-default-branch.ts
@@ -0,0 +1,13 @@
+import getDefaultBranch from './get-default-branch.js';
+import getCurrentGitRef from './get-current-git-ref.js';
+
+/** Detects if the current view is on the default branch. To be used on file/folder/commit lists */
+export default async function isDefaultBranch(): Promise<boolean> {
+ const currentBranch = getCurrentGitRef();
+ if (!currentBranch) {
+ // This happens on the repo root OR on views that are not branch-specific (like isIssue)
+ return true;
+ }
+
+ return currentBranch === await getDefaultBranch();
+}