Github JSON Dependencies Linker

Linkify all dependencies found in an JSON file.

2018-01-27 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

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