From e72176d88f604e9bcfe27fa5d4108e44ab4532c7 Mon Sep 17 00:00:00 2001 From: davanesh Date: Fri, 13 Mar 2026 01:51:12 +0530 Subject: [PATCH 1/4] fix: render constructors as \`new ClassName(params)\` format (closes #2) --- plugins/theme/index.mjs | 164 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 4 deletions(-) diff --git a/plugins/theme/index.mjs b/plugins/theme/index.mjs index cf9710a..268dc94 100644 --- a/plugins/theme/index.mjs +++ b/plugins/theme/index.mjs @@ -12,15 +12,171 @@ export class DocKitTheme extends MarkdownTheme { } export class DocKitThemeContext extends MarkdownThemeContext { - helpers = helpers(this); + helpers = { + ...this.helpers, - partials = partials(this); + /** @param {import('typedoc').ParameterReflection} */ + typedListItem: ({ label, name, type, comment }) => { + const namePart = label ? ` ${label}:` : name ? ` \`${name}\`` : ""; + const typePart = type + ? ` ${typeof type === "string" ? type : this.partials.someType(type)}` + : ""; + const descPart = comment + ? ` ${this.helpers.getCommentParts(comment.summary ?? comment.content)}` + : ""; - templates = { - ...this.templates, + return `*${namePart}${typePart}${descPart}`; + }, + + typedList: (entries) => entries.map(this.helpers.typedListItem).join("\n"), + }; + partials = { + ...this.partials, + ...typePartials, + + constructor: (model, options) => { + return model.signatures?.map(signature => { + const params = signature.parameters ?? []; + const className = model.parent?.name ?? "Unknown"; + const allOptional = params.length > 0 && + params.every(p => p.flags?.isOptional); + const paramStr = allOptional + ? `[${params.map(p => p.name).join(", ")}]` + : params.map(p => + p.flags?.isOptional ? `[${p.name}]` : p.name + ).join(", "); + const title = `\`new ${className}(${paramStr})\``; + const paramsList = params.length + ? this.helpers.typedList(params) + : ""; + return [ + `#### ${title}`, + paramsList, + ].filter(Boolean).join("\n"); + }).join("\n\n") ?? ""; + }, + // Removes *** horizontal rules between members + members: (model, options) => { + const items = model.filter( + (item) => !this.router.hasOwnDocument(item) + ); + return items + .map(item => + this.partials.memberContainer(item, { + headingLevel: options.headingLevel, + groupTitle: options.groupTitle, + }) + ) + .filter(Boolean) + .join("\n\n"); + }, + + // Removes ### Constructors / ### Methods / ### Properties headings + groups: (model, options) => { + return (model.groups ?? []) + .flatMap(group => { + // Skip properties — already shown in constructor params + const isPropertiesGroup = group.children?.every( + child => child.kind === ReflectionKind.Property + ); + if (isPropertiesGroup) return []; + + const children = group.children?.filter( + child => child.isDeclaration() + ) ?? []; + if (!children.length) return []; + + return [ + this.partials.members(children, { + headingLevel: options.headingLevel, + groupTitle: group.title, + }) + ]; + }) + .filter(Boolean) + .join("\n\n"); + }, + + body: (model, options) => { + if (model.groups?.length) { + return this.partials.groups(model, { + headingLevel: options.headingLevel, + kind: model.kind, + }); + } + return ""; + }, + + // Typed Lists + parametersList: this.helpers.typedList, + propertiesTable: this.helpers.typedList, + signature: (model, options) => { + const comment = options.multipleSignatures + ? model.comment + : model.comment || model.parent?.comment; + + return [ + model.typeParameters?.length && + this.partials.typeParametersList(model.typeParameters, { + headingLevel: options.headingLevel, + }), + model.parameters?.length && + this.partials.parametersList(model.parameters, { + headingLevel: options.headingLevel, + }), + this.helpers.typedListItem({ + label: "Returns", + type: model.type ?? "void", + comment: model.comment?.getTag("@returns"), + }), + "", + comment && + this.partials.comment(comment, { + headingLevel: options.headingLevel, + }), + ] + .filter((x) => (typeof x === "string" ? x : Boolean(x))) + .join("\n"); + }, + + // Titles + memberTitle: (model) => { + //DEBUG + // console.log("KIND:", model.kind, "NAME:", model.name, "CONSTRUCTOR KIND:", ReflectionKind.Constructor); + + const prefix = resolveMemberPrefix(model); + const params = model.signatures?.[0]?.parameters ?? null; + const name = params + ? `\`${model.name}(${params.map((p) => p.name).join(", ")})\`` + : `\`${model.name}\``; + return prefix ? `${prefix}: ${name}` : name; + }, + declarationTitle: (model) => { + return this.helpers.typedListItem({ + name: model.name, + type: model.type, + comment: model.comment, + }); + }, + memberContainer: (model, options) => { + const md = []; + if (!this.router.hasOwnDocument(model) && + ![ReflectionKind.Constructor].includes(model.kind)) { + md.push( + "#".repeat(options.headingLevel) + " " + + this.partials.memberTitle(model) + ); + } + md.push(this.partials.member(model, { + headingLevel: options.headingLevel + 1, // ← methods get #### + nested: options.nested, + })); + return md.filter(Boolean).join("\n\n"); + }, }; } +/** @param {import('typedoc').Application} app */ export function load(app) { app.renderer.defineTheme('doc-kit', DocKitTheme); } From e2b9d893a4a2a995f045d9862d38c5ad4d440891 Mon Sep 17 00:00:00 2001 From: davanesh Date: Fri, 13 Mar 2026 14:01:47 +0530 Subject: [PATCH 2/4] refactor: move changes to helpers/index and partials/index --- plugins/theme/index.mjs | 166 +------------------------------ plugins/theme/partials/index.mjs | 152 ++++++++++++++++++++++++---- 2 files changed, 133 insertions(+), 185 deletions(-) diff --git a/plugins/theme/index.mjs b/plugins/theme/index.mjs index 268dc94..0347c03 100644 --- a/plugins/theme/index.mjs +++ b/plugins/theme/index.mjs @@ -12,171 +12,11 @@ export class DocKitTheme extends MarkdownTheme { } export class DocKitThemeContext extends MarkdownThemeContext { - helpers = { - ...this.helpers, - - /** @param {import('typedoc').ParameterReflection} */ - typedListItem: ({ label, name, type, comment }) => { - const namePart = label ? ` ${label}:` : name ? ` \`${name}\`` : ""; - const typePart = type - ? ` ${typeof type === "string" ? type : this.partials.someType(type)}` - : ""; - const descPart = comment - ? ` ${this.helpers.getCommentParts(comment.summary ?? comment.content)}` - : ""; - - return `*${namePart}${typePart}${descPart}`; - }, - - typedList: (entries) => entries.map(this.helpers.typedListItem).join("\n"), - }; - partials = { - ...this.partials, - ...typePartials, - - constructor: (model, options) => { - return model.signatures?.map(signature => { - const params = signature.parameters ?? []; - const className = model.parent?.name ?? "Unknown"; - const allOptional = params.length > 0 && - params.every(p => p.flags?.isOptional); - const paramStr = allOptional - ? `[${params.map(p => p.name).join(", ")}]` - : params.map(p => - p.flags?.isOptional ? `[${p.name}]` : p.name - ).join(", "); - const title = `\`new ${className}(${paramStr})\``; - const paramsList = params.length - ? this.helpers.typedList(params) - : ""; - return [ - `#### ${title}`, - paramsList, - ].filter(Boolean).join("\n"); - }).join("\n\n") ?? ""; - }, - // Removes *** horizontal rules between members - members: (model, options) => { - const items = model.filter( - (item) => !this.router.hasOwnDocument(item) - ); - return items - .map(item => - this.partials.memberContainer(item, { - headingLevel: options.headingLevel, - groupTitle: options.groupTitle, - }) - ) - .filter(Boolean) - .join("\n\n"); - }, - - // Removes ### Constructors / ### Methods / ### Properties headings - groups: (model, options) => { - return (model.groups ?? []) - .flatMap(group => { - // Skip properties — already shown in constructor params - const isPropertiesGroup = group.children?.every( - child => child.kind === ReflectionKind.Property - ); - if (isPropertiesGroup) return []; - - const children = group.children?.filter( - child => child.isDeclaration() - ) ?? []; - if (!children.length) return []; - - return [ - this.partials.members(children, { - headingLevel: options.headingLevel, - groupTitle: group.title, - }) - ]; - }) - .filter(Boolean) - .join("\n\n"); - }, - - body: (model, options) => { - if (model.groups?.length) { - return this.partials.groups(model, { - headingLevel: options.headingLevel, - kind: model.kind, - }); - } - return ""; - }, - - // Typed Lists - parametersList: this.helpers.typedList, - propertiesTable: this.helpers.typedList, - signature: (model, options) => { - const comment = options.multipleSignatures - ? model.comment - : model.comment || model.parent?.comment; - - return [ - model.typeParameters?.length && - this.partials.typeParametersList(model.typeParameters, { - headingLevel: options.headingLevel, - }), - model.parameters?.length && - this.partials.parametersList(model.parameters, { - headingLevel: options.headingLevel, - }), - this.helpers.typedListItem({ - label: "Returns", - type: model.type ?? "void", - comment: model.comment?.getTag("@returns"), - }), - "", - comment && - this.partials.comment(comment, { - headingLevel: options.headingLevel, - }), - ] - .filter((x) => (typeof x === "string" ? x : Boolean(x))) - .join("\n"); - }, - - // Titles - memberTitle: (model) => { - //DEBUG - // console.log("KIND:", model.kind, "NAME:", model.name, "CONSTRUCTOR KIND:", ReflectionKind.Constructor); - - const prefix = resolveMemberPrefix(model); - const params = model.signatures?.[0]?.parameters ?? null; - const name = params - ? `\`${model.name}(${params.map((p) => p.name).join(", ")})\`` - : `\`${model.name}\``; - return prefix ? `${prefix}: ${name}` : name; - }, - declarationTitle: (model) => { - return this.helpers.typedListItem({ - name: model.name, - type: model.type, - comment: model.comment, - }); - }, - memberContainer: (model, options) => { - const md = []; - if (!this.router.hasOwnDocument(model) && - ![ReflectionKind.Constructor].includes(model.kind)) { - md.push( - "#".repeat(options.headingLevel) + " " + - this.partials.memberTitle(model) - ); - } - md.push(this.partials.member(model, { - headingLevel: options.headingLevel + 1, // ← methods get #### - nested: options.nested, - })); - return md.filter(Boolean).join("\n\n"); - }, - }; + helpers = helpers(this); + partials = partials(this); } /** @param {import('typedoc').Application} app */ export function load(app) { app.renderer.defineTheme('doc-kit', DocKitTheme); -} +} \ No newline at end of file diff --git a/plugins/theme/partials/index.mjs b/plugins/theme/partials/index.mjs index 4aac1b8..a13fdb3 100644 --- a/plugins/theme/partials/index.mjs +++ b/plugins/theme/partials/index.mjs @@ -19,7 +19,6 @@ export const getMemberPrefix = (model) => { const prefix = model.flags?.isStatic ? STATIC_PREFIX[model.kind] : KIND_PREFIX[model.kind]; - return prefix ? `${prefix}: ` : ''; }; @@ -31,61 +30,170 @@ export default (ctx) => ({ ...ctx.partials, ...typePartials, - signature(model, options) { + signature: (model, options) => { const comment = options.multipleSignatures ? model.comment : model.comment || model.parent?.comment; + const stability = (() => { + if (!comment) return null; + const deprecated = comment.blockTags?.find(t => t.tag === '@deprecated'); + const experimental = comment.blockTags?.find(t => t.tag === '@experimental') + ?? comment.blockTags?.find(t => t.tag === '@beta'); + const legacy = comment.blockTags?.find(t => t.tag === '@legacy'); + + if (deprecated) { + const desc = deprecated.content?.length + ? ctx.helpers.getCommentParts(deprecated.content) + : 'Deprecated'; + return `> Stability: 0 - Deprecated: ${desc}`; + } + if (experimental) { + const desc = experimental.content?.length + ? `: ${ctx.helpers.getCommentParts(experimental.content)}` + : ''; + return `> Stability: 1 - Experimental${desc}`; + } + if (legacy) { + const desc = legacy.content?.length + ? `: ${ctx.helpers.getCommentParts(legacy.content)}` + : ''; + return `> Stability: 3 - Legacy${desc}`; + } + return null; + })(); + return [ - model.typeParameters?.length && - ctx.partials.typeParametersList(model.typeParameters, { - headingLevel: options.headingLevel, - }), + stability, model.parameters?.length && ctx.partials.parametersList(model.parameters, { headingLevel: options.headingLevel, }), ctx.helpers.typedListItem({ - label: 'Returns', - type: model.type ?? 'void', - comment: model.comment?.getTag('@returns'), + label: "Returns", + type: model.type ?? "void", + comment: model.comment?.getTag("@returns"), + }), + "", + comment && ctx.partials.comment(comment, { + headingLevel: options.headingLevel, }), - '', - comment && - ctx.partials.comment(comment, { - headingLevel: options.headingLevel, - }), ] - .filter((x) => (typeof x === 'string' ? x : Boolean(x))) - .join('\n'); + .filter((x) => (typeof x === "string" ? x : Boolean(x))) + .join("\n"); }, memberTitle(model) { + if (model.kind === ReflectionKind.Constructor) { + const params = model.signatures?.[0]?.parameters ?? []; + const className = model.parent?.name ?? model.name; + const allOptional = params.length > 0 && + params.every(p => p.flags?.isOptional); + const paramStr = allOptional + ? `[${params.map(p => p.name).join(", ")}]` + : params.map(p => + p.flags?.isOptional ? `[${p.name}]` : p.name + ).join(", "); + return `\`new ${className}(${paramStr})\``; + } + const prefix = getMemberPrefix(model); const params = model.signatures?.[0]?.parameters; - if (!params) { return `${prefix}\`${model.name}\``; } - const paramsString = params .map((param, index) => { const paramName = param.name; if (param.flags?.isOptional) { - // For optional params, wrap comma + name in brackets (except for first param) return index === 0 ? `[${paramName}]` : `[, ${paramName}]`; } else { - // For required params, add comma separator (except for first param) return index === 0 ? paramName : `, ${paramName}`; } }) .join(''); - return `${prefix}\`${model.name}(${paramsString})\``; }, + memberContainer: (model, options) => { + const md = []; + if (!ctx.router.hasOwnDocument(model)) { + md.push( + "#".repeat(options.headingLevel) + " " + + ctx.partials.memberTitle(model) + ); + } + md.push(ctx.partials.member(model, { + headingLevel: options.headingLevel + 1, + nested: options.nested, + })); + return md.filter(Boolean).join("\n\n"); + }, + + constructor: (model, options) => { + return model.signatures?.map(signature => { + const params = signature.parameters ?? []; + return params.length ? ctx.helpers.typedList(params) : ""; + }).filter(Boolean).join("\n\n") ?? ""; + }, + + members: (model, options) => { + const items = model.filter( + (item) => !ctx.router.hasOwnDocument(item) + ); + return items + .map(item => + ctx.partials.memberContainer(item, { + headingLevel: options.headingLevel, + groupTitle: options.groupTitle, + }) + ) + .filter(Boolean) + .join("\n\n"); + }, + + groups: (model, options) => { + return (model.groups ?? []) + .flatMap(group => { + const isPropertiesGroup = group.children?.every( + child => child.kind === ReflectionKind.Property + ); + if (isPropertiesGroup) return []; + const children = group.children?.filter( + child => child.isDeclaration() + ) ?? []; + if (!children.length) return []; + return [ + ctx.partials.members(children, { + headingLevel: options.headingLevel, + groupTitle: group.title, + }) + ]; + }) + .filter(Boolean) + .join("\n\n"); + }, + + body: (model, options) => { + if (model.groups?.length) { + return ctx.partials.groups(model, { + headingLevel: options.headingLevel, + kind: model.kind, + }); + } + return ""; + }, + + declarationTitle: (model) => { + return ctx.helpers.typedListItem({ + name: model.name, + type: model.type, + comment: model.comment, + }); + }, + parametersList: ctx.helpers.typedList, typedParametersList: ctx.helpers.typedList, typeDeclarationList: ctx.helpers.typedList, propertiesTable: ctx.helpers.typedList, -}); +}); \ No newline at end of file From 8e0768f777eb6f8b3157622e789ce8e2974f45e9 Mon Sep 17 00:00:00 2001 From: davanesh Date: Fri, 13 Mar 2026 23:08:54 +0530 Subject: [PATCH 3/4] refactor: extract constructor title into buildConstructorTitle helper --- plugins/theme/helpers/index.mjs | 11 ++++++++ plugins/theme/partials/index.mjs | 43 ++------------------------------ 2 files changed, 13 insertions(+), 41 deletions(-) diff --git a/plugins/theme/helpers/index.mjs b/plugins/theme/helpers/index.mjs index 4847191..37d273e 100644 --- a/plugins/theme/helpers/index.mjs +++ b/plugins/theme/helpers/index.mjs @@ -30,4 +30,15 @@ export default (ctx) => ({ typedList(entries) { return entries.map(ctx.helpers.typedListItem).join("\n"); }, + + buildConstructorTitle(model) { + const params = model.signatures?.[0]?.parameters ?? []; + const className = model.parent?.name ?? model.name; + const allOptional = + params.length > 0 && params.every((p) => p.flags?.isOptional); + const paramStr = allOptional + ? `[${params.map((p) => p.name).join(", ")}]` + : params.map((p) => (p.flags?.isOptional ? `[${p.name}]` : p.name)).join(", "); + return `\`new ${className}(${paramStr})\``; + }, }); diff --git a/plugins/theme/partials/index.mjs b/plugins/theme/partials/index.mjs index 3ec9e04..568bc04 100644 --- a/plugins/theme/partials/index.mjs +++ b/plugins/theme/partials/index.mjs @@ -31,41 +31,12 @@ export default (ctx) => ({ ...ctx.partials, ...typePartials, - signature: (model, options) => { + signature(model, options) { const comment = options.multipleSignatures ? model.comment : model.comment || model.parent?.comment; - const stability = (() => { - if (!comment) return null; - const deprecated = comment.blockTags?.find(t => t.tag === '@deprecated'); - const experimental = comment.blockTags?.find(t => t.tag === '@experimental') - ?? comment.blockTags?.find(t => t.tag === '@beta'); - const legacy = comment.blockTags?.find(t => t.tag === '@legacy'); - - if (deprecated) { - const desc = deprecated.content?.length - ? ctx.helpers.getCommentParts(deprecated.content) - : 'Deprecated'; - return `> Stability: 0 - Deprecated: ${desc}`; - } - if (experimental) { - const desc = experimental.content?.length - ? `: ${ctx.helpers.getCommentParts(experimental.content)}` - : ''; - return `> Stability: 1 - Experimental${desc}`; - } - if (legacy) { - const desc = legacy.content?.length - ? `: ${ctx.helpers.getCommentParts(legacy.content)}` - : ''; - return `> Stability: 3 - Legacy${desc}`; - } - return null; - })(); - return [ - stability, model.parameters?.length && ctx.partials.parametersList(model.parameters, { headingLevel: options.headingLevel, @@ -87,16 +58,7 @@ export default (ctx) => ({ memberTitle(model) { if (model.kind === ReflectionKind.Constructor) { - const params = model.signatures?.[0]?.parameters ?? []; - const className = model.parent?.name ?? model.name; - const allOptional = params.length > 0 && - params.every(p => p.flags?.isOptional); - const paramStr = allOptional - ? `[${params.map(p => p.name).join(", ")}]` - : params.map(p => - p.flags?.isOptional ? `[${p.name}]` : p.name - ).join(", "); - return `\`new ${className}(${paramStr})\``; + return ctx.helpers.buildConstructorTitle(model); } const prefix = getMemberPrefix(model); @@ -114,7 +76,6 @@ export default (ctx) => ({ } }) .join(""); - return `${prefix}\`${model.name}(${paramsString})\``; }, From 7e24ed9c81b3631db4d952b1b8bb25827c814241 Mon Sep 17 00:00:00 2001 From: davanesh Date: Sat, 14 Mar 2026 19:14:34 +0530 Subject: [PATCH 4/4] refactor: replace buildConstructorTitle with generic signatureTitle helper, restore templates --- plugins/theme/helpers/index.mjs | 55 ++++++++++++++++++++++++-------- plugins/theme/index.mjs | 3 ++ plugins/theme/partials/index.mjs | 22 +++---------- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/plugins/theme/helpers/index.mjs b/plugins/theme/helpers/index.mjs index 37d273e..d05b64d 100644 --- a/plugins/theme/helpers/index.mjs +++ b/plugins/theme/helpers/index.mjs @@ -15,30 +15,57 @@ export default (ctx) => ({ typedListItem({ label, name, type, comment }) { const namePart = label ? ` ${label}:` : name ? ` \`${name}\`` : ""; - const typePart = type ? ` ${typeof type === "string" ? type : ctx.partials.someType(type)}` : ""; - const descPart = comment ? ` ${ctx.helpers.getCommentParts(comment.summary ?? comment.content)}` : ""; - return `*${namePart}${typePart}${descPart}`; }, typedList(entries) { return entries.map(ctx.helpers.typedListItem).join("\n"); }, - - buildConstructorTitle(model) { - const params = model.signatures?.[0]?.parameters ?? []; - const className = model.parent?.name ?? model.name; - const allOptional = - params.length > 0 && params.every((p) => p.flags?.isOptional); - const paramStr = allOptional - ? `[${params.map((p) => p.name).join(", ")}]` - : params.map((p) => (p.flags?.isOptional ? `[${p.name}]` : p.name)).join(", "); - return `\`new ${className}(${paramStr})\``; + + signatureTitle(name, params) { + const paramsStr = params + .map((param, index) => { + if (param.flags?.isOptional) { + return index === 0 ? `[${param.name}]` : `[, ${param.name}]`; + } + return index === 0 ? param.name : `, ${param.name}`; + }) + .join(""); + return `\`${name}(${paramsStr})\``; + }, + + stabilityBlockquote(comment) { + if (!comment) return null; + const deprecated = comment.blockTags?.find((t) => t.tag === "@deprecated"); + const isExperimental = + comment.modifierTags?.has("@experimental") || + comment.modifierTags?.has("@beta"); + const legacy = comment.blockTags?.find((t) => t.tag === "@legacy"); + if (deprecated) { + const message = deprecated.content?.length + ? ctx.helpers.getCommentParts(deprecated.content).trim() + : ""; + return message + ? `> Stability: 0 - Deprecated: ${message}` + : `> Stability: 0 - Deprecated`; + } + if (isExperimental) { + return `> Stability: 1 - Experimental`; + } + if (legacy) { + const message = legacy.content?.length + ? ctx.helpers.getCommentParts(legacy.content).trim() + : ""; + return message + ? `> Stability: 3 - Legacy: ${message}` + : `> Stability: 3 - Legacy`; + } + return null; }, -}); +}); \ No newline at end of file diff --git a/plugins/theme/index.mjs b/plugins/theme/index.mjs index fd19527..4891923 100644 --- a/plugins/theme/index.mjs +++ b/plugins/theme/index.mjs @@ -14,6 +14,9 @@ export class DocKitTheme extends MarkdownTheme { export class DocKitThemeContext extends MarkdownThemeContext { helpers = helpers(this); partials = partials(this); + templates = { + ...this.templates, + }; } /** @param {import('typedoc').Application} app */ diff --git a/plugins/theme/partials/index.mjs b/plugins/theme/partials/index.mjs index 89f08eb..743b42d 100644 --- a/plugins/theme/partials/index.mjs +++ b/plugins/theme/partials/index.mjs @@ -62,26 +62,14 @@ export default (ctx) => ({ }, memberTitle(model) { + const params = model.signatures?.[0]?.parameters ?? []; if (model.kind === ReflectionKind.Constructor) { - return ctx.helpers.buildConstructorTitle(model); + const className = model.parent?.name ?? model.name; + return ctx.helpers.signatureTitle(`new ${className}`, params); } - const prefix = getMemberPrefix(model); - const params = model.signatures?.[0]?.parameters; - if (!params) { - return `${prefix}\`${model.name}\``; - } - const paramsString = params - .map((param, index) => { - const paramName = param.name; - if (param.flags?.isOptional) { - return index === 0 ? `[${paramName}]` : `[, ${paramName}]`; - } else { - return index === 0 ? paramName : `, ${paramName}`; - } - }) - .join(""); - return `${prefix}\`${model.name}(${paramsString})\``; + if (!params.length) return `${prefix}\`${model.name}\``; + return `${prefix}${ctx.helpers.signatureTitle(model.name, params)}`; }, memberContainer: (model, options) => {