Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

feat: add namespace support & add extendedDescription property & add examples on class #1

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
20 changes: 16 additions & 4 deletions src/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from 'path';
import type { JSONOutput } from 'typedoc';
import type { customSettings, ProjectData } from './index';
import { ClassDoc, parseClass } from './util/class';
import { NamespaceDoc, parseNamespace } from './util/namespace';
import { TypedefDoc, parseTypedef } from './util/typedef';
import { version } from '../package.json';

Expand All @@ -26,21 +27,24 @@ interface CodeDoc {
// interfaces: unknown[]
// external: unknown[]
typedefs: TypedefDoc[];
namespaces: NamespaceDoc[];
}

export function generateDocs(data: ProjectData): CodeDoc {
const classes = [];
const classes: ClassDoc[] = [];
// interfaces = [], // not using this at the moment
// externals = [], // ???
const typedefs = [];
const typedefs: TypedefDoc[] = [];
const namespaces: NamespaceDoc[] = [];

for (const c of data.children ?? []) {
const { type, value } = parseRootElement(c);
if (!value) continue;

if (type === 'class') classes.push(value);
// if (type == 'interface') interfaces.push(value)
if (type === 'typedef') typedefs.push(value);
// if (type === 'interface') interfaces.push(value);
if (type === 'typedef') typedefs.push(value as TypedefDoc);
if (type === 'namespace') namespaces.push(value);
// if (type == 'external') externals.push(value)
}

Expand All @@ -49,6 +53,7 @@ export function generateDocs(data: ProjectData): CodeDoc {
// interfaces,
// externals,
typedefs,
namespaces,
};
}

Expand All @@ -67,6 +72,11 @@ function parseRootElement(element: DeclarationReflection) {
type: 'typedef',
value: parseTypedef(element),
};
case 'Namespace':
return {
type: 'namespace',
value: parseNamespace(element),
};

// Externals?

Expand All @@ -91,4 +101,6 @@ export function parseMeta(element: DeclarationReflection): DocMeta | undefined {
path: path.dirname(meta.fileName),
};
}

return undefined;
}
87 changes: 50 additions & 37 deletions src/util/class.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import path from 'node:path';

import { DeclarationReflection, DocMeta, parseMeta } from '../documentation';
import { DocType, parseType, parseTypeSimple } from './types';

export interface ClassDoc {
name: string;
description?: string | undefined;
see?: string[] | undefined;
extends?: [string] | undefined;
implements?: [string] | undefined;
access?: 'private' | undefined;
abstract?: boolean | undefined;
deprecated?: boolean | undefined;
access?: 'private' | undefined;
construct?: ClassMethodDoc | undefined;
props?: ClassPropDoc[] | undefined;
methods?: ClassMethodDoc[] | undefined;
deprecated?: boolean | undefined;
description?: string | undefined;
events?: ClassEventDoc[] | undefined;
examples?: string[] | undefined;
extendedDescription?: string | undefined;
extends?: [string] | undefined;
implements?: [string] | undefined;
isExternal?: boolean | undefined;
meta?: DocMeta | undefined;
methods?: ClassMethodDoc[] | undefined;
name: string;
props?: ClassPropDoc[] | undefined;
see?: string[] | undefined;
}

