Github JSON Dependencies Linker

Linkify all dependencies found in an JSON file.

目前為 2019-04-24 提交的版本,檢視 最新版本

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