Skip to content

Commit

Permalink
docs: improve visualization for complex types (#608)
Browse files Browse the repository at this point in the history
  • Loading branch information
ST-DDT authored Mar 15, 2022
1 parent c933d24 commit f038937
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 16 deletions.
94 changes: 84 additions & 10 deletions scripts/apidoc/signature.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import sanitizeHtml from 'sanitize-html';
import type {
Comment,
DeclarationReflection,
ParameterReflection,
Reflection,
SignatureReflection,
SomeType,
Type,
} from 'typedoc';
import { ReflectionFlag, ReflectionKind } from 'typedoc';
import { createMarkdownRenderer } from 'vitepress';
Expand Down Expand Up @@ -78,7 +80,8 @@ export function analyzeSignature(
for (const parameter of typeParameters) {
signatureTypeParameters.push(parameter.name);
parameters.push({
name: parameter.name,
name: `<${parameter.name}>`,
type: parameter.type ? typeToText(parameter.type) : undefined,
description: mdToHtml(toBlock(parameter.comment)),
});
}
Expand Down Expand Up @@ -145,7 +148,7 @@ export function analyzeSignature(
title: prettyMethodName,
description: mdToHtml(toBlock(signature.comment)),
parameters: parameters,
returns: signature.type.toString(),
returns: typeToText(signature.type),
examples: mdToHtml('```ts\n' + examples + '```'),
deprecated: signature.comment?.hasTag('deprecated') ?? false,
seeAlsos,
Expand All @@ -159,20 +162,19 @@ function analyzeParameter(parameter: ParameterReflection): {
const name = parameter.name;
const declarationName = name + (isOptional(parameter) ? '?' : '');
const type = parameter.type;
const typeText = type.toString();
const defaultValue = parameter.defaultValue;

let signatureText = '';
if (defaultValue) {
signatureText = ' = ' + defaultValue;
}

const signature = declarationName + ': ' + typeText + signatureText;
const signature = declarationName + ': ' + typeToText(type) + signatureText;

const parameters: MethodParameter[] = [
{
name: declarationName,
type: typeText,
type: typeToText(type, true),
default: defaultValue,
description: mdToHtml(toBlock(parameter.comment)),
},
Expand All @@ -194,13 +196,13 @@ function analyzeParameterOptions(
analyzeParameterOptions(name, type)
);
} else if (parameterType.type === 'reflection') {
const properties = parameterType.declaration.getChildrenByKind(
ReflectionKind.Property
);
const properties = parameterType.declaration.children ?? [];
return properties.map((property) => ({
name: `${name}.${property.name}${isOptional(property) ? '?' : ''}`,
type: property.type.toString(),
description: mdToHtml(toBlock(property.comment)),
type: declarationTypeToText(property),
description: mdToHtml(
toBlock(property.comment ?? property.signatures?.[0].comment)
),
}));
}

Expand All @@ -210,3 +212,75 @@ function analyzeParameterOptions(
function isOptional(parameter: Reflection): boolean {
return parameter.flags.hasFlag(ReflectionFlag.Optional);
}

function typeToText(type_: Type, short = false): string {
const type = type_ as SomeType;
switch (type.type) {
case 'array':
return `${typeToText(type.elementType, short)}[]`;
case 'union':
return type.types
.map((t) => typeToText(t, short))
.sort()
.join(' | ');
case 'reference':
if (
typeof type.typeArguments === 'undefined' ||
!type.typeArguments.length
) {
return type.name;
} else {
return `${type.name}<${type.typeArguments
.map((t) => typeToText(t, short))
.join(', ')}>`;
}
case 'reflection':
return declarationTypeToText(type.declaration, short);
case 'indexedAccess':
return `${typeToText(type.objectType, short)}[${typeToText(
type.indexType,
short
)}]`;
default:
return type.toString();
}
}

function declarationTypeToText(
declaration: DeclarationReflection,
short = false
): string {
switch (declaration.kind) {
case ReflectionKind.Method:
return signatureTypeToText(declaration.signatures[0]);
case ReflectionKind.Property:
return typeToText(declaration.type);
case ReflectionKind.TypeLiteral:
if (declaration.children?.length) {
if (short) {
// This is too long for the parameter table, thus we abbreviate this.
return '{ ... }';
}
return (
'{' +
declaration.children
.map((c) => `\n${c.name}: ${declarationTypeToText(c)}`)
.join()
.replace(/\n/g, '\n ') +
'\n}'
);
} else if (declaration.signatures?.length) {
return signatureTypeToText(declaration.signatures[0]);
} else {
return declaration.toString();
}
default:
return declaration.toString();
}
}

function signatureTypeToText(signature: SignatureReflection): string {
return `(${signature.parameters
.map((p) => `${p.name}: ${typeToText(p.type)}`)
.join(', ')}) => ${typeToText(signature.type)}`;
}
2 changes: 1 addition & 1 deletion src/unique.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class Unique {
* @param opts.compare The function used to determine whether a value was already returned.
*
* @example
* faker.unique(faker.name.firstName)
* faker.unique(faker.name.firstName) // 'Corbin'
*/
unique<Method extends (...parameters) => RecordKey>(
method: Method,
Expand Down
8 changes: 7 additions & 1 deletion test/scripts/apidoc/signature.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,14 @@ export class SignatureTest {
* @param options.a The number parameter.
* @param options.b The string parameter.
* @param options.c The boolean parameter.
* @param options.d The method parameter.
*/
optionsParamMethod(options: { a: number; b?: string; c: boolean }): number {
optionsParamMethod(options: {
a: number;
b?: string;
c: boolean;
d: () => string;
}): number {
return options.c ? options.a : +options.b;
}

Expand Down
13 changes: 9 additions & 4 deletions test/scripts/apidoc/signature.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@
"parameters": [
{
"name": "fn",
"type": "Function",
"type": "(a: string) => number",
"description": "<p>The function parameter.</p>\n"
}
],
"returns": "number",
"examples": "<div class=\"language-ts\"><pre v-pre><code>faker<span class=\"token punctuation\">.</span><span class=\"token function\">functionParamMethod</span><span class=\"token punctuation\">(</span>fn<span class=\"token operator\">:</span> <span class=\"token builtin\">Function</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n</code></pre>\n</div>",
"examples": "<div class=\"language-ts\"><pre v-pre><code>faker<span class=\"token punctuation\">.</span><span class=\"token function\">functionParamMethod</span><span class=\"token punctuation\">(</span><span class=\"token function-variable function\">fn</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>a<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=&gt;</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n</code></pre>\n</div>",
"deprecated": false,
"seeAlsos": []
},
Expand Down Expand Up @@ -112,7 +112,7 @@
"parameters": [
{
"name": "options",
"type": "Object",
"type": "{ ... }",
"description": "<p>The function parameter.</p>\n"
},
{
Expand All @@ -129,10 +129,15 @@
"name": "options.c",
"type": "boolean",
"description": "<p>The boolean parameter.</p>\n"
},
{
"name": "options.d",
"type": "() => string",
"description": "<p>The method parameter.</p>\n"
}
],
"returns": "number",
"examples": "<div class=\"language-ts\"><pre v-pre><code>faker<span class=\"token punctuation\">.</span><span class=\"token function\">optionsParamMethod</span><span class=\"token punctuation\">(</span>options<span class=\"token operator\">:</span> Object<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n</code></pre>\n</div>",
"examples": "<div class=\"language-ts\"><pre v-pre><code>faker<span class=\"token punctuation\">.</span><span class=\"token function\">optionsParamMethod</span><span class=\"token punctuation\">(</span>options<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n a<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span>\n b<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n c<span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">,</span>\n <span class=\"token function-variable function\">d</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=&gt;</span> <span class=\"token builtin\">string</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n</code></pre>\n</div>",
"deprecated": false,
"seeAlsos": []
},
Expand Down

0 comments on commit f038937

Please sign in to comment.