diff --git a/src/lib/docs.ts b/src/lib/docs.ts index b8b7754..fbe7c41 100644 --- a/src/lib/docs.ts +++ b/src/lib/docs.ts @@ -11,7 +11,8 @@ import type { TypeChecker, Symbol as TypeScriptSymbol, VariableDeclaration, - VariableStatement + VariableStatement, + EnumDeclaration, } from 'typescript'; import { ModifierFlags, @@ -32,7 +33,8 @@ import { isModuleDeclaration, isPropertySignature, isTypeAliasDeclaration, - isVariableStatement + isVariableStatement, + isEnumDeclaration, } from 'typescript'; import type {BuildOptions, DocEntry, DocEntryConstructor, DocEntryType} from './types'; @@ -55,6 +57,41 @@ const serializeSymbol = ({ }; }; +const serializeEnum = ({ + checker, + symbol, +}: { + checker: TypeChecker; + symbol: TypeScriptSymbol; + doc_type?: DocEntryType; +}): DocEntry => { + const node = symbol.valueDeclaration as EnumDeclaration + const properties: DocEntry[] = [] + node.members.forEach(member=>{ + const type = member.initializer?.getText() + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const documentation = displayPartsToString(member.symbol.getDocumentationComment(checker)) + const property: DocEntry = { + name: member.name.getText(), + } + if (type){ + property.type = type + } + if (documentation){ + property.documentation = documentation + } + properties.push(property) + }) + return { + name: symbol.getName(), + documentation: displayPartsToString(symbol.getDocumentationComment(checker)), + properties, + jsDocs: symbol.getJsDocTags(), + doc_type: 'enum', + }; +}; + /** Serialize a class symbol information */ const serializeClass = ({ checker, @@ -299,6 +336,16 @@ const visit = ({ entries.push(typeEntry); } + } else if (isEnumDeclaration(node)) { + const symbol = checker.getSymbolAtLocation((node as EnumDeclaration).name)!; + const details = serializeEnum({checker, symbol}); + entries.push({ + ...details, + ...buildSource({ + node, + ...rest + }) + }); } } diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index cc23c6b..be2f77d 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -137,7 +137,7 @@ const toMarkdown = ({ }: { entries: DocEntry[]; headingLevel: MarkdownHeadingLevel | '####'; - docType: 'Constant' | 'Function' | 'Method' | 'Type'; + docType: 'Constant' | 'Function' | 'Method' | 'Type' | 'Enum'; } & Pick): string => { const jsDocsToParams = (jsDocs: JSDocTagInfo[]): Params[] => { const params: JSDocTagInfo[] = jsDocs.filter(({name}: JSDocTagInfo) => name === 'param'); @@ -234,6 +234,7 @@ const DEFAULT_EMOJI: MarkdownEmoji = { classes: 'factory', functions: 'toolbox', constants: 'wrench', + enum: 'tropical_drink', entry: 'gear', link: 'link', interfaces: 'tropical_drink', @@ -267,6 +268,7 @@ export const documentationToMarkdown = ({ const functions: DocEntry[] = entries.filter(({doc_type}: DocEntry) => doc_type === 'function'); const classes: DocEntry[] = entries.filter(({doc_type}: DocEntry) => doc_type === 'class'); const constants: DocEntry[] = entries.filter(({doc_type}: DocEntry) => doc_type === 'const'); + const enums: DocEntry[] = entries.filter(({doc_type}: DocEntry) => doc_type === 'enum'); const types: DocEntry[] = entries.filter(({doc_type}: DocEntry) => doc_type === 'type'); const interfaces: DocEntry[] = entries.filter(({doc_type}: DocEntry) => doc_type === 'interface'); @@ -287,11 +289,18 @@ export const documentationToMarkdown = ({ `${toMarkdown({entries: constants, headingLevel, emoji, docType: 'Constant'})}\n` ); } - markdown.push( classes.map((entry: DocEntry) => classesToMarkdown({entry, headingLevel, emoji})).join('\n') ); - + if (enums.length) { + markdown.push(`${headingLevel}${emojiTitle({emoji, key: 'enum'})} Enum\n`); + markdown.push(`${tableOfContent({entries: enums, emoji})}\n`); + markdown.push( + enums + .map((entry: DocEntry) => interfacesToMarkdown({entry, headingLevel, emoji})) + .join('\n') + ); + } if (interfaces.length) { markdown.push(`${headingLevel}${emojiTitle({emoji, key: 'interfaces'})} Interfaces\n`); markdown.push(`${tableOfContent({entries: interfaces, emoji})}\n`); diff --git a/src/lib/types.ts b/src/lib/types.ts index 353fb1f..03b1534 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,6 +1,6 @@ import type {CompilerOptions, JSDocTagInfo} from 'typescript'; -export type DocEntryType = 'function' | 'method' | 'class' | 'const' | 'interface' | 'type'; +export type DocEntryType = 'function' | 'method' | 'class' | 'const' | 'interface' | 'type' | 'enum'; export type DocEntryConstructor = Pick & { visibility: 'private' | 'public'; @@ -29,6 +29,7 @@ export interface MarkdownEmoji { classes: string; functions: string; constants: string; + enum: string; // A function, method or constant title - i.e. an entry of one above titles entry: string; link: string; diff --git a/src/test/markdown.spec.ts b/src/test/markdown.spec.ts index 3e6ca28..4ecf73c 100644 --- a/src/test/markdown.spec.ts +++ b/src/test/markdown.spec.ts @@ -13,11 +13,9 @@ describe('markdown', () => { types: true } }); - const markdown: string = documentationToMarkdown({ entries: doc }); - const expectedDoc = readFileSync('./src/test/mock.md', 'utf8').replace(/\r\n/g, '\n'); expect(markdown).toEqual(expectedDoc); diff --git a/src/test/mock.json b/src/test/mock.json index 9afb726..97ee143 100644 --- a/src/test/mock.json +++ b/src/test/mock.json @@ -7,7 +7,10 @@ { "name": "param", "text": [ - {"kind": "parameterName", "text": "yolo"}, + { + "kind": "parameterName", + "text": "yolo" + }, { "kind": "space", "text": " " @@ -57,7 +60,10 @@ { "name": "param", "text": [ - {"kind": "parameterName", "text": "agent"}, + { + "kind": "parameterName", + "text": "agent" + }, { "kind": "space", "text": " " @@ -74,13 +80,33 @@ }, { "documentation": "", - "jsDocs": [{"name": "param", "text": [{"kind": "text", "text": "canisterId"}]}], + "jsDocs": [ + { + "name": "param", + "text": [ + { + "kind": "text", + "text": "canisterId" + } + ] + } + ], "name": "canisterId", "type": "{ canisterId: string; }" }, { "documentation": "", - "jsDocs": [{"name": "param", "text": [{"kind": "text", "text": "hardwareWallet"}]}], + "jsDocs": [ + { + "name": "param", + "text": [ + { + "kind": "text", + "text": "hardwareWallet" + } + ] + } + ], "name": "hardwareWallet", "type": "boolean" } @@ -97,7 +123,17 @@ { "doc_type": "method", "documentation": "Create a LedgerCanister", - "jsDocs": [{"name": "param", "text": [{"kind": "text", "text": "params"}]}], + "jsDocs": [ + { + "name": "param", + "text": [ + { + "kind": "text", + "text": "params" + } + ] + } + ], "name": "create", "type": "(options: { canisterId?: string | undefined; }) => LedgerCanister" }, @@ -105,14 +141,34 @@ "doc_type": "function", "documentation": "Returns the balance of the specified account identifier.", "jsDocs": [ - {"name": "param", "text": [{"kind": "text", "text": "params"}]}, + { + "name": "param", + "text": [ + { + "kind": "text", + "text": "params" + } + ] + }, { "name": "throws", "text": [ - {"kind": "text", "text": "an "}, - {"kind": "link", "text": "{@link "}, - {"kind": "linkText", "text": "Error "}, - {"kind": "link", "text": "}"} + { + "kind": "text", + "text": "an " + }, + { + "kind": "link", + "text": "{@link " + }, + { + "kind": "linkText", + "text": "Error " + }, + { + "kind": "link", + "text": "}" + } ] } ], @@ -190,14 +246,34 @@ "jsDocs": [], "doc_type": "interface", "properties": [ - {"name": "hello", "documentation": "Says hello.", "type": "string", "jsDocs": []}, + { + "name": "hello", + "documentation": "Says hello.", + "type": "string", + "jsDocs": [] + }, { "name": "world", "documentation": "Something", "type": "string | undefined", - "jsDocs": [{"name": "default", "text": [{"text": "`hello`", "kind": "text"}]}] + "jsDocs": [ + { + "name": "default", + "text": [ + { + "text": "`hello`", + "kind": "text" + } + ] + } + ] }, - {"name": "abc", "documentation": "", "type": "Abc", "jsDocs": []} + { + "name": "abc", + "documentation": "", + "type": "Abc", + "jsDocs": [] + } ], "fileName": "src/test/mock.ts" }, @@ -216,5 +292,41 @@ "jsDocs": [], "doc_type": "type", "fileName": "src/test/mock.ts" + }, + { + "name": "Time", + "documentation": "", + "properties": [ + { + "name": "SECOND", + "type": "1000" + }, + { + "name": "MINUTE", + "type": "60 * SECOND" + } + ], + "jsDocs": [], + "doc_type": "enum", + "fileName": "src/test/mock.ts" + }, + { + "name": "MemberType", + "documentation": "", + "properties": [ + { + "name": "T1" + }, + { + "name": "T2", + "documentation": "comment" + }, + { + "name": "T3" + } + ], + "jsDocs": [], + "doc_type": "enum", + "fileName": "src/test/mock.ts" } ] diff --git a/src/test/mock.md b/src/test/mock.md index 70d758f..e83eac7 100644 --- a/src/test/mock.md +++ b/src/test/mock.md @@ -153,6 +153,32 @@ Description [:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L118) +## :tropical_drink: Enum + +- [Time](#gear-time) +- [MemberType](#gear-membertype) + +### :gear: Time + + + +| Property | Type | Description | +| ---------- | ---------- | ---------- | +| `SECOND` | `1000` | | +| `MINUTE` | `60 * SECOND` | | + + +### :gear: MemberType + + + +| Property | Type | Description | +| ---------- | ---------- | ---------- | +| `T1` | `` | | +| `T2` | `` | comment | +| `T3` | `` | | + + ## :tropical_drink: Interfaces - [Foo](#gear-foo) diff --git a/src/test/mock.ts b/src/test/mock.ts index 292fcff..e88f5dc 100644 --- a/src/test/mock.ts +++ b/src/test/mock.ts @@ -146,3 +146,17 @@ export type yolo = 'string'; * A type yolo */ export type Abc = Foo & {hello: string}; + +export enum Time { + SECOND = 1000, + MINUTE = 60 * SECOND, +} + +export enum MemberType { + T1, + /** + * comment + */ + T2, + T3, +} \ No newline at end of file