Github JSON Dependencies Linker

Linkify all dependencies found in an JSON file.

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