-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
index.ts
147 lines (120 loc) · 5.5 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import select from 'select-dom';
import onetime from 'onetime';
import elementReady from 'element-ready';
import compareVersions from 'tiny-version-compare';
import * as pageDetect from 'github-url-detection';
// This never changes, so it can be cached here
export const getUsername = onetime(pageDetect.utils.getUsername);
export const {getRepositoryInfo: getRepo, getCleanPathname} = pageDetect.utils;
export const getConversationNumber = (): string | undefined => {
if (pageDetect.isPR() || pageDetect.isIssue()) {
return location.pathname.split('/')[4];
}
return undefined;
};
/**
Tested on isRepoTree, isBlame, isSingleFile, isEditFile, isSingleCommit, isCommitList, isCompare. Subtly incompatible with isPR
Example tag content on public repositories: https://github.com/sindresorhus/refined-github/commits/branch-or-commit/even/with/slashes.atom
Example tag content on private repositories https://github.com/private/private/commits/master.atom?token=AEAXKWNRHXA2XJ2ZWCMGUUN44LM62
*/
export const getCurrentBranch = (): string | undefined => {
// .last needed for #2799
const feedLink = select.last('link[type="application/atom+xml"]');
// The feedLink is not available on `isIssue` #3641
if (!feedLink) {
return;
}
return new URL(feedLink.href)
.pathname
.split('/')
.slice(4) // Drops the initial /user/repo/route/ part
.join('/')
.replace(/\.atom$/, '');
};
export const isFirefox = navigator.userAgent.includes('Firefox/');
// The type requires at least one parameter https://stackoverflow.com/a/49910890
export const buildRepoURL = (...pathParts: Array<string | number> & {0: string}): string => {
for (const part of pathParts) {
// TODO: Can TypeScript take care of this? With https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/#template-literal-types
if (typeof part === 'string' && /^\/|\/$/.test(part)) {
throw new TypeError('The path parts shouldn’t start or end with a slash: ' + part);
}
}
return [location.origin, getRepo()?.nameWithOwner, ...pathParts].join('/');
};
export const getPRHeadRepo = (): ReturnType<typeof getRepo> => {
const headLink = select('.commit-ref.head-ref a');
return getRepo(headLink);
};
export function getForkedRepo(): string | undefined {
return select('meta[name="octolytics-dimension-repository_parent_nwo"]')?.content;
}
export const parseTag = (tag: string): {version: string; namespace: string} => {
const [, namespace = '', version = ''] = /(?:(.*)@)?([^@]+)/.exec(tag) ?? [];
return {namespace, version};
};
export function compareNames(username: string, realname: string): boolean {
return username.replace(/-/g, '').toLowerCase() === realname.normalize('NFD').replace(/[\u0300-\u036F\W.]/g, '').toLowerCase();
}
const validVersion = /^[vr]?\d+(?:\.\d+)+/;
const isPrerelease = /^[vr]?\d+(?:\.\d+)+(-\d)/;
export function getLatestVersionTag(tags: string[]): string {
// Some tags aren't valid versions; comparison is meaningless.
// Just use the latest tag returned by the API (reverse chronologically-sorted list)
if (!tags.every(tag => validVersion.test(tag))) {
return tags[0];
}
// Exclude pre-releases
let releases = tags.filter(tag => !isPrerelease.test(tag));
if (releases.length === 0) { // They were all pre-releases; undo.
releases = tags;
}
let latestVersion = releases[0];
for (const release of releases) {
if (compareVersions(latestVersion, release) < 0) {
latestVersion = release;
}
}
return latestVersion;
}
const escapeRegex = (string: string): string => string.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
const prCommitPathnameRegex = /[/][^/]+[/][^/]+[/]pull[/](\d+)[/]commits[/]([\da-f]{7})[\da-f]{33}(?:#[\w-]+)?\b/; // eslint-disable-line unicorn/better-regex
export const prCommitUrlRegex = new RegExp('\\b' + escapeRegex(location.origin) + prCommitPathnameRegex.source, 'gi');
const prComparePathnameRegex = /[/][^/]+[/][^/]+[/]compare[/](.+)(#diff-[\da-fR-]+)/; // eslint-disable-line unicorn/better-regex
export const prCompareUrlRegex = new RegExp('\\b' + escapeRegex(location.origin) + prComparePathnameRegex.source, 'gi');
// To be used as replacer callback in string.replace()
export function preventPrCommitLinkLoss(url: string, pr: string, commit: string, index: number, fullText: string): string {
if (fullText[index + url.length] === ')') {
return url;
}
return `[\`${commit}\` (#${pr})](${url})`;
}
// To be used as replacer callback in string.replace() for compare links
export function preventPrCompareLinkLoss(url: string, compare: string, hash: string, index: number, fullText: string): string {
if (fullText[index + url.length] === ')') {
return url;
}
return `[\`${compare}\`${hash.slice(0, 16)}](${url})`;
}
// https://github.com/idimetrix/text-case/blob/master/packages/upper-case-first/src/index.ts
export function upperCaseFirst(input: string): string {
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
}
/** Is tag or commit, with elementReady */
export async function isPermalink(): Promise<boolean> {
if (/^[\da-f]{40}$/.test(getCurrentBranch()!)) {
// It's a commit
return true;
}
await elementReady('[data-hotkey="w"]');
return (
// Pre "Latest commit design updates"
/Tag|Tree/.test(select('[data-hotkey="w"] i')?.textContent!) || // Text appears in the branch selector
// "Latest commit design updates"
select.exists('[data-hotkey="w"] .octicon-tag') // Tags have an icon
);
}
// Negative function so it can be used directly in `exclude` array
export function isNotRefinedGitHubRepo(): boolean {
return !location.pathname.startsWith('/sindresorhus/refined-github/');
}