From 1d875ba6671e4f63211a81191910518891e4c1f0 Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 07:15:14 -0400 Subject: [PATCH 01/10] Introducing a new configuration function (frontMatterHeader) that allows for configuring the Jekyll front matter output. --- .idea/.gitignore | 2 ++ README.md | 10 +++++++ ROADMAP.md | 16 ----------- apexdocs.config.ts | 3 +- src/cli/generate.ts | 1 + src/settings.ts | 16 +++++++++++ .../markdown/jekyll/jekyll-docsProcessor.ts | 28 +++++++++++++++++-- 7 files changed, 56 insertions(+), 20 deletions(-) delete mode 100644 ROADMAP.md diff --git a/.idea/.gitignore b/.idea/.gitignore index fb465aba..06f25a75 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -6,3 +6,5 @@ /dataSources.local.xml # Editor-based HTTP Client requests /httpRequests/ +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/README.md b/README.md index ed738d0f..adb36e5b 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,16 @@ allow you to override some of the default behavior: The full object definition can be imported from `@cparra/apexdocs/lib/settings` - `onAfterProcess` - A function that will be called after all files have been processed. It receives a `TargetFile[]` array with all of the files that were processed and does not return anything. +- `frontMatterHeader` - A function that will be called before the front matter is written to the file (when using the Jekyll generator). + It receives a `TargetType` object + and should return a list of strings that will be written to the file as the front matter. + The full object definition can be imported from `@cparra/apexdocs/lib/settings` + and contains the following properties: + - `name` - The name of the type + - `typeName` - Can be 'class', 'interface', or 'enum' + - `accessModifier` - The access modifier of the type + - `group` - The group to which the type belongs (if any) + - `description` - The description of the type as defined in the ApexDoc ```typescript import {TargetFile} from "@cparra/apexdocs/lib/settings"; diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index b2be051d..00000000 --- a/ROADMAP.md +++ /dev/null @@ -1,16 +0,0 @@ -[ ] Respect access modifiers where the properties/methods are different from the class declaration. For example, -AuraEnabled properties do not live in an AuraEnabled class, so there's no way to just generate docs to showcase the -AuraEnabled properties of a class without some sort of combination of also exposing other public/globals - -[ ] Versioning capabilities. When creating the doc you specify a version number, and a new directory is created for the -files, instead of just overriding - -[ ] New generatortype: To JsDocs from AuraEnabled - -[ ] More unit tests - -[ ] config.js support to allow for injections, home header, etc. - -[ ] Add configuration setting that allows someone to set the "namespace" - -[ ] Homepage support similar to what docsify does diff --git a/apexdocs.config.ts b/apexdocs.config.ts index 9516f54d..fa7445bc 100644 --- a/apexdocs.config.ts +++ b/apexdocs.config.ts @@ -1,4 +1,4 @@ -import { TargetFile } from './src/settings'; +import { TargetFile, TargetType } from './src/settings'; export default { onBeforeFileWrite: (file: TargetFile): TargetFile => { @@ -8,4 +8,5 @@ export default { onAfterProcess: (files: TargetFile[]) => { console.log('onAfterProcess files', files); }, + frontMatterHeader: (file: TargetType) => [`title: ${file.name}.cls`, `description: ${file.description}`], }; diff --git a/src/cli/generate.ts b/src/cli/generate.ts index 8c74bba3..faa0a06f 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -118,6 +118,7 @@ result.then((config) => { rootDir: argv.documentationRootDir, onAfterProcess: config?.config?.onAfterProcess, onBeforeFileWrite: config?.config?.onBeforeFileWrite, + frontMatterHeader: config?.config?.frontMatterHeader, }); try { diff --git a/src/settings.ts b/src/settings.ts index d4a4f6f0..106d0d72 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -13,6 +13,14 @@ export type OutputDir = { fileDir: string; }; +export type TargetType = { + name: string; + typeName: 'class' | 'interface' | 'enum'; + accessModifier: string; + description?: string; + group?: string; +}; + export interface SettingsConfig { sourceDirectory: string; recursive: boolean; @@ -30,6 +38,7 @@ export interface SettingsConfig { rootDir?: string; onAfterProcess?: (files: TargetFile[]) => void; onBeforeFileWrite?: (file: TargetFile) => TargetFile; + frontMatterHeader?: (file: TargetType) => string[]; } export class Settings { @@ -123,4 +132,11 @@ export class Settings { } return file; } + + public frontMatterHeader(file: TargetType): string[] { + if (this.config.frontMatterHeader) { + return this.config.frontMatterHeader(file); + } + return []; + } } diff --git a/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts b/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts index 1b972435..374bdc1e 100644 --- a/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts +++ b/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts @@ -3,6 +3,7 @@ import { Type } from '@cparra/apex-reflection'; import { MarkdownHomeFile } from '../../../model/markdown-home-file'; import { MarkdownTypeFile } from '../../../model/markdown-type-file'; import { LinkingStrategy } from '../../processor-type-transpiler'; +import { Settings } from '../../../settings'; export class JekyllDocsProcessor extends MarkdownTranspilerBase { homeFileName(): string { @@ -10,17 +11,38 @@ export class JekyllDocsProcessor extends MarkdownTranspilerBase { } onBeforeProcess = (types: Type[]) => { - this._fileContainer.pushFile(new MarkdownHomeFile(this.homeFileName(), types, this.frontMatterHeader)); + this._fileContainer.pushFile(new MarkdownHomeFile(this.homeFileName(), types, this.frontMatterForHomeFile)); }; onProcess(type: Type): void { - this._fileContainer.pushFile(new MarkdownTypeFile(type, 1, this.frontMatterHeader)); + this._fileContainer.pushFile(new MarkdownTypeFile(type, 1, this.getFrontMatterHeader(type))); } - get frontMatterHeader(): string { + get frontMatterForHomeFile(): string { return '---\nlayout: default\n---'; } + getFrontMatterHeader(type: Type): string { + const headerLines = ['---']; + headerLines.push('layout: default'); + // Add any additional front matter headers that might have been configured in the settings + const targetType = { + name: type.name, + typeName: type.type_name, + accessModifier: type.access_modifier, + group: type.group, + description: type.docComment?.description, + }; + const configuredHeaders = Settings.getInstance().frontMatterHeader(targetType); + if (configuredHeaders) { + configuredHeaders.forEach((header) => { + headerLines.push(header); + }); + } + headerLines.push('---'); + return headerLines.join('\n'); + } + getLinkingStrategy(): LinkingStrategy { return 'path-relative'; } From a8e82fd9fa226eec0b30f381561da3d20e2060dd Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 12:24:56 -0400 Subject: [PATCH 02/10] Commenting code --- src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts b/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts index 374bdc1e..ad12ecda 100644 --- a/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts +++ b/src/transpiler/markdown/jekyll/jekyll-docsProcessor.ts @@ -24,6 +24,7 @@ export class JekyllDocsProcessor extends MarkdownTranspilerBase { getFrontMatterHeader(type: Type): string { const headerLines = ['---']; + // "layout: default" is a required front matter header for Jekyll headerLines.push('layout: default'); // Add any additional front matter headers that might have been configured in the settings const targetType = { From 6e104f0bc430193d91e6463734e24f88debfe0aa Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 14:17:02 -0400 Subject: [PATCH 03/10] Introducing a way to sort type members when generating documentation files. --- apexdocs.config.ts | 1 + docs/types/Classes/nspc.ChildClass.md | 39 ++++++++++--------- docs/types/Main/nspc.SampleClass.md | 26 ++++++------- .../main/default/classes/ChildClass.cls | 1 + src/cli/generate.ts | 5 +++ .../field-declaration-util.ts | 12 ++---- src/model/markdown-type-file.ts | 7 ++++ src/service/walkers/class-walker.ts | 22 +++++++---- src/service/walkers/enum-walker.ts | 2 +- src/service/walkers/interface-walker.ts | 1 + src/settings.ts | 5 +++ .../markdown/class-file-generatorHelper.ts | 4 +- 12 files changed, 75 insertions(+), 50 deletions(-) diff --git a/apexdocs.config.ts b/apexdocs.config.ts index fa7445bc..83c0a7e7 100644 --- a/apexdocs.config.ts +++ b/apexdocs.config.ts @@ -9,4 +9,5 @@ export default { console.log('onAfterProcess files', files); }, frontMatterHeader: (file: TargetType) => [`title: ${file.name}.cls`, `description: ${file.description}`], + sortMembersAlphabetically: true, }; diff --git a/docs/types/Classes/nspc.ChildClass.md b/docs/types/Classes/nspc.ChildClass.md index bee452bf..a281181f 100644 --- a/docs/types/Classes/nspc.ChildClass.md +++ b/docs/types/Classes/nspc.ChildClass.md @@ -21,6 +21,9 @@ ChildClass ## Fields +### `private aPrivateString` → `String` + + ### `private privateStringFromChild` → `String` @@ -48,6 +51,24 @@ This is a protected string, use carefully. --- ## Methods ### `public void doSomething()` +### `public void execute()` + +Executes the command. + +### `public String getValue()` + +Returns a value based on the executed command. + +#### Returns + +|Type|Description| +|---|---| +|`String`|The value| + +### `public virtual String overridableMethod()` + +*Inherited* + ### `public override String overridableMethodOverridden()` This method was overridden. @@ -73,22 +94,4 @@ sequenceDiagram ``` -### `public void execute()` - -Executes the command. - -### `public String getValue()` - -Returns a value based on the executed command. - -#### Returns - -|Type|Description| -|---|---| -|`String`|The value| - -### `public virtual String overridableMethod()` - -*Inherited* - --- diff --git a/docs/types/Main/nspc.SampleClass.md b/docs/types/Main/nspc.SampleClass.md index 3eca85df..0973af9a 100644 --- a/docs/types/Main/nspc.SampleClass.md +++ b/docs/types/Main/nspc.SampleClass.md @@ -62,23 +62,23 @@ Constructs a SampleClass with an argument. ## Fields ### Common Constants -* `public ANOTHER_CONSTANT` → `String` * `public A_CONSTANT` → `String` [`NAMESPACEACCESSIBLE` ] - This is a constant. +* `public ANOTHER_CONSTANT` → `String` * `public listOfStrings` → `List` --- ### 'General' Constants -* `public GENERAL_ANOTHER_CONSTANT` → `String` * `public GENERAL_A_CONSTANT` → `String` [`NAMESPACEACCESSIBLE` ] - This is a constant. ---- -### Other variables - -* `public someVariable` → `String` +* `public GENERAL_ANOTHER_CONSTANT` → `String` --- ### Other * `private somePrivateStuff` → `String` --- +### Other variables + +* `public someVariable` → `String` +--- ## Properties ### `public AnotherProp` → `Decimal` @@ -97,6 +97,13 @@ This is a String property. --- ## Methods ### A method group +##### `public static String anotherSampleMethod(String arg1)` + +Something here + + +**Arg1** The arg1 description + ##### `public static String sampleMethod(String argument1, String argument2)` `NAMESPACEACCESSIBLE` @@ -123,13 +130,6 @@ System.debug(result); ``` -##### `public static String anotherSampleMethod(String arg1)` - -Something here - - -**Arg1** The arg1 description - ##### `public static Map> yetAnotherSampleMethod(String arg1)` --- ### Other diff --git a/examples/force-app/main/default/classes/ChildClass.cls b/examples/force-app/main/default/classes/ChildClass.cls index 6daa36cb..45f5cc0a 100644 --- a/examples/force-app/main/default/classes/ChildClass.cls +++ b/examples/force-app/main/default/classes/ChildClass.cls @@ -4,6 +4,7 @@ */ public class ChildClass extends ParentClass implements SampleInterface { private String privateStringFromChild; + private String aPrivateString; public void doSomething() { System.debug('Do something'); diff --git a/src/cli/generate.ts b/src/cli/generate.ts index faa0a06f..9c0f51e9 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -85,6 +85,10 @@ result.then((config) => { describe: 'If using "openapi" as the target generator, this allows you to specify the name of the output file.', default: 'openapi', }, + sortMembersAlphabetically: { + type: 'boolean', + describe: 'Whether to sort class members alphabetically', + }, includeMetadata: { type: 'boolean', describe: "Whether to include the file's meta.xml information: Whether it is active and and the API version", @@ -114,6 +118,7 @@ result.then((config) => { title: argv.title, namespace: argv.namespace, openApiFileName: argv.openApiFileName, + sortMembersAlphabetically: argv.sortMembersAlphabetically, includeMetadata: argv.includeMetadata, rootDir: argv.documentationRootDir, onAfterProcess: config?.config?.onAfterProcess, diff --git a/src/model/markdown-generation-util/field-declaration-util.ts b/src/model/markdown-generation-util/field-declaration-util.ts index 215cc67d..5e89ca85 100644 --- a/src/model/markdown-generation-util/field-declaration-util.ts +++ b/src/model/markdown-generation-util/field-declaration-util.ts @@ -8,15 +8,9 @@ export function declareField( grouped = false, ) { markdownFile.addBlankLine(); - fields - .sort((propA, propB) => { - if (propA.name < propB.name) return -1; - if (propA.name > propB.name) return 1; - return 0; - }) - .forEach((propertyModel) => { - addFieldSection(markdownFile, propertyModel, startingHeadingLevel, grouped); - }); + fields.forEach((propertyModel) => { + addFieldSection(markdownFile, propertyModel, startingHeadingLevel, grouped); + }); markdownFile.addHorizontalRule(); } diff --git a/src/model/markdown-type-file.ts b/src/model/markdown-type-file.ts index c62398ca..f112fe06 100644 --- a/src/model/markdown-type-file.ts +++ b/src/model/markdown-type-file.ts @@ -36,6 +36,7 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener { walker.walk(this); } + // TODO: Sort types public onTypeDeclaration(typeMirror: Type): void { let fullTypeName; if (this.isInner) { @@ -62,29 +63,35 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener { this.declareMethodWithGroupings(constructors, className); } + // TODO: Sort fields public onFieldsDeclaration(fields: FieldMirrorWithInheritance[]): void { this.addTitle('Fields', this.headingLevel + 1); this.declareFieldOrProperty(fields); } + //TODO: Sort properties public onPropertiesDeclaration(properties: PropertyMirrorWithInheritance[]): void { this.addTitle('Properties', this.headingLevel + 1); this.declareFieldOrProperty(properties); } + // TODO: Sort methods public onMethodsDeclaration(methods: MethodMirror[]): void { this.addTitle('Methods', this.headingLevel + 1); this.declareMethodWithGroupings(methods); } + // TODO: Sort this stuff public onInnerEnumsDeclaration(enums: EnumMirror[]): void { this.addInnerTypes('Enums', enums); } + // TODO: Sort this stuff as well public onInnerClassesDeclaration(classes: ClassMirror[]): void { this.addInnerTypes('Classes', classes); } + // TODO: Sort this stuff public onInnerInterfacesDeclaration(interfaces: InterfaceMirror[]): void { this.addInnerTypes('Interfaces', interfaces, false); } diff --git a/src/service/walkers/class-walker.ts b/src/service/walkers/class-walker.ts index c4e31236..2cf4fbb8 100644 --- a/src/service/walkers/class-walker.ts +++ b/src/service/walkers/class-walker.ts @@ -1,31 +1,39 @@ import { Walker, WalkerListener } from './walker'; import { ClassMirror } from '@cparra/apex-reflection'; +import { Settings } from '../../settings'; export class ClassWalker extends Walker { walk(listener: WalkerListener): void { listener.onTypeDeclaration(this.type); const classMirror = this.type as ClassMirror; - + const shouldSortMembers = Settings.getInstance().sortMembersAlphabetically(); if (classMirror.constructors.length) { listener.onConstructorDeclaration(this.type.name, classMirror.constructors); } if (classMirror.fields.length) { - listener.onFieldsDeclaration(classMirror.fields); + listener.onFieldsDeclaration(this.sortType(shouldSortMembers, classMirror.fields)); } if (classMirror.properties.length) { - listener.onPropertiesDeclaration(classMirror.properties); + listener.onPropertiesDeclaration(this.sortType(shouldSortMembers, classMirror.properties)); } if (classMirror.methods.length) { - listener.onMethodsDeclaration(classMirror.methods); + listener.onMethodsDeclaration(this.sortType(shouldSortMembers, classMirror.methods)); } if (classMirror.enums.length) { - listener.onInnerEnumsDeclaration(classMirror.enums); + listener.onInnerEnumsDeclaration(this.sortType(shouldSortMembers, classMirror.enums)); } if (classMirror.classes.length) { - listener.onInnerClassesDeclaration(classMirror.classes); + listener.onInnerClassesDeclaration(this.sortType(shouldSortMembers, classMirror.classes)); } if (classMirror.interfaces.length) { - listener.onInnerInterfacesDeclaration(classMirror.interfaces); + listener.onInnerInterfacesDeclaration(this.sortType(shouldSortMembers, classMirror.interfaces)); + } + } + + private sortType(shouldSort: boolean, types: T[]): T[] { + if (shouldSort) { + return types.sort((a, b) => a.name.localeCompare(b.name)); } + return types; } } diff --git a/src/service/walkers/enum-walker.ts b/src/service/walkers/enum-walker.ts index 7230fe16..6466c587 100644 --- a/src/service/walkers/enum-walker.ts +++ b/src/service/walkers/enum-walker.ts @@ -1,8 +1,8 @@ import { Walker, WalkerListener } from './walker'; -import { EnumMirror } from '@cparra/apex-reflection'; export class EnumWalker extends Walker { walk(listener: WalkerListener): void { + // TODO: Sort here as well listener.onTypeDeclaration(this.type); } } diff --git a/src/service/walkers/interface-walker.ts b/src/service/walkers/interface-walker.ts index 2175bd3c..11af4d8b 100644 --- a/src/service/walkers/interface-walker.ts +++ b/src/service/walkers/interface-walker.ts @@ -2,6 +2,7 @@ import { Walker, WalkerListener } from './walker'; import { InterfaceMirror } from '@cparra/apex-reflection'; export class InterfaceWalker extends Walker { + // TODO: Sort here as well walk(listener: WalkerListener): void { listener.onTypeDeclaration(this.type); const interfaceMirror = this.type as InterfaceMirror; diff --git a/src/settings.ts b/src/settings.ts index 106d0d72..c74074db 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -36,6 +36,7 @@ export interface SettingsConfig { openApiFileName: string; includeMetadata: boolean; rootDir?: string; + sortMembersAlphabetically?: boolean; onAfterProcess?: (files: TargetFile[]) => void; onBeforeFileWrite?: (file: TargetFile) => TargetFile; frontMatterHeader?: (file: TargetType) => string[]; @@ -116,6 +117,10 @@ export class Settings { return this.config.includeMetadata; } + public sortMembersAlphabetically(): boolean { + return this.config.sortMembersAlphabetically ?? false; + } + public getRootDir(): string | undefined { return this.config.rootDir; } diff --git a/src/transpiler/markdown/class-file-generatorHelper.ts b/src/transpiler/markdown/class-file-generatorHelper.ts index bf20bfbf..5a91bb6c 100644 --- a/src/transpiler/markdown/class-file-generatorHelper.ts +++ b/src/transpiler/markdown/class-file-generatorHelper.ts @@ -40,11 +40,11 @@ export default class ClassFileGeneratorHelper { // If the types the same groups then we simply link directly to that file return './'; } else { - // If the types have different groups then we have to go up a directory + // If the types have different groups, then we have to go up a directory return `../${this.getSanitizedGroup(classModel)}/`; } } else { - // If nothing is being processed then we assume we are at the root and links should include the groups + // If nothing is being processed, then we assume we are at the root and links should include the groups return `./${this.getSanitizedGroup(classModel)}/`; } } From 2984d79f7661ba23163c3da87f0b3a82b3c23317 Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 14:19:26 -0400 Subject: [PATCH 04/10] Removing sort when the flag is not enabled. --- src/model/markdown-type-file.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/model/markdown-type-file.ts b/src/model/markdown-type-file.ts index f112fe06..28d10d00 100644 --- a/src/model/markdown-type-file.ts +++ b/src/model/markdown-type-file.ts @@ -36,7 +36,6 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener { walker.walk(this); } - // TODO: Sort types public onTypeDeclaration(typeMirror: Type): void { let fullTypeName; if (this.isInner) { @@ -63,13 +62,11 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener { this.declareMethodWithGroupings(constructors, className); } - // TODO: Sort fields public onFieldsDeclaration(fields: FieldMirrorWithInheritance[]): void { this.addTitle('Fields', this.headingLevel + 1); this.declareFieldOrProperty(fields); } - //TODO: Sort properties public onPropertiesDeclaration(properties: PropertyMirrorWithInheritance[]): void { this.addTitle('Properties', this.headingLevel + 1); this.declareFieldOrProperty(properties); @@ -81,33 +78,24 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener { this.declareMethodWithGroupings(methods); } - // TODO: Sort this stuff public onInnerEnumsDeclaration(enums: EnumMirror[]): void { this.addInnerTypes('Enums', enums); } - // TODO: Sort this stuff as well public onInnerClassesDeclaration(classes: ClassMirror[]): void { this.addInnerTypes('Classes', classes); } - // TODO: Sort this stuff public onInnerInterfacesDeclaration(interfaces: InterfaceMirror[]): void { this.addInnerTypes('Interfaces', interfaces, false); } private addInnerTypes(title: string, types: Type[], addSeparator = true) { this.addTitle(title, this.headingLevel + 1); - types - .sort((typeA, typeB) => { - if (typeA.name < typeB.name) return -1; - if (typeA.name > typeB.name) return 1; - return 0; - }) - .forEach((currentType) => { - const innerFile = new MarkdownTypeFile(currentType, this.headingLevel + 2, undefined, true); - this.addText(innerFile._contents); - }); + types.forEach((currentType) => { + const innerFile = new MarkdownTypeFile(currentType, this.headingLevel + 2, undefined, true); + this.addText(innerFile._contents); + }); if (addSeparator) { this.addHorizontalRule(); } From 25ac16d8da2131a7f0ce4bc13a3ce4ef419e5a1c Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 14:22:34 -0400 Subject: [PATCH 05/10] Updating documentation. --- README.md | 33 +++++++++++++++++---------------- src/cli/generate.ts | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index adb36e5b..466a4749 100644 --- a/README.md +++ b/README.md @@ -102,22 +102,23 @@ apexdocs-generate The CLI supports the following parameters: -| Parameter | Alias | Description | Default | Required | -|------------------------|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|----------| -| --sourceDir | -s | The directory location which contains your apex .cls classes. | N/A | Yes | -| --targetDir | -t | The directory location where documentation will be generated to. | `docs` | No | -| --recursive | -r | Whether .cls classes will be searched for recursively in the directory provided. | `true` | No | -| --scope | -p | A list of scopes to document. Values should be separated by a space, e.g --scope public private. Note that this setting is ignored if generating an OpenApi REST specification since that looks for classes annotated with @RestResource. | `global` | No | -| --targetGenerator | -g | Define the static file generator for which the documents will be created. Currently supports: `jekyll`, `docsify`, `plain-markdown`, and `openapi`. | `jekyll` | No | -| --indexOnly | N/A | Defines whether only the index file should be generated. | `false` | No | -| --defaultGroupName | N/A | Defines the `@group` name to be used when a file does not specify it. | `Miscellaneous` | No | -| --sanitizeHtml | N/A | When on, any special character within your ApexDocs is converted into its HTML code representation. This is specially useful when generic objects are described within the docs, e.g. "List< Foo>", "Map" because otherwise the content within < and > would be treated as HTML tags and not shown in the output. Content in @example blocks are never sanitized. | `Apex REST Api` | No | -| --openApiTitle | N/A | If using "openapi" as the target generator, this allows you to specify the OpenApi title value. | true | No | -| --title | N/A | Allows you to specify the home page main title. If using "openapi" this acts as an alias to the openApiTitle parameter | `Classes` | No | -| --namespace | N/A | The package namespace, if any. If this value is provided the namespace will be added as a prefix to all of the parsed files. If generating an OpenApi definition, it will be added to the file's Server Url. | N/A | No | -| --openApiFileName | N/A | If using "openapi" as the target generator, this allows you to specify the name of the output file. | `openapi` | No | -| --includeMetadata | N/A | Whether to include the file's meta.xml information: Whether it is active and and the API version | false | No | -| --documentationRootDir | N/A | The root directory where the documentation will be generated. This is useful when you want to generate the documentation in a subdirectory of your project. | N/A | No | +| Parameter | Alias | Description | Default | Required | +|-----------------------------|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|----------| +| --sourceDir | -s | The directory location which contains your apex .cls classes. | N/A | Yes | +| --targetDir | -t | The directory location where documentation will be generated to. | `docs` | No | +| --recursive | -r | Whether .cls classes will be searched for recursively in the directory provided. | `true` | No | +| --scope | -p | A list of scopes to document. Values should be separated by a space, e.g --scope public private. Note that this setting is ignored if generating an OpenApi REST specification since that looks for classes annotated with @RestResource. | `global` | No | +| --targetGenerator | -g | Define the static file generator for which the documents will be created. Currently supports: `jekyll`, `docsify`, `plain-markdown`, and `openapi`. | `jekyll` | No | +| --indexOnly | N/A | Defines whether only the index file should be generated. | `false` | No | +| --defaultGroupName | N/A | Defines the `@group` name to be used when a file does not specify it. | `Miscellaneous` | No | +| --sanitizeHtml | N/A | When on, any special character within your ApexDocs is converted into its HTML code representation. This is specially useful when generic objects are described within the docs, e.g. "List< Foo>", "Map" because otherwise the content within < and > would be treated as HTML tags and not shown in the output. Content in @example blocks are never sanitized. | `Apex REST Api` | No | +| --openApiTitle | N/A | If using "openapi" as the target generator, this allows you to specify the OpenApi title value. | true | No | +| --title | N/A | Allows you to specify the home page main title. If using "openapi" this acts as an alias to the openApiTitle parameter | `Classes` | No | +| --namespace | N/A | The package namespace, if any. If this value is provided the namespace will be added as a prefix to all of the parsed files. If generating an OpenApi definition, it will be added to the file's Server Url. | N/A | No | +| --openApiFileName | N/A | If using "openapi" as the target generator, this allows you to specify the name of the output file. | `openapi` | No | +| --sortMembersAlphabetically | N/A | Whether to sort the members of a class alphabetically. | `false` | No | + | --includeMetadata | N/A | Whether to include the file's meta.xml information: Whether it is active and and the API version | false | No | + | --documentationRootDir | N/A | The root directory where the documentation will be generated. This is useful when you want to generate the documentation in a subdirectory of your project. | N/A | No | ### Using a configuration file diff --git a/src/cli/generate.ts b/src/cli/generate.ts index 9c0f51e9..cffb802f 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -87,7 +87,7 @@ result.then((config) => { }, sortMembersAlphabetically: { type: 'boolean', - describe: 'Whether to sort class members alphabetically', + describe: 'Whether to sort members alphabetically.', }, includeMetadata: { type: 'boolean', From 0e73160328e7ea00ac57c324c7c03c413e664e68 Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 14:24:26 -0400 Subject: [PATCH 06/10] Cleaning up TODOs --- src/model/markdown-type-file.ts | 1 - src/service/walkers/walker.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/model/markdown-type-file.ts b/src/model/markdown-type-file.ts index 28d10d00..516e87e5 100644 --- a/src/model/markdown-type-file.ts +++ b/src/model/markdown-type-file.ts @@ -72,7 +72,6 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener { this.declareFieldOrProperty(properties); } - // TODO: Sort methods public onMethodsDeclaration(methods: MethodMirror[]): void { this.addTitle('Methods', this.headingLevel + 1); this.declareMethodWithGroupings(methods); diff --git a/src/service/walkers/walker.ts b/src/service/walkers/walker.ts index 7c613adb..2659c0c4 100644 --- a/src/service/walkers/walker.ts +++ b/src/service/walkers/walker.ts @@ -8,7 +8,6 @@ import { PropertyMirror, Type, } from '@cparra/apex-reflection'; -import { Annotation } from '@cparra/apex-reflection/index'; export interface WalkerListener { onTypeDeclaration(typeMirror: Type): void; From 22c2c819a72bdef480f0baa366cff9fc7c444fa6 Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 14:27:02 -0400 Subject: [PATCH 07/10] Cleaning up --- src/service/walkers/class-walker.ts | 21 ++++++--------------- src/service/walkers/enum-walker.ts | 1 - src/service/walkers/walker.ts | 8 ++++++++ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/service/walkers/class-walker.ts b/src/service/walkers/class-walker.ts index 2cf4fbb8..f2b3ddc2 100644 --- a/src/service/walkers/class-walker.ts +++ b/src/service/walkers/class-walker.ts @@ -1,39 +1,30 @@ import { Walker, WalkerListener } from './walker'; import { ClassMirror } from '@cparra/apex-reflection'; -import { Settings } from '../../settings'; export class ClassWalker extends Walker { walk(listener: WalkerListener): void { listener.onTypeDeclaration(this.type); const classMirror = this.type as ClassMirror; - const shouldSortMembers = Settings.getInstance().sortMembersAlphabetically(); if (classMirror.constructors.length) { listener.onConstructorDeclaration(this.type.name, classMirror.constructors); } if (classMirror.fields.length) { - listener.onFieldsDeclaration(this.sortType(shouldSortMembers, classMirror.fields)); + listener.onFieldsDeclaration(this.sortType(classMirror.fields)); } if (classMirror.properties.length) { - listener.onPropertiesDeclaration(this.sortType(shouldSortMembers, classMirror.properties)); + listener.onPropertiesDeclaration(this.sortType(classMirror.properties)); } if (classMirror.methods.length) { - listener.onMethodsDeclaration(this.sortType(shouldSortMembers, classMirror.methods)); + listener.onMethodsDeclaration(this.sortType(classMirror.methods)); } if (classMirror.enums.length) { - listener.onInnerEnumsDeclaration(this.sortType(shouldSortMembers, classMirror.enums)); + listener.onInnerEnumsDeclaration(this.sortType(classMirror.enums)); } if (classMirror.classes.length) { - listener.onInnerClassesDeclaration(this.sortType(shouldSortMembers, classMirror.classes)); + listener.onInnerClassesDeclaration(this.sortType(classMirror.classes)); } if (classMirror.interfaces.length) { - listener.onInnerInterfacesDeclaration(this.sortType(shouldSortMembers, classMirror.interfaces)); + listener.onInnerInterfacesDeclaration(this.sortType(classMirror.interfaces)); } } - - private sortType(shouldSort: boolean, types: T[]): T[] { - if (shouldSort) { - return types.sort((a, b) => a.name.localeCompare(b.name)); - } - return types; - } } diff --git a/src/service/walkers/enum-walker.ts b/src/service/walkers/enum-walker.ts index 6466c587..b31dd0f7 100644 --- a/src/service/walkers/enum-walker.ts +++ b/src/service/walkers/enum-walker.ts @@ -2,7 +2,6 @@ import { Walker, WalkerListener } from './walker'; export class EnumWalker extends Walker { walk(listener: WalkerListener): void { - // TODO: Sort here as well listener.onTypeDeclaration(this.type); } } diff --git a/src/service/walkers/walker.ts b/src/service/walkers/walker.ts index 2659c0c4..f19fcae1 100644 --- a/src/service/walkers/walker.ts +++ b/src/service/walkers/walker.ts @@ -8,6 +8,7 @@ import { PropertyMirror, Type, } from '@cparra/apex-reflection'; +import { Settings } from '../../settings'; export interface WalkerListener { onTypeDeclaration(typeMirror: Type): void; @@ -31,4 +32,11 @@ export abstract class Walker { constructor(public type: Type) {} abstract walk(listener: WalkerListener): void; + + protected sortType(types: T[]): T[] { + if (Settings.getInstance().sortMembersAlphabetically()) { + return types.sort((a, b) => a.name.localeCompare(b.name)); + } + return types; + } } From a0242b845aeec0b79763d8a1baeb55d3b61c8171 Mon Sep 17 00:00:00 2001 From: Cesar Parra Date: Mon, 15 Apr 2024 14:27:32 -0400 Subject: [PATCH 08/10] Sorting interface methods as well. --- src/service/walkers/interface-walker.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/service/walkers/interface-walker.ts b/src/service/walkers/interface-walker.ts index 11af4d8b..dd2beead 100644 --- a/src/service/walkers/interface-walker.ts +++ b/src/service/walkers/interface-walker.ts @@ -2,12 +2,11 @@ import { Walker, WalkerListener } from './walker'; import { InterfaceMirror } from '@cparra/apex-reflection'; export class InterfaceWalker extends Walker { - // TODO: Sort here as well walk(listener: WalkerListener): void { listener.onTypeDeclaration(this.type); const interfaceMirror = this.type as InterfaceMirror; if (interfaceMirror.methods.length) { - listener.onMethodsDeclaration(interfaceMirror.methods); + listener.onMethodsDeclaration(this.sortType(interfaceMirror.methods)); } } } From 623990d115b1474fc63e8e103985a03735c3d4d0 Mon Sep 17 00:00:00 2001 From: cesarParra Date: Thu, 18 Apr 2024 06:38:06 -0400 Subject: [PATCH 09/10] Fixing issue where configuration file configs are not respected when argv is configured with a default. --- src/cli/generate.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cli/generate.ts b/src/cli/generate.ts index cffb802f..98d79ef7 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -8,6 +8,7 @@ import { cosmiconfig } from 'cosmiconfig'; const result = cosmiconfig('apexdocs').search(); result.then((config) => { + yargs.config(config?.config); let argv = yargs.options({ sourceDir: { type: 'string', @@ -88,6 +89,7 @@ result.then((config) => { sortMembersAlphabetically: { type: 'boolean', describe: 'Whether to sort members alphabetically.', + default: false, }, includeMetadata: { type: 'boolean', From 52ce13c1533889f710b9f642126e7103bd202bab Mon Sep 17 00:00:00 2001 From: cesarParra Date: Fri, 19 Apr 2024 10:14:26 -0400 Subject: [PATCH 10/10] Publishing version 2.23.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 57684f20..657c002b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cparra/apexdocs", - "version": "2.22.0", + "version": "2.23.0", "description": "Library with CLI capabilities to generate documentation for Salesforce Apex classes.", "keywords": [ "apex", @@ -18,7 +18,7 @@ "scripts": { "test": "jest --coverage", "build": "rimraf ./lib && npm run lint && tsc --declaration", - "lint": "eslint ./src/**/*.{js,ts} --quiet --fix", + "lint": "eslint './src/**/*.{js,ts}' --quiet --fix", "prepare": "npm run build", "version": "npm run format && git add -A src", "postversion": "git push && git push --tags",