defs.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * Copyright (C) 2024-present Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. // METADATA // {"ai-commented":{"service":"claude"}}
  20. const dedent = require('dedent');
  21. const doctrine = require('doctrine');
  22. /**
  23. * Out class - A utility class for generating formatted text output
  24. * Provides methods for creating headings, line feeds, and text output
  25. *
  26. * ~~with a fluent interface.~~
  27. * ^ Nope, AI got this wrong but maybe it's a good idea to
  28. * make this a fluent interface
  29. *
  30. * The constructor returns a bound function that
  31. * maintains the output state and provides access to helper methods.
  32. */
  33. class Out {
  34. constructor () {
  35. this.str = '';
  36. const fn = this.out.bind(this);
  37. fn.h = this.h.bind(this);
  38. fn.lf = this.lf.bind(this);
  39. fn.text = () => this.str;
  40. return fn;
  41. }
  42. h (n, text) {
  43. this.str += '#'.repeat(n) + ' ' + text + '\n\n';
  44. }
  45. /**
  46. * Adds a line feed (newline) to the output string
  47. * @returns {void}
  48. */
  49. lf () { this.str += '\n'; }
  50. /**
  51. * Append to the string
  52. * @param {string} str
  53. */
  54. out (str) {
  55. this.str += str;
  56. }
  57. }
  58. /**
  59. * Doc class serves as a base class for documentation generation.
  60. * Provides core functionality for parsing and storing documentation comments
  61. * using the doctrine parser. Contains methods for handling JSDoc-style
  62. * comments and maintaining documentation state.
  63. */
  64. class Doc {
  65. constructor () {
  66. this._construct();
  67. }
  68. provide_comment (comment) {
  69. const parsed_comment = doctrine.parse(comment.value, { unwrap: true });
  70. this.comment = parsed_comment.description;
  71. }
  72. }
  73. /**
  74. * ModuleDoc class extends Doc to represent documentation for a module.
  75. * Handles module-level documentation including services, libraries, and requirements.
  76. * Provides methods for adding services/libraries and generating markdown documentation.
  77. * Tracks external imports and generates notes about module dependencies.
  78. */
  79. class ModuleDoc extends Doc {
  80. /**
  81. * Initializes the base properties for a ModuleDoc instance
  82. * Sets up empty arrays for services, requires, and libs collections
  83. * @private
  84. */
  85. _construct () {
  86. this.services = [];
  87. this.requires = [];
  88. this.libs = [];
  89. }
  90. /**
  91. * Creates and adds a new service to this module's services array
  92. * @returns {ServiceDoc} The newly created service document instance
  93. */
  94. add_service () {
  95. const service = new ServiceDoc();
  96. this.services.push(service);
  97. return service;
  98. }
  99. /**
  100. * Creates and adds a new LibDoc instance to the module's libs array
  101. * @returns {LibDoc} The newly created LibDoc instance
  102. */
  103. add_lib () {
  104. const lib = new LibDoc();
  105. this.libs.push(lib);
  106. return lib;
  107. }
  108. /**
  109. * Populates a "notes" array for the module documentation
  110. * based on findings about imports.
  111. */
  112. ready () {
  113. this.notes = [];
  114. const rel_requires = this.requires.filter(r => r.startsWith('../'));
  115. if ( rel_requires.length > 0 ) {
  116. this.notes.push({
  117. title: 'Outside Imports',
  118. desc: dedent(`
  119. This module has external relative imports. When these are
  120. removed it may become possible to move this module to an
  121. extension.
  122. **Imports:**
  123. ${rel_requires.map(r => {
  124. let maybe_aside = '';
  125. if ( r.endsWith('BaseService') ) {
  126. maybe_aside = ' (use.BaseService)';
  127. }
  128. return `- \`${r}\`` + maybe_aside;
  129. }).join('\n')}
  130. `)
  131. });
  132. }
  133. }
  134. toMarkdown ({ hl, out } = { hl: 1 }) {
  135. this.ready();
  136. out = out ?? new Out();
  137. out.h(hl, this.name);
  138. out(this.comment + '\n\n');
  139. if ( this.services.length > 0 ) {
  140. out.h(hl + 1, 'Services');
  141. for ( const service of this.services ) {
  142. service.toMarkdown({ out, hl: hl + 2 });
  143. }
  144. }
  145. if ( this.libs.length > 0 ) {
  146. out.h(hl + 1, 'Libraries');
  147. for ( const lib of this.libs ) {
  148. lib.toMarkdown({ out, hl: hl + 2 });
  149. }
  150. }
  151. if ( this.notes.length > 0 ) {
  152. out.h(hl + 1, 'Notes');
  153. for ( const note of this.notes ) {
  154. out.h(hl + 2, note.title);
  155. out(note.desc);
  156. out.lf();
  157. }
  158. }
  159. return out.text();
  160. }
  161. }
  162. /**
  163. * ServiceDoc class represents documentation for a service module.
  164. * Handles parsing and formatting of service-related documentation including
  165. * listeners, methods, and their associated parameters. Extends the base Doc class
  166. * to provide specialized documentation capabilities for service components.
  167. */
  168. class ServiceDoc extends Doc {
  169. /**
  170. * Represents documentation for a service
  171. * Handles parsing and storing service documentation including listeners and methods
  172. * Initializes with empty arrays for listeners and methods
  173. */
  174. _construct () {
  175. this.listeners = [];
  176. this.methods = [];
  177. }
  178. provide_comment (comment) {
  179. const parsed_comment = doctrine.parse(comment.value, { unwrap: true });
  180. this.comment = parsed_comment.description;
  181. }
  182. provide_listener (listener) {
  183. const parsed_comment = doctrine.parse(listener.comment, { unwrap: true });
  184. const params = [];
  185. for ( const tag of parsed_comment.tags ) {
  186. if ( tag.title !== 'evtparam' ) continue;
  187. const name = tag.description.slice(0, tag.description.indexOf(' '));
  188. const desc = tag.description.slice(tag.description.indexOf(' '));
  189. params.push({ name, desc })
  190. }
  191. this.listeners.push({
  192. ...listener,
  193. comment: parsed_comment.description,
  194. params,
  195. });
  196. }
  197. provide_method (method) {
  198. const parsed_comment = doctrine.parse(method.comment, { unwrap: true });
  199. const params = [];
  200. for ( const tag of parsed_comment.tags ) {
  201. if ( tag.title !== 'param' ) continue;
  202. const name = tag.name;
  203. const desc = tag.description;
  204. params.push({ name, desc })
  205. }
  206. this.methods.push({
  207. ...method,
  208. comment: parsed_comment.description,
  209. params,
  210. });
  211. }
  212. toMarkdown ({ hl, out } = { hl: 1 }) {
  213. out = out ?? new Out();
  214. out.h(hl, this.name);
  215. out(this.comment + '\n\n');
  216. if ( this.listeners.length > 0 ) {
  217. out.h(hl + 1, 'Listeners');
  218. for ( const listener of this.listeners ) {
  219. out.h(hl + 2, '`' + listener.key + '`');
  220. out (listener.comment + '\n\n');
  221. if ( listener.params.length > 0 ) {
  222. out.h(hl + 3, 'Parameters');
  223. for ( const param of listener.params ) {
  224. out(`- **${param.name}:** ${param.desc}\n`);
  225. }
  226. out.lf();
  227. }
  228. }
  229. }
  230. if ( this.methods.length > 0 ) {
  231. out.h(hl + 1, 'Methods');
  232. for ( const method of this.methods ) {
  233. out.h(hl + 2, '`' + method.key + '`');
  234. out (method.comment + '\n\n');
  235. if ( method.params.length > 0 ) {
  236. out.h(hl + 3, 'Parameters');
  237. for ( const param of method.params ) {
  238. out(`- **${param.name}:** ${param.desc}\n`);
  239. }
  240. out.lf();
  241. }
  242. }
  243. }
  244. return out.text();
  245. }
  246. }
  247. /**
  248. * LibDoc class for documenting library modules
  249. * Handles documentation for library functions including their descriptions,
  250. * parameters, and markdown generation. Extends the base Doc class to provide
  251. * specialized documentation capabilities for library components.
  252. */
  253. class LibDoc extends Doc {
  254. /**
  255. * Represents documentation for a library module
  256. *
  257. * Handles parsing and formatting documentation for library functions.
  258. * Stores function definitions with their comments, parameters and descriptions.
  259. * Can output formatted markdown documentation.
  260. */
  261. _construct () {
  262. this.functions = [];
  263. }
  264. provide_function ({ key, comment, params }) {
  265. const parsed_comment = doctrine.parse(comment, { unwrap: true });
  266. const parsed_params = [];
  267. for ( const tag of parsed_comment.tags ) {
  268. if ( tag.title !== 'param' ) continue;
  269. const name = tag.name;
  270. const desc = tag.description;
  271. parsed_params.push({ name, desc });
  272. }
  273. this.functions.push({
  274. key,
  275. comment: parsed_comment.description,
  276. params: parsed_params,
  277. });
  278. }
  279. toMarkdown ({ hl, out } = { hl: 1 }) {
  280. out = out ?? new Out();
  281. out.h(hl, this.name);
  282. console.log('functions?', this.functions);
  283. if ( this.functions.length > 0 ) {
  284. out.h(hl + 1, 'Functions');
  285. for ( const func of this.functions ) {
  286. out.h(hl + 2, '`' + func.key + '`');
  287. out(func.comment + '\n\n');
  288. if ( func.params.length > 0 ) {
  289. out.h(hl + 3, 'Parameters');
  290. for ( const param of func.params ) {
  291. out(`- **${param.name}:** ${param.desc}\n`);
  292. }
  293. out.lf();
  294. }
  295. }
  296. }
  297. return out.text();
  298. }
  299. }
  300. module.exports = {
  301. ModuleDoc,
  302. ServiceDoc,
  303. };