export function parseClass(element: DeclarationReflection): ClassDoc {
const extended = element.extendedTypes?.[0];
const implemented = element.implementedTypes?.[0];
const examples = element.comment?.tags?.filter((t) => t.tag === 'example')?.map((t) => t.text.trim());

const construct = element.children?.find((c) => c.kindString === 'Constructor');
// Ignore setter-only accessors (the typings still exist, but the docs don't show them)
const props = element.children?.filter(
Expand All @@ -35,9 +39,11 @@ export function parseClass(element: DeclarationReflection): ClassDoc {
return {
name: element.name === 'default' ? path.parse(meta?.file ?? 'default').name : element.name,
description: element.comment?.shortText?.trim(),
extendedDescription: element.comment?.text?.trim(),
see: element.comment?.tags?.filter((t) => t.tag === 'see').map((t) => t.text.trim()),
extends: extended ? [parseTypeSimple(extended)] : undefined,
implements: implemented ? [parseTypeSimple(implemented)] : undefined,
examples: examples ? examples : undefined,
access:
element.flags.isPrivate || element.comment?.tags?.some((t) => t.tag === 'private' || t.tag === 'internal')
? 'private'
Expand All @@ -49,29 +55,32 @@ export function parseClass(element: DeclarationReflection): ClassDoc {
methods: methods && methods.length > 0 ? methods.map(parseClassMethod) : undefined,
events: events && events.length > 0 ? events.map(parseClassEvent) : undefined,
meta,
isExternal: element.flags.isExternal,
};
}

interface ClassPropDoc {
name: string;
description?: string | undefined;
see?: string[] | undefined;
scope?: 'static' | undefined;
access?: 'private' | undefined;
readonly?: boolean | undefined;
nullable?: never | undefined; // it would already be in the type
abstract?: boolean | undefined;
deprecated?: boolean | undefined;
access?: 'private' | undefined;
default?: string | boolean | number | undefined;
type?: DocType | undefined;
props?: never | undefined; // prefer using a type reference (like a dedicated instance) instead of documenting using @property tags
deprecated?: boolean | undefined;
description?: string | undefined;
extendedDescription?: string | undefined;
meta?: DocMeta | undefined;
name: string;
nullable?: never | undefined; // it would already be in the type
props?: never | undefined; // prefer using a type reference (like a dedicated instance) instead of documenting using @property tags
readonly?: boolean | undefined;
scope?: 'static' | undefined;
see?: string[] | undefined;
type?: DocType | undefined;
}

function parseClassProp(element: DeclarationReflection): ClassPropDoc {
const base: ClassPropDoc = {
name: element.name,
description: element.comment?.shortText?.trim(),
extendedDescription: element.comment?.text?.trim(),
see: element.comment?.tags?.filter((t) => t.tag === 'see').map((t) => t.text.trim()),
scope: element.flags.isStatic ? 'static' : undefined,
access:
Expand Down Expand Up @@ -108,6 +117,7 @@ function parseClassProp(element: DeclarationReflection): ClassPropDoc {
return {
...res,
description: getter.comment?.shortText?.trim(),
extendedDescription: getter.comment?.text?.trim(),
see: getter.comment?.tags?.filter((t) => t.tag === 'see').map((t) => t.text.trim()),
access:
getter.flags.isPrivate || getter.comment?.tags?.some((t) => t.tag === 'private' || t.tag === 'internal')
Expand All @@ -130,19 +140,20 @@ function parseClassProp(element: DeclarationReflection): ClassPropDoc {
}

interface ClassMethodDoc {
name: string;
description?: string | undefined;
see?: string[] | undefined;
scope?: 'static' | undefined;
access?: 'private' | undefined;
inherits?: never | undefined; // let's just don't
inherited?: never | undefined; // let's just don't
implements?: never | undefined; // let's just don't
examples?: string[] | undefined;
abstract?: boolean | undefined;
access?: 'private' | undefined;
async?: never | undefined; // it would already be in the type
deprecated?: boolean | undefined;
description?: string | undefined;
emits?: string[] | undefined;
throws?: never | undefined; // let's just don't
examples?: string[] | undefined;
extendDescription?: string | undefined;
generator?: never | undefined; // not used by djs
implements?: never | undefined; // let's just don't
inherited?: never | undefined; // let's just don't
inherits?: never | undefined; // let's just don't
meta?: DocMeta | undefined;
name: string;
params?:
| {
name: string;
Expand All @@ -154,11 +165,11 @@ interface ClassMethodDoc {
type?: DocType | undefined;
}[]
| undefined;
async?: never | undefined; // it would already be in the type
generator?: never | undefined; // not used by djs
returns?: DocType | undefined;
returnsDescription?: string | undefined;
meta?: DocMeta | undefined;
scope?: 'static' | undefined;
see?: string[] | undefined;
throws?: never | undefined; // let's just don't
}

export function parseClassMethod(element: DeclarationReflection): ClassMethodDoc {
Expand All @@ -167,6 +178,7 @@ export function parseClassMethod(element: DeclarationReflection): ClassMethodDoc
return {
name: element.name,
description: signature.comment?.shortText?.trim(),
extendDescription: signature.comment?.text?.trim(),
see: signature.comment?.tags?.filter((t) => t.tag === 'see').map((t) => t.text.trim()),
scope: element.flags.isStatic ? 'static' : undefined,
access:
Expand Down Expand Up @@ -201,10 +213,11 @@ export function parseParam(param: DeclarationReflection): ClassMethodParamDoc {
}

interface ClassEventDoc {
name: string;
description?: string | undefined;
see?: string[] | undefined;
deprecated?: boolean | undefined;
description?: string | undefined;
extendedDescription?: string | undefined;
meta?: DocMeta | undefined;
name: string;
params?:
| {
name: string;
Expand All @@ -216,7 +229,7 @@ interface ClassEventDoc {
type?: DocType | undefined;
}[]
| undefined;
meta?: DocMeta | undefined;
see?: string[] | undefined;
}

function parseClassEvent(element: DeclarationReflection): ClassEventDoc {
Expand Down
34 changes: 34 additions & 0 deletions src/util/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DocMeta, parseMeta, type DeclarationReflection } from '../documentation';
import { parseTypedef, TypedefDoc } from './typedef';

export interface NamespaceDoc {
deprecated?: boolean | undefined;
description?: string | undefined;
enumerations?: TypedefDoc[] | undefined;
extendedDescription?: string | undefined;
interfaces?: TypedefDoc[] | undefined;
isExternal?: boolean | undefined;
meta?: DocMeta | undefined;
name: string;
see?: string[] | undefined;
typeAliases?: TypedefDoc[] | undefined;
}

export function parseNamespace(element: DeclarationReflection): NamespaceDoc {
const typeAliases = element.children?.filter((c) => c.kindString === 'Type alias');
const interfaces = element.children?.filter((c) => c.kindString === 'Interface');
const enumerations = element.children?.filter((c) => c.kindString === 'Enumeration');

return {
name: element.name,
description: element.comment?.shortText?.trim(),
extendedDescription: element.comment?.text?.trim(),
see: element.comment?.tags?.filter((t) => t.tag === 'see').map((t) => t.text.trim()),
deprecated: element.comment?.tags?.some((t) => t.tag === 'deprecated'),
typeAliases: typeAliases && typeAliases.length > 0 ? typeAliases.map(parseTypedef) : undefined,
interfaces: interfaces && interfaces.length > 0 ? interfaces.map(parseTypedef) : undefined,
enumerations: enumerations && enumerations.length > 0 ? enumerations.map(parseTypedef) : undefined,
meta: parseMeta(element),
isExternal: element.flags.isExternal,
};
}
30 changes: 24 additions & 6 deletions src/util/typedef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@ import type { ClassMethodParamDoc } from './class';
import { DocType, parseType, typeUtil } from './types';

export interface TypedefDoc {
name: string;
description?: string | undefined;
see?: string[] | undefined;
access?: 'private' | undefined;
deprecated?: boolean | undefined;
type?: DocType | undefined;
props?: ClassMethodParamDoc[] | undefined;
description?: string | undefined;
extendedDescription?: string | undefined;
isExternal?: boolean | undefined;
meta?: DocMeta | undefined;
name: string;
params?: ClassMethodParamDoc[] | undefined;
props?: ClassMethodParamDoc[] | undefined;
returns?: DocType | undefined;
returnsDescription?: string | undefined;
meta?: DocMeta | undefined;
see?: string[] | undefined;
type?: DocType | undefined;
variant: 'type' | 'interface' | 'enum';
}

function parseKindString(kindString: DeclarationReflection['kindString']): TypedefDoc['variant'] {
switch (kindString?.toLowerCase()) {
case 'interface':
return 'interface';
case 'enumeration':
return 'enum';
case 'type alias':
default:
return 'type';
}
}

export function parseTypedef(element: DeclarationReflection): TypedefDoc {
const baseReturn: TypedefDoc = {
name: element.name,
description: element.comment?.shortText?.trim(),
extendedDescription: element.comment?.text?.trim(),
see: element.comment?.tags?.filter((t) => t.tag === 'see').map((t) => t.text.trim()),
access:
element.flags.isPrivate || element.comment?.tags?.some((t) => t.tag === 'private' || t.tag === 'internal')
Expand All @@ -29,6 +45,8 @@ export function parseTypedef(element: DeclarationReflection): TypedefDoc {
// @ts-ignore
type: element.type ? parseType(element.type) : undefined,
meta: parseMeta(element),
variant: parseKindString(element.kindString),
isExternal: element.flags.isExternal,
};

let typeDef: DeclarationReflection | undefined;
Expand Down
10 changes: 9 additions & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ export const tsup: Options = {
dts: true,
entryPoints: ['src/index.ts'],
format: ['esm', 'cjs'],
minify: true,
minify: false,
keepNames: true,
skipNodeModulesBundle: true,
sourcemap: true,
target: 'es2021',
esbuildOptions: (options, context) => {
if (context.format === 'cjs') {
options.banner = {
js: '"use strict";',
};
}
},
};