| 
1 | 1 | // @ts-check  | 
2 |  | -import { ReflectionType, UnionType } from 'typedoc';  | 
 | 2 | +import { ReflectionKind, ReflectionType, UnionType } from 'typedoc';  | 
3 | 3 | import { MarkdownTheme, MarkdownThemeContext } from 'typedoc-plugin-markdown';  | 
4 | 4 | 
 
  | 
5 | 5 | /**  | 
@@ -47,22 +47,111 @@ class ClerkMarkdownThemeContext extends MarkdownThemeContext {  | 
47 | 47 |  this.partials = {  | 
48 | 48 |  ...superPartials,  | 
49 | 49 |  /**  | 
50 |  | - * This hides the "Type parameters" section and the signature title from the output  | 
 | 50 | + * This hides the "Type parameters" section and the signature title from the output (by default). Shows the signature title if the `@displayFunctionSignature` tag is present.  | 
51 | 51 |  * @param {import('typedoc').SignatureReflection} model  | 
52 | 52 |  * @param {{ headingLevel: number, nested?: boolean, accessor?: string, multipleSignatures?: boolean; hideTitle?: boolean }} options  | 
53 | 53 |  */  | 
54 | 54 |  signature: (model, options) => {  | 
 | 55 | + const displayFunctionSignatureTag = model.comment?.getTag('@displayFunctionSignature');  | 
 | 56 | + const paramExtensionTag = model.comment?.getTag('@paramExtension');  | 
 | 57 | + const delimiter = '\n\n';  | 
 | 58 | + | 
55 | 59 |  const customizedOptions = {  | 
56 | 60 |  ...options,  | 
57 |  | - hideTitle: true,  | 
 | 61 | + // Hide the title by default, only show it if the `@displayFunctionSignature` tag is present  | 
 | 62 | + hideTitle: !displayFunctionSignatureTag,  | 
58 | 63 |  };  | 
59 | 64 |  const customizedModel = model;  | 
60 | 65 |  customizedModel.typeParameters = undefined;  | 
61 | 66 | 
 
  | 
62 |  | - const output = superPartials.signature(customizedModel, customizedOptions);  | 
 | 67 | + let output = superPartials.signature(customizedModel, customizedOptions);  | 
 | 68 | + | 
 | 69 | + // If there are extra tags, split the output by the delimiter and do the work  | 
 | 70 | + if (displayFunctionSignatureTag || paramExtensionTag) {  | 
 | 71 | + let splitOutput = output.split(delimiter);  | 
 | 72 | + | 
 | 73 | + if (displayFunctionSignatureTag) {  | 
 | 74 | + // Change position of the 0 index and 2 index of the output  | 
 | 75 | + // This way the function signature is below the description  | 
 | 76 | + splitOutput = swap(splitOutput, 0, 2);  | 
 | 77 | + }  | 
 | 78 | + | 
 | 79 | + if (paramExtensionTag) {  | 
 | 80 | + const stuff = this.helpers.getCommentParts(paramExtensionTag.content);  | 
 | 81 | + | 
 | 82 | + // Find the index of the item that contains '## Returns'  | 
 | 83 | + const index = splitOutput.findIndex(item => item.includes('## Returns'));  | 
 | 84 | + | 
 | 85 | + // If the index is found, insert the stuff before it  | 
 | 86 | + if (index !== -1) {  | 
 | 87 | + splitOutput.splice(index, 0, stuff);  | 
 | 88 | + }  | 
 | 89 | + }  | 
 | 90 | + | 
 | 91 | + // Join the output again  | 
 | 92 | + output = splitOutput.join(delimiter);  | 
 | 93 | + }  | 
63 | 94 | 
 
  | 
64 | 95 |  return output;  | 
65 | 96 |  },  | 
 | 97 | + /**  | 
 | 98 | + * If `signature` has @displayFunctionSignature tag, the function will run `signatureTitle`. We want to use a completely custom code block here.  | 
 | 99 | + * @param {import('typedoc').SignatureReflection} model  | 
 | 100 | + * @param {{ accessor?: string; includeType?: boolean }} [options]  | 
 | 101 | + * https://github.com/typedoc2md/typedoc-plugin-markdown/blob/c83cff97b72ab25b224463ceec118c34e940cb8a/packages/typedoc-plugin-markdown/src/theme/context/partials/member.signatureTitle.ts  | 
 | 102 | + */  | 
 | 103 | + signatureTitle: (model, options) => {  | 
 | 104 | + /**  | 
 | 105 | + * @type {string[]}  | 
 | 106 | + */  | 
 | 107 | + const md = [];  | 
 | 108 | + | 
 | 109 | + const keyword = this.helpers.getKeyword(model.parent.kind);  | 
 | 110 | + | 
 | 111 | + if (this.helpers.isGroupKind(model.parent) && keyword) {  | 
 | 112 | + md.push(keyword + ' ');  | 
 | 113 | + }  | 
 | 114 | + | 
 | 115 | + if (options?.accessor) {  | 
 | 116 | + md.push(options?.accessor + ' ');  | 
 | 117 | + }  | 
 | 118 | + | 
 | 119 | + if (model.parent) {  | 
 | 120 | + const flagsString = this.helpers.getReflectionFlags(model.parent?.flags);  | 
 | 121 | + if (flagsString.length) {  | 
 | 122 | + md.push(this.helpers.getReflectionFlags(model.parent.flags) + ' ');  | 
 | 123 | + }  | 
 | 124 | + }  | 
 | 125 | + | 
 | 126 | + if (!['__call', '__type'].includes(model.name)) {  | 
 | 127 | + /**  | 
 | 128 | + * @type {string[]}  | 
 | 129 | + */  | 
 | 130 | + const name = [];  | 
 | 131 | + if (model.kind === ReflectionKind.ConstructorSignature) {  | 
 | 132 | + name.push('new');  | 
 | 133 | + }  | 
 | 134 | + name.push(escapeChars(model.name));  | 
 | 135 | + md.push(name.join(' '));  | 
 | 136 | + }  | 
 | 137 | + | 
 | 138 | + if (model.typeParameters) {  | 
 | 139 | + md.push(  | 
 | 140 | + `${this.helpers.getAngleBracket('<')}${model.typeParameters  | 
 | 141 | + .map(typeParameter => typeParameter.name)  | 
 | 142 | + .join(', ')}${this.helpers.getAngleBracket('>')}`,  | 
 | 143 | + );  | 
 | 144 | + }  | 
 | 145 | + | 
 | 146 | + md.push(this.partials.signatureParameters(model.parameters || []));  | 
 | 147 | + | 
 | 148 | + if (model.type) {  | 
 | 149 | + md.push(`: ${this.partials.someType(model.type)}`);  | 
 | 150 | + }  | 
 | 151 | + | 
 | 152 | + const result = md.join('');  | 
 | 153 | + return codeBlock(result);  | 
 | 154 | + },  | 
