Github JSON Dependencies Linker

Linkify all dependencies found in an JSON file.

当前为 2024-02-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Github JSON Dependencies Linker
  3. // @id Github_JSON_Dependencies_Linker@https://github.com/jerone/UserScripts
  4. // @namespace https://github.com/jerone/UserScripts
  5. // @description Linkify all dependencies found in an JSON file.
  6. // @author jerone
  7. // @copyright 2015+, jerone (https://github.com/jerone)
  8. // @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
  9. // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
  10. // @homepage https://github.com/jerone/UserScripts/tree/master/Github_JSON_Dependencies_Linker
  11. // @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_JSON_Dependencies_Linker
  12. // @supportURL https://github.com/jerone/UserScripts/issues
  13. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VCYMHWQ7ZMBKW
  14. // @version 0.3.2
  15. // @icon https://github.githubassets.com/pinned-octocat.svg
  16. // @grant GM_xmlhttpRequest
  17. // @run-at document-end
  18. // @include https://github.com/*/package.json
  19. // @include https://github.com/*/npm-shrinkwrap.json
  20. // @include https://github.com/*/bower.json
  21. // @include https://github.com/*/project.json
  22. // ==/UserScript==
  23.  
  24. // cSpell:ignore linkify, Sindre Sorhus
  25. /* eslint security/detect-object-injection: "off" */
  26. /* global GM_xmlhttpRequest */
  27.  
  28. (function () {
  29. var blobElm = document.querySelector(".highlight"),
  30. blobLineElms = blobElm.querySelectorAll(".blob-code > span"),
  31. pkg = (function () {
  32. try {
  33. // JSON parser could fail on JSON with comments.
  34. return JSON.parse(blobElm.textContent);
  35. } catch (ex) {
  36. // Strip out comments from the JSON and try again.
  37. return JSON.parse(stripJsonComments(blobElm.textContent));
  38. }
  39. })(),
  40. isNPM =
  41. location.pathname.endsWith("/package.json") ||
  42. location.pathname.endsWith("/npm-shrinkwrap.json"),
  43. isBower = location.pathname.endsWith("/bower.json"),
  44. isNuGet = location.pathname.endsWith("/project.json"),
  45. isAtom = (function () {
  46. if (location.pathname.endsWith("/package.json")) {
  47. if (pkg.atomShellVersion) {
  48. return true;
  49. } else if (pkg.engines && pkg.engines.atom) {
  50. return true;
  51. }
  52. }
  53. return false;
  54. })(),
  55. dependencyKeys = [
  56. "dependencies",
  57. "devDependencies",
  58. "peerDependencies",
  59. "bundleDependencies",
  60. "bundledDependencies",
  61. "packageDependencies",
  62. "optionalDependencies",
  63. ],
  64. modules = (function () {
  65. var _modules = {};
  66. dependencyKeys.forEach(function (dependencyKey) {
  67. _modules[dependencyKey] = [];
  68. });
  69. return _modules;
  70. })();
  71.  
  72. // Get an unique list of all modules.
  73. function fetchModules(root) {
  74. dependencyKeys.forEach(function (dependencyKey) {
  75. var dependencies = root[dependencyKey] || {};
  76. Object.keys(dependencies).forEach(function (module) {
  77. if (modules[dependencyKey].indexOf(module) === -1) {
  78. modules[dependencyKey].push(module);
  79. }
  80. fetchModules(dependencies[module]);
  81. });
  82. });
  83. }
  84. fetchModules(pkg);
  85.  
  86. // Linkify module.
  87. function linkify(module, url) {
  88. // Try to find the module; could be multiple locations.
  89. var moduleFilterText = '"' + module + '"';
  90. var moduleElms = Array.prototype.filter.call(
  91. blobLineElms,
  92. function (blobLineElm) {
  93. if (blobLineElm.textContent.trim() === moduleFilterText) {
  94. // Module name preceding a colon is never a key.
  95. var prev = blobLineElm.previousSibling;
  96. return !(prev && prev.textContent.trim() === ":");
  97. }
  98. return false;
  99. },
  100. );
  101.  
  102. // Modules could exist in multiple dependency lists.
  103. Array.prototype.forEach.call(moduleElms, function (moduleElm) {
  104. // Module names are textNodes on Github.
  105. var moduleElmText = Array.prototype.find.call(
  106. moduleElm.childNodes,
  107. function (moduleElmChild) {
  108. return moduleElmChild.nodeType === 3;
  109. },
  110. );
  111.  
  112. var moduleElmLink = document.createElement("a");
  113. moduleElmLink.setAttribute("href", url);
  114. moduleElmLink.appendChild(document.createTextNode(module));
  115.  
  116. // Replace textNode, so we keep surrounding elements (like the highlighted quotes).
  117. moduleElm.replaceChild(moduleElmLink, moduleElmText);
  118. });
  119. }
  120.  
  121. /*!
  122. strip-json-comments
  123. Strip comments from JSON. Lets you use comments in your JSON files!
  124. https://github.com/sindresorhus/strip-json-comments
  125. by Sindre Sorhus
  126. MIT License
  127. */
  128. function stripJsonComments(str) {
  129. var currentChar;
  130. var nextChar;
  131. var insideString = false;
  132. var insideComment = false;
  133. var ret = "";
  134. for (var i = 0; i < str.length; i++) {
  135. currentChar = str[i];
  136. nextChar = str[i + 1];
  137. if (!insideComment && str[i - 1] !== "\\" && currentChar === '"') {
  138. insideString = !insideString;
  139. }
  140. if (insideString) {
  141. ret += currentChar;
  142. continue;
  143. }
  144. if (!insideComment && currentChar + nextChar === "//") {
  145. insideComment = "single";
  146. i++;
  147. } else if (
  148. insideComment === "single" &&
  149. currentChar + nextChar === "\r\n"
  150. ) {
  151. insideComment = false;
  152. i++;
  153. ret += currentChar;
  154. ret += nextChar;
  155. continue;
  156. } else if (insideComment === "single" && currentChar === "\n") {
  157. insideComment = false;
  158. } else if (!insideComment && currentChar + nextChar === "/*") {
  159. insideComment = "multi";
  160. i++;
  161. continue;
  162. } else if (
  163. insideComment === "multi" &&
  164. currentChar + nextChar === "*/"
  165. ) {
  166. insideComment = false;
  167. i++;
  168. continue;
  169. }
  170. if (insideComment) {
  171. continue;
  172. }
  173. ret += currentChar;
  174. }
  175. return ret;
  176. }
  177.  
  178. // Init.
  179. Object.keys(modules).forEach(function (dependencyKey) {
  180. modules[dependencyKey].forEach(function (module) {
  181. if (isAtom && dependencyKey === "packageDependencies") {
  182. // Atom needs to be before NPM.
  183. var url = "https://atom.io/packages/" + module;
  184. linkify(module, url);
  185. } else if (isNPM) {
  186. var url = "https://www.npmjs.org/package/" + module;
  187. linkify(module, url);
  188. } else if (isBower) {
  189. GM_xmlhttpRequest({
  190. method: "GET",
  191. url: "http://bower.herokuapp.com/packages/" + module,
  192. onload: function (response) {
  193. var data = JSON.parse(response.responseText);
  194. var re = /github\.com\/([\w\-\.]+)\/([\w\-\.]+)/i;
  195. var parsedUrl = re.exec(data.url.replace(/\.git$/, ""));
  196. if (parsedUrl) {
  197. var user = parsedUrl[1];
  198. var repo = parsedUrl[2];
  199. var url = "https://github.com/" + user + "/" + repo;
  200. linkify(module, url);
  201. } else {
  202. linkify(module, data.url);
  203. }
  204. },
  205. });
  206. } else if (isNuGet) {
  207. var url = "https://www.nuget.org/packages/" + module;
  208. linkify(module, url);
  209. }
  210. });
  211. });
  212. })();