defs.js 9.9 KB

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