import React from 'dom-chef'; import select from 'select-dom'; import onetime from 'onetime'; import elementReady from 'element-ready'; import * as pageDetect from 'github-url-detection'; import features from '.'; import getDefaultBranch from '../github-helpers/get-default-branch'; import {getCleanPathname} from '../github-helpers'; async function is404(url: string): Promise { const {status} = await fetch(url, {method: 'head'}); return status === 404; } function getStrikeThrough(text: string): HTMLElement { return {text}; } async function checkAnchor(anchor: HTMLAnchorElement): Promise { if (await is404(anchor.href)) { anchor.replaceWith(getStrikeThrough(anchor.textContent!)); } } function parseCurrentURL(): string[] { const parts = getCleanPathname().split('/'); if (parts[2] === 'blob') { // Blob URLs are never useful parts[2] = 'tree'; } return parts; } // If the resource was deleted, link to the commit history async function addCommitHistoryLink(bar: Element): Promise { const parts = parseCurrentURL(); parts[2] = 'commits'; const url = '/' + parts.join('/'); if (await is404(location.origin + url)) { return; } bar.after(

See also the file’s commit history

); } // If the resource exists in the default branch, link to it async function addDefaultBranchLink(bar: Element): Promise { const parts = getCleanPathname().split('/'); const branch = parts[3]; if (!branch) { return; } const defaultBranch = await getDefaultBranch(); if (branch === defaultBranch) { return; } parts[3] = defaultBranch; // Change branch const url = '/' + parts.join('/'); if (await is404(location.origin + url)) { return; } bar.after(

See also the object on the default branch

); } function init(): false | void { const parts = parseCurrentURL(); if (parts.length <= 1 || !select.exists('[alt*="This is not the web page you are looking for"]')) { return false; } const bar =

; for (const [i, part] of parts.entries()) { if (i === 2 && ['tree', 'blob', 'edit'].includes(part)) { // Exclude parts that don't exist as standalones continue; } if (i === parts.length - 1) { // The last part of the URL is a known 404 bar.append(' / ', getStrikeThrough(part)); } else { const pathname = '/' + parts.slice(0, i + 1).join('/'); bar.append(i ? ' / ' : '', {part}); } } select('main > :first-child, #parallax_illustration')!.after(bar); // Check parts from right to left; skip the last part for (let i = bar.children.length - 2; i >= 0; i--) { void checkAnchor(bar.children[i] as HTMLAnchorElement); } if (parts[2] === 'tree' || parts[2] === 'blob') { // Object might be 410 Gone void addCommitHistoryLink(bar); } if (parts[2] === 'edit') { // File might not be available on the current branch // GitHub already redirects /tree/ and /blob/ natively void addDefaultBranchLink(bar); } } async function initPRCommit(): Promise { const commitUrl = location.href.replace(/pull\/\d+\/commits/, 'commit'); if (await is404(commitUrl)) { return false; } const blankSlateParagraph = await elementReady('.blankslate p'); blankSlateParagraph!.after(

You can also try to view the detached standalone commit.

); } void features.add({ id: __filebasename, description: 'Adds possible related pages and alternatives on 404 pages.', screenshot: 'https://user-images.githubusercontent.com/1402241/46402857-7bdada80-c733-11e8-91a1-856573078ff5.png' }, { include: [ pageDetect.is404 ], init: onetime(init) }, { include: [ pageDetect.isPRCommit404 ], waitForDomReady: false, init: onetime(initPRCommit) });