madge.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // IMPORTANT:
  2. // Madge needs the Graphviz software to generate the SVG graphs.
  3. //
  4. // On Windows, download it here: https://graphviz.gitlab.io/download/, then add the path to the "bin" folder to your PATH variable or specify the path to the bin folder in the "madgeOptions" object below.
  5. // On Linux, run "sudo apt-get install graphviz"
  6. // On Mac, run "brew install graphviz || port install graphviz"
  7. //
  8. // To add files to be included in the graphing process, add them to the "otherFiles" array below (path is relative to project root).
  9. // Before running this script by using "node dev/madge", make sure all dev dependencies are installed by running the command "npm i --save-dev"
  10. const madgeOptions = {
  11. // graphVizPath: "C:/Users/fes/Desktop/Graphviz/bin" // set to null to use the path inside the PATH environment variable
  12. graphVizPath: null
  13. };
  14. const otherFiles = [
  15. "./JokeAPI.js"
  16. ];
  17. const madge = require("madge");
  18. const fs = require("fs-extra");
  19. const settings = require("../../settings");
  20. var fileList = [];
  21. var firstIframePos = {url: "./madge/JokeAPI.html", name: "./JokeAPI"};
  22. let isWindows = process.platform == "win32";
  23. if(isWindows && !process.env.PATH.toLowerCase().includes("graphviz") && madgeOptions.graphVizPath == null)
  24. {
  25. console.log("\x1b[31m\x1b[1m\nMadge needs the GraphViz software to generate the SVG graphs. Please download it (https://graphviz.gitlab.io/download/) and add it to your PATH environment variable.\nAlso make sure the path to it contains the word \"Graphviz\"\x1b[0m");
  26. process.exit(1);
  27. }
  28. const generateForOther = () => {
  29. let iterCount = 0;
  30. return new Promise((resolve, reject) => {
  31. otherFiles.forEach(file => {
  32. if(!file.endsWith(".js"))
  33. {
  34. iterCount++;
  35. return;
  36. }
  37. let filename = file.replace(/\.js/g, "");
  38. try
  39. {
  40. madge(`${file}`, madgeOptions)
  41. .then((res) => res.svg())
  42. .then((output) => {
  43. iterCount++;
  44. fs.writeFileSync(`./dev/madge/${filename}.html`, output.toString());
  45. if(iterCount == otherFiles.length)
  46. resolve();
  47. });
  48. fileList.push(`<li><span class="mimica" onclick="setIframe('./madge/${filename}.html', '${filename}')">${filename}.js</span></li>`);
  49. }
  50. catch(err)
  51. {
  52. reject(err);
  53. }
  54. });
  55. });
  56. }
  57. const generateForSrc = () => {
  58. let iterCount = 0;
  59. let srcFiles = fs.readdirSync("./src");
  60. return new Promise((resolve, reject) => {
  61. srcFiles.forEach(file => {
  62. if(!file.endsWith(".js"))
  63. {
  64. iterCount++;
  65. return;
  66. }
  67. let filename = file.replace(/\.js/g, "");
  68. try
  69. {
  70. madge(`./src/${file}`, madgeOptions)
  71. .then((res) => res.svg())
  72. .then((output) => {
  73. iterCount++;
  74. fs.writeFileSync(`./dev/madge/src-${filename}.html`, output.toString());
  75. if(iterCount == srcFiles.length)
  76. resolve();
  77. });
  78. fileList.push(`<li><span class="mimica" onclick="setIframe('./madge/src-${filename}.html', '${filename}')">src/${filename}.js</span></li>`);
  79. }
  80. catch(err)
  81. {
  82. reject(err);
  83. }
  84. });
  85. });
  86. }
  87. const generateForEndpoints = () => {
  88. let iterCount = 0;
  89. let endpointFiles = fs.readdirSync("./endpoints");
  90. return new Promise((resolve, reject) => {
  91. endpointFiles.forEach(file => {
  92. if(!file.endsWith(".js"))
  93. {
  94. iterCount++;
  95. return;
  96. }
  97. let filename = file.replace(/\.js/g, "");
  98. try
  99. {
  100. madge(`./endpoints/${file}`, madgeOptions)
  101. .then((res) => res.svg())
  102. .then((output) => {
  103. iterCount++;
  104. fs.writeFileSync(`./dev/madge/endpoints-${filename}.html`, output.toString());
  105. if(iterCount == endpointFiles.length)
  106. resolve();
  107. });
  108. fileList.push(`<li><span class="mimica" onclick="setIframe('./madge/endpoints-${filename}.html', '${filename}')">endpoints/${filename}.js</span></li>`);
  109. }
  110. catch(err)
  111. {
  112. reject(err);
  113. }
  114. });
  115. });
  116. }
  117. const generateForTools = () => {
  118. let iterCount = 0;
  119. let toolFiles = fs.readdirSync("./tools");
  120. return new Promise((resolve, reject) => {
  121. toolFiles.forEach(file => {
  122. if(!file.endsWith(".js"))
  123. {
  124. iterCount++;
  125. return;
  126. }
  127. let filename = file.replace(/\.js/g, "");
  128. try
  129. {
  130. madge(`./tools/${file}`, madgeOptions)
  131. .then((res) => res.svg())
  132. .then((output) => {
  133. iterCount++;
  134. fs.writeFileSync(`./dev/madge/tools-${filename}.html`, output.toString());
  135. if(iterCount == toolFiles.length)
  136. resolve();
  137. });
  138. fileList.push(`<li><span class="mimica" onclick="setIframe('./madge/tools-${filename}.html', '${filename}')">tools/${filename}.js</span></li>`);
  139. }
  140. catch(err)
  141. {
  142. reject(err);
  143. }
  144. });
  145. });
  146. }
  147. const generateForClasses = () => {
  148. let iterCount = 0;
  149. let classesFiles = fs.readdirSync("./src/classes");
  150. return new Promise((resolve, reject) => {
  151. classesFiles.forEach(file => {
  152. if(!file.endsWith(".js"))
  153. {
  154. iterCount++;
  155. return;
  156. }
  157. let filename = file.replace(/\.js/g, "");
  158. try
  159. {
  160. madge(`./src/classes/${file}`, madgeOptions)
  161. .then((res) => res.svg())
  162. .then((output) => {
  163. iterCount++;
  164. fs.writeFileSync(`./dev/madge/classes-${filename}.html`, output.toString());
  165. if(iterCount == classesFiles.length)
  166. resolve();
  167. });
  168. fileList.push(`<li><span class="mimica" onclick="setIframe('./madge/classes-${filename}.html', '${filename}')">classes/${filename}.js</span></li>`);
  169. }
  170. catch(err)
  171. {
  172. reject(err);
  173. }
  174. });
  175. });
  176. }
  177. const generateForTests = () => {
  178. let iterCount = 0;
  179. let testsFiles = fs.readdirSync("./tests");
  180. return new Promise((resolve, reject) => {
  181. testsFiles.forEach(file => {
  182. if(!file.endsWith(".js") || file == "template.js")
  183. {
  184. iterCount++;
  185. return;
  186. }
  187. let filename = file.replace(/\.js/g, "");
  188. try
  189. {
  190. madge(`./tests/${file}`, madgeOptions)
  191. .then((res) => res.svg())
  192. .then((output) => {
  193. iterCount++;
  194. fs.writeFileSync(`./dev/madge/tests-${filename}.html`, output.toString());
  195. if(iterCount == testsFiles.length)
  196. resolve();
  197. });
  198. fileList.push(`<li><span class="mimica" onclick="setIframe('./madge/tests-${filename}.html', '${filename}')">tests/${filename}.js</span></li>`);
  199. }
  200. catch(err)
  201. {
  202. reject(err);
  203. }
  204. });
  205. });
  206. }
  207. const writeIndex = () => {
  208. let index = getIndex();
  209. fs.writeFileSync("./dev/dependency-graph.html", index);
  210. console.log(`\n\n\x1b[32m\x1b[1mSuccessfully generated dependency graphs for ${fileList.length} files.\n\x1b[0m`);
  211. process.exit(0);
  212. }
  213. const getIndex = () => `\
  214. <!DOCTYPE html>
  215. <html>
  216. <head>
  217. <meta charset="UTF-8">
  218. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  219. <title>${settings.info.name} Dependency Graph</title>
  220. <style>
  221. body {font-family: "Source Sans Pro", "Segoe UI", sans-serif; margin: 5px; overflow-x: hidden;}
  222. .mimica {color: #00e; cursor: pointer;}
  223. .mimica:active {color: #000;}
  224. #iframe {width: 100%; height: 100%; border: none; margin-left: 15px;}
  225. #ifrtd {position: relative; display: inline-block; height: 100%; width: 100%;}
  226. #listtd {position: relative; display: inline-block; height: 100%;}
  227. table, table tr {width: 98vw;}
  228. #iframewrapper {width: 100%; height: 100%;}
  229. #flexcontainer {display: flex; flex-direction: row; flex-wrap: nowrap; min-height: 99vh;}
  230. .flexitem {flex-grow: 0;}
  231. .flexitem.grow {flex-grow: 3; padding-left: 20px;}
  232. h2 {margin-bottom: 10px; margin-top: 16px;}
  233. footer {position: fixed; bottom: 10px; right: 10px;}
  234. </style>
  235. <script>
  236. function onLoad()
  237. {
  238. setIframe("${firstIframePos.url}", "${firstIframePos.name}");
  239. }
  240. function setIframe(url, name)
  241. {
  242. document.getElementById("iframe").src = url;
  243. document.getElementById("selectedgraphtitle").innerHTML = name + ".js:";
  244. }
  245. </script>
  246. </head>
  247. <body onload="onLoad()">
  248. <div id="flexcontainer">
  249. <div class="flexitem">
  250. <ul>
  251. ${fileList.join("\n\t\t\t\t\t")}
  252. </ul>
  253. <br><br>
  254. Blue has dependencies.<br>
  255. Green has no dependencies.<br>
  256. Red has circular dependencies.
  257. </div>
  258. <div class="flexitem grow">
  259. <h2 id="selectedgraphtitle"></h2>
  260. <iframe id="iframe"></iframe>
  261. </div>
  262. </div>
  263. <footer>
  264. Generated with <a href="https://www.npmjs.com/package/madge" target="_blank">Madge</a>
  265. </footer>
  266. </body>
  267. </html>`;
  268. async function exec()
  269. {
  270. try
  271. {
  272. if(!fs.existsSync("./dev/madge"))
  273. fs.mkdirSync("./dev/madge");
  274. await generateForOther();
  275. process.stdout.write(".");
  276. await generateForSrc();
  277. process.stdout.write(".");
  278. await generateForEndpoints();
  279. process.stdout.write(".");
  280. await generateForTools();
  281. process.stdout.write(".");
  282. await generateForClasses();
  283. process.stdout.write(".");
  284. await generateForTests();
  285. process.stdout.write(".");
  286. writeIndex();
  287. process.stdout.write(".\n");
  288. }
  289. catch(err)
  290. {
  291. console.log(`\n\n\x1b[31m\x1b[1mError while generating dependency graphs:\n\x1b[0m${err}\n`);
  292. process.exit(1);
  293. }
  294. }
  295. exec();