66 | 155 |  /**  | 
67 | 156 |  * This condenses the output if only a "simple" return type + `@returns` is given.  | 
68 | 157 |  * @param {import('typedoc').SignatureReflection} model  | 
@@ -326,3 +415,78 @@ ${tabs}  | 
326 | 415 |  };  | 
327 | 416 |  }  | 
328 | 417 | }  | 
 | 418 | + | 
 | 419 | +/**  | 
 | 420 | + * @param {string} str - The string to unescape  | 
 | 421 | + */  | 
 | 422 | +function unEscapeChars(str) {  | 
 | 423 | + return str  | 
 | 424 | + .replace(/(`[^`]*?)\\*([^`]*?`)/g, (_match, p1, p2) => `${p1}${p2.replace(/\*/g, '\\*')}`)  | 
 | 425 | + .replace(/\\\\/g, '\\')  | 
 | 426 | + .replace(/(?<!\\)\*/g, '')  | 
 | 427 | + .replace(/\\</g, '<')  | 
 | 428 | + .replace(/\\>/g, '>')  | 
 | 429 | + .replace(/</g, '<')  | 
 | 430 | + .replace(/>/g, '>')  | 
 | 431 | + .replace(/\\_/g, '_')  | 
 | 432 | + .replace(/\\{/g, '{')  | 
 | 433 | + .replace(/\\}/g, '}')  | 
 | 434 | + .replace(/``.*?``|(?<!\\)`/g, match => (match.startsWith('``') ? match : ''))  | 
 | 435 | + .replace(/`` /g, '')  | 
 | 436 | + .replace(/ ``/g, '')  | 
 | 437 | + .replace(/\\`/g, '`')  | 
 | 438 | + .replace(/\\\*/g, '*')  | 
 | 439 | + .replace(/\\\|/g, '|')  | 
 | 440 | + .replace(/\\\]/g, ']')  | 
 | 441 | + .replace(/\\\[/g, '[')  | 
 | 442 | + .replace(/\[([^[\]]*)\]\((.*?)\)/gm, '$1');  | 
 | 443 | +}  | 
 | 444 | + | 
 | 445 | +/**  | 
 | 446 | + * @param {string} content  | 
 | 447 | + */  | 
 | 448 | +function codeBlock(content) {  | 
 | 449 | + /**  | 
 | 450 | + * @param {string} content  | 
 | 451 | + */  | 
 | 452 | + const trimLastLine = content => {  | 
 | 453 | + const lines = content.split('\n');  | 
 | 454 | + return lines.map((line, index) => (index === lines.length - 1 ? line.trim() : line)).join('\n');  | 
 | 455 | + };  | 
 | 456 | + const trimmedContent =  | 
 | 457 | + content.endsWith('}') || content.endsWith('};') || content.endsWith('>') || content.endsWith('>;')  | 
 | 458 | + ? trimLastLine(content)  | 
 | 459 | + : content;  | 
 | 460 | + return '```ts\n' + unEscapeChars(trimmedContent) + '\n```';  | 
 | 461 | +}  | 
 | 462 | + | 
 | 463 | +/**  | 
 | 464 | + * @param {string} str  | 
 | 465 | + */  | 
 | 466 | +function escapeChars(str) {  | 
 | 467 | + return str  | 
 | 468 | + .replace(/>/g, '\\>')  | 
 | 469 | + .replace(/</g, '\\<')  | 
 | 470 | + .replace(/{/g, '\\{')  | 
 | 471 | + .replace(/}/g, '\\}')  | 
 | 472 | + .replace(/_/g, '\\_')  | 
 | 473 | + .replace(/`/g, '\\`')  | 
 | 474 | + .replace(/\|/g, '\\|')  | 
 | 475 | + .replace(/\[/g, '\\[')  | 
 | 476 | + .replace(/\]/g, '\\]')  | 
 | 477 | + .replace(/\*/g, '\\*');  | 
 | 478 | +}  | 
 | 479 | + | 
 | 480 | +/**  | 
 | 481 | + *  | 
 | 482 | + * @param {string[]} arr  | 
 | 483 | + * @param {number} i  | 
 | 484 | + * @param {number} j  | 
 | 485 | + * @returns  | 
 | 486 | + */  | 
 | 487 | +function swap(arr, i, j) {  | 
 | 488 | + let t = arr[i];  | 
 | 489 | + arr[i] = arr[j];  | 
 | 490 | + arr[j] = t;  | 
 | 491 | + return arr;  | 
 | 492 | +}  | 
0 commit comments