GitHub First Commit

A simple userscript to view the very first commit of a GitHub repository.

  1. // ==UserScript==
  2. // @name GitHub First Commit
  3. // @namespace https://github.com/lazypolymath
  4. // @version 1.0.0
  5. // @author https://github.com/lazypolymath
  6. // @description A simple userscript to view the very first commit of a GitHub repository.
  7. // @license MIT
  8. // @icon https://github.githubassets.com/favicons/favicon.svg
  9. // @source https://github.com/lazypolymath/first-commit
  10. // @match https://github.com/*/*
  11. // @connect api.github.com
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. (async () => {
  18. const tabContainerSelector = ".position-relative.header-wrapper.js-header-wrapper > header > div.AppHeader-localBar > nav > ul";
  19. const renderTab = async () => {
  20. var _a, _b;
  21. const url = new URL(document.location.href);
  22. let pathname = url.pathname.substring(1);
  23. const segments = pathname.split("/");
  24. const author = segments[0].trim();
  25. const repositoryName = segments[1].trim();
  26. if (author === "" || repositoryName === "") {
  27. return;
  28. }
  29. const tabContainerEl = document.querySelector(tabContainerSelector);
  30. if (!tabContainerEl) {
  31. return;
  32. }
  33. const lastEl = tabContainerEl.querySelector("li:last-child");
  34. if (!lastEl) {
  35. return;
  36. }
  37. let response = await fetch(
  38. `https://api.github.com/repos/${author}/${repositoryName}/commits?per_page=1&page=1`
  39. );
  40. const linkHeader = response.headers.get("link");
  41. const matches = /rel="next",\s\<(.*?)\>;\srel="last"/.exec(
  42. linkHeader || ""
  43. );
  44. if (!matches || !matches.length) {
  45. return;
  46. }
  47. const firstCommitURL = matches[1];
  48. response = await fetch(firstCommitURL);
  49. const json = await response.json();
  50. if (json && json.length && json[0].sha) {
  51. const data = json[0];
  52. const sha = data.sha;
  53. const commit = data.commit;
  54. const firstCommitTabEl = lastEl.cloneNode(true);
  55. (_a = firstCommitTabEl.querySelector("a")) == null ? void 0 : _a.remove();
  56. const tabLinkEl = document.createElement("a");
  57. tabLinkEl.id = "first-commit-tab-link";
  58. tabLinkEl.title = `Message: ${commit.message}
  59. Author: ${commit.committer.name || "Anonymous"}
  60. DateTime: ${commit.committer.date}
  61. Comments: ${commit.comment_count || 0}`;
  62. tabLinkEl.setAttribute(
  63. "class",
  64. ((_b = lastEl.querySelector("a")) == null ? void 0 : _b.getAttribute("class")) || ""
  65. );
  66. tabLinkEl.href = `https://github.com/${author}/${repositoryName}/commit/${sha}`;
  67. tabLinkEl.innerHTML = `
  68. <svg aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 -2 20 20" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg>
  69. <span data-content="First Commit">First Commit</span>
  70. `;
  71. tabContainerEl.append(tabLinkEl);
  72. }
  73. };
  74. const handleHistoryChange = async () => {
  75. setTimeout(async () => {
  76. if (document.querySelector("#first-commit-tab-link")) {
  77. return;
  78. }
  79. await renderTab();
  80. }, 1e3);
  81. };
  82. (function(history) {
  83. const originalPushState = history.pushState;
  84. history.pushState = function(...args) {
  85. const result = originalPushState.apply(this, args);
  86. const pushStateEvent = new CustomEvent("pushState", {
  87. detail: { ...args }
  88. });
  89. window.dispatchEvent(pushStateEvent);
  90. return result;
  91. };
  92. })(window.history);
  93. window.addEventListener("popstate", handleHistoryChange);
  94. window.addEventListener("pushState", handleHistoryChange);
  95. await renderTab();
  96. })();
  97.  
  98. })();