From 6664dea94de92e763497de93ab025bfff648f7e8 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Mon, 16 Dec 2024 20:25:25 +0100 Subject: [PATCH] feat: support for class properties (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: support for class properties * 🤖 Documentation auto-update * feat: keep undefined if nothing visited * 🤖 Documentation auto-update --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- README.md | 4 ++-- src/lib/docs.ts | 27 ++++++++++++++++++++++-- src/lib/markdown.ts | 43 +++++++++++++++++++++++++-------------- src/lib/types.ts | 1 + src/test/markdown.spec.ts | 2 +- src/test/mock.json | 9 ++++++++ src/test/mock.md | 37 +++++++++++++++++++++------------ src/test/mock.ts | 17 ++++++++++++++++ 8 files changed, 107 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 91d7705..c68995d 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Parameters: - `params.inputFiles`: The list of files to scan and for which the documentation should be build. - `params.options`: Optional compiler options to generate the docs -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/lib/docs.ts#L402) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/lib/docs.ts#L426) ### :gear: documentationToMarkdown @@ -164,7 +164,7 @@ Parameters: - `params.entries`: The entries of the documentation (functions, constants and classes). - `params.options`: Optional configuration to render the Markdown content. See `types.ts` for details. -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/lib/markdown.ts#L280) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/lib/markdown.ts#L293) ### :gear: generateDocumentation diff --git a/src/lib/docs.ts b/src/lib/docs.ts index 48fb694..e88c053 100644 --- a/src/lib/docs.ts +++ b/src/lib/docs.ts @@ -32,6 +32,7 @@ import { isInterfaceDeclaration, isMethodDeclaration, isModuleDeclaration, + isPropertyDeclaration, isPropertySignature, isTypeAliasDeclaration, isVariableStatement @@ -117,7 +118,10 @@ const isNodeExportedOrPublic = (node: Node): boolean => { const flags = getCombinedModifierFlags(node as Declaration); // Check for '#' methods or properties - if (isMethodDeclaration(node) && node.name.kind === SyntaxKind.PrivateIdentifier) { + if ( + (isMethodDeclaration(node) || isPropertyDeclaration(node)) && + node.name.kind === SyntaxKind.PrivateIdentifier + ) { return false; } @@ -255,9 +259,24 @@ const visit = ({ const visitChild = (node: Node) => { const docEntries: DocEntry[] = visit({node, checker, types, ...rest}); + // We do not need to repeat the file name for class members // eslint-disable-next-line @typescript-eslint/no-unused-vars - classEntry.methods?.push(...docEntries.map(({fileName: _, ...rest}) => rest)); + const omitFilename = ({fileName: _, ...rest}: DocEntry): Omit => rest; + + classEntry.methods?.push( + ...docEntries + .filter(({doc_type}) => doc_type === 'method' || doc_type === 'function') + .map(omitFilename) + ); + + const properties = docEntries + .filter(({doc_type}) => doc_type === 'property') + .map(omitFilename); + + if (properties.length > 0) { + classEntry.properties = [...(classEntry?.properties ?? []), ...properties]; + } }; forEachChild(node, visitChild); @@ -286,6 +305,10 @@ const visit = ({ ((arrowFunc as ArrowFunction).parent as VariableDeclaration).name ); addDocEntry({symbol, doc_type: 'function', node: arrowFunc}); + } else if (isPropertyDeclaration(node)) { + // We test for the property after the arrow function because a public property of a class can be an arrow function. + const symbol = checker.getSymbolAtLocation(node.name); + addDocEntry({symbol, doc_type: 'property', node}); } else if (isVariableStatement(node)) { const { declarationList: {declarations, flags} diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index 3cf9394..5ccb038 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -35,7 +35,7 @@ const classesToMarkdown = ({ entry: DocEntry; } & Required> & Omit): string => { - const {name, url, documentation, methods, constructors} = entry; + const {name, url, documentation, methods, properties, constructors} = entry; const markdown: string[] = [`${headingLevel}${emojiTitle({emoji, key: 'classes'})} ${name}\n`]; @@ -70,22 +70,35 @@ const classesToMarkdown = ({ markdown.push('\n'); } - if (!methods || methods.length === 0) { - return markdown.join('\n'); + if ((methods?.length ?? 0) > 0) { + markdown.push(`${headingLevel}# Methods\n`); + markdown.push(`${tableOfContent({entries: methods ?? [], emoji})}\n`); + + // Explicitly do not pass repo to generate the source code link afterwards for the all block + markdown.push( + `${toMarkdown({ + entries: methods ?? [], + headingLevel: `${headingLevel}#`, + docType: 'Method', + emoji + })}` + ); } - markdown.push(`${headingLevel}# Methods\n`); - markdown.push(`${tableOfContent({entries: methods ?? [], emoji})}\n`); + if ((properties?.length ?? 0) > 0) { + markdown.push(`${headingLevel}# Properties\n`); + markdown.push(`${tableOfContent({entries: properties ?? [], emoji})}\n`); - // Explicitly do not pass repo to generate the source code link afterwards for the all block - markdown.push( - `${toMarkdown({ - entries: methods ?? [], - headingLevel: `${headingLevel}#`, - docType: 'Method', - emoji - })}\n` - ); + // Explicitly do not pass repo to generate the source code link afterwards for the all block + markdown.push( + `${toMarkdown({ + entries: properties ?? [], + headingLevel: `${headingLevel}#`, + docType: 'Property', + emoji + })}` + ); + } return markdown.join('\n'); }; @@ -151,7 +164,7 @@ const toMarkdown = ({ }: { entries: DocEntry[]; headingLevel: MarkdownHeadingLevel | '####'; - docType: 'Constant' | 'Function' | 'Method' | 'Type' | 'Enum'; + docType: 'Constant' | 'Function' | 'Method' | 'Property' | 'Type' | 'Enum'; } & Pick): string => { const jsDocsToParams = (jsDocs: JSDocTagInfo[]): Params[] => { const params: JSDocTagInfo[] = jsDocs.filter(({name}: JSDocTagInfo) => name === 'param'); diff --git a/src/lib/types.ts b/src/lib/types.ts index c9e23bf..4ae3678 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,6 +1,7 @@ import type {CompilerOptions, JSDocTagInfo} from 'typescript'; export type DocEntryType = + | 'property' | 'function' | 'method' | 'class' diff --git a/src/test/markdown.spec.ts b/src/test/markdown.spec.ts index 8ad3c0d..c61102f 100644 --- a/src/test/markdown.spec.ts +++ b/src/test/markdown.spec.ts @@ -21,7 +21,7 @@ describe('markdown', () => { expect(markdown).toEqual(expectedDoc); }); - it.each([35, 101, 129])('should generate a markdown link to line %s', (line) => { + it.each([35, 118, 146])('should generate a markdown link to line %s', (line) => { const doc = buildDocumentation({ inputFiles: ['./src/test/mock.ts'], options: { diff --git a/src/test/mock.json b/src/test/mock.json index d084f2e..ee13a3c 100644 --- a/src/test/mock.json +++ b/src/test/mock.json @@ -184,6 +184,15 @@ "doc_type": "method" } ], + "properties": [ + { + "name": "publicShouldBeDocumented", + "documentation": "The documentation of the public property.", + "type": "string", + "jsDocs": [], + "doc_type": "property" + } + ], "fileName": "src/test/mock.ts" }, { diff --git a/src/test/mock.md b/src/test/mock.md index c47aa5e..d99f1e5 100644 --- a/src/test/mock.md +++ b/src/test/mock.md @@ -86,7 +86,7 @@ function submit() { ``` -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L221) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L238) ## :wrench: Constants @@ -155,12 +155,25 @@ Public method. | ---------- | ---------- | | `shouldBeDocumented` | `() => void` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L80) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L97) + +### Properties + +- [publicShouldBeDocumented](#gear-publicshouldbedocumented) + +#### :gear: publicShouldBeDocumented + +The documentation of the public property. + +| Property | Type | +| ---------- | ---------- | +| `publicShouldBeDocumented` | `string` | +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L80) ## :factory: SnsLedgerCanister -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L101) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L118) ### Constructors @@ -181,7 +194,7 @@ This create function is public as well. | ---------- | ---------- | | `create` | `(options: { canisterId?: string or undefined; }) => SnsLedgerCanister` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L116) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L133) #### :gear: metadata @@ -191,12 +204,11 @@ The token metadata (name, symbol, etc.). | ---------- | ---------- | | `metadata` | `(params: QueryParams) => Promise` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L125) - +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L142) ## :factory: default -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L129) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L146) ### Methods @@ -210,8 +222,7 @@ Description | ---------- | ---------- | | `bar` | `() => void` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L133) - +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L150) ## :nut_and_bolt: Enum @@ -281,7 +292,7 @@ A type yolo | ---------- | ---------- | | `yolo` | `'string'` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L158) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L175) ### :gear: Abc @@ -291,7 +302,7 @@ A type yolo | ---------- | ---------- | | `Abc` | `Foo and {hello: string}` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L163) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L180) ### :gear: StorageConfigSourceGlob @@ -299,7 +310,7 @@ A type yolo | ---------- | ---------- | | `StorageConfigSourceGlob` | | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L234) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L251) ### :gear: SatelliteConfig @@ -307,5 +318,5 @@ A type yolo | ---------- | ---------- | | `SatelliteConfig` | `Either and CliConfig and SatelliteConfigOptions` | -[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L260) +[:link: Source](https://github.com/peterpeterparker/tsdoc-markdown/tree/main/src/test/mock.ts#L277) diff --git a/src/test/mock.ts b/src/test/mock.ts index 9c4094e..9413b89 100644 --- a/src/test/mock.ts +++ b/src/test/mock.ts @@ -74,6 +74,23 @@ export class LedgerCanister { return {icp: 1n}; }; + /** + * The documentation of the public property. + */ + publicShouldBeDocumented = 'hello'; + + /** + * Private property. + * @private + */ + private privateShouldNoBeDocumented = 'hello'; + + /** + * Private property with a # identifier. + * @private + */ + #privateShouldNoBeDocumentedNeither = 'hello'; + /** * Public method. */