@cparra/apexdocs 2.25.0-alpha.0 → 2.25.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/__mocks__/chalk.js +12 -0
  2. package/__mocks__/log-update.js +6 -0
  3. package/eslint.config.mjs +10 -0
  4. package/examples/plain-markdown/docs/Miscellaneous/ns.BaseClass.md +16 -0
  5. package/examples/plain-markdown/docs/Miscellaneous/ns.MultiInheritanceClass.md +73 -0
  6. package/examples/plain-markdown/docs/Miscellaneous/ns.ParentInterface.md +15 -0
  7. package/examples/plain-markdown/docs/Miscellaneous/ns.ReferencedEnum.md +8 -0
  8. package/examples/plain-markdown/docs/Miscellaneous/ns.SampleException.md +7 -0
  9. package/examples/plain-markdown/docs/Miscellaneous/ns.SampleInterface.md +115 -0
  10. package/examples/plain-markdown/docs/Sample-Enums/ns.SampleEnum.md +36 -0
  11. package/examples/plain-markdown/docs/SampleGroup/ns.SampleClass.md +173 -0
  12. package/examples/plain-markdown/docs/index.md +33 -0
  13. package/examples/plain-markdown/force-app/classes/MultiInheritanceClass.cls +1 -0
  14. package/examples/plain-markdown/force-app/classes/SampleClass.cls +37 -0
  15. package/examples/plain-markdown/package.json +2 -6
  16. package/jest.config.js +4 -0
  17. package/jest.d.ts +7 -0
  18. package/lib/__spec__/core/expect-extensions.d.ts +3 -0
  19. package/lib/__spec__/core/expect-extensions.js +54 -0
  20. package/lib/__spec__/core/expect-extensions.js.map +1 -0
  21. package/lib/__spec__/core/generating-class-docs.spec.d.ts +1 -0
  22. package/lib/__spec__/core/generating-class-docs.spec.js +427 -0
  23. package/lib/__spec__/core/generating-class-docs.spec.js.map +1 -0
  24. package/lib/__spec__/core/generating-enum-docs.spec.d.ts +1 -0
  25. package/lib/__spec__/core/generating-enum-docs.spec.js +303 -0
  26. package/lib/__spec__/core/generating-enum-docs.spec.js.map +1 -0
  27. package/lib/__spec__/core/generating-interface-docs.spec.d.ts +1 -0
  28. package/lib/__spec__/core/generating-interface-docs.spec.js +361 -0
  29. package/lib/__spec__/core/generating-interface-docs.spec.js.map +1 -0
  30. package/lib/__spec__/core/generating-reference-guide.spec.d.ts +1 -0
  31. package/lib/__spec__/core/generating-reference-guide.spec.js +161 -0
  32. package/lib/__spec__/core/generating-reference-guide.spec.js.map +1 -0
  33. package/lib/adapters/apex-types.d.ts +5 -5
  34. package/lib/adapters/apex-types.js +97 -22
  35. package/lib/adapters/apex-types.js.map +1 -1
  36. package/lib/adapters/documentables.d.ts +4 -3
  37. package/lib/adapters/documentables.js +23 -8
  38. package/lib/adapters/documentables.js.map +1 -1
  39. package/lib/adapters/fields-and-properties.d.ts +4 -3
  40. package/lib/adapters/fields-and-properties.js +26 -5
  41. package/lib/adapters/fields-and-properties.js.map +1 -1
  42. package/lib/adapters/methods-and-constructors.d.ts +4 -3
  43. package/lib/adapters/methods-and-constructors.js +54 -12
  44. package/lib/adapters/methods-and-constructors.js.map +1 -1
  45. package/lib/adapters/references.d.ts +1 -1
  46. package/lib/adapters/references.js +3 -3
  47. package/lib/adapters/references.js.map +1 -1
  48. package/lib/adapters/type-utils.d.ts +1 -1
  49. package/lib/adapters/type-utils.js +1 -2
  50. package/lib/adapters/type-utils.js.map +1 -1
  51. package/lib/application/Apexdocs.js +21 -15
  52. package/lib/application/Apexdocs.js.map +1 -1
  53. package/lib/application/flows/generate-markdown-files.d.ts +3 -0
  54. package/lib/application/flows/generate-markdown-files.js +57 -0
  55. package/lib/application/flows/generate-markdown-files.js.map +1 -0
  56. package/lib/cli/generate.js +7 -7
  57. package/lib/cli/generate.js.map +1 -1
  58. package/lib/core/__test__/inheritance-chain.test.d.ts +1 -0
  59. package/lib/core/__test__/inheritance-chain.test.js +42 -0
  60. package/lib/core/__test__/inheritance-chain.test.js.map +1 -0
  61. package/lib/core/generate-docs.d.ts +24 -0
  62. package/lib/core/generate-docs.js +267 -0
  63. package/lib/core/generate-docs.js.map +1 -0
  64. package/lib/core/inheritance-chain.d.ts +2 -0
  65. package/lib/core/inheritance-chain.js +35 -0
  66. package/lib/core/inheritance-chain.js.map +1 -0
  67. package/lib/core/template.d.ts +10 -0
  68. package/lib/core/template.js +92 -0
  69. package/lib/core/template.js.map +1 -0
  70. package/lib/core/templates/reference-guide.d.ts +1 -0
  71. package/lib/core/templates/reference-guide.js +18 -0
  72. package/lib/core/templates/reference-guide.js.map +1 -0
  73. package/lib/index.d.ts +2 -1
  74. package/lib/index.js +14 -3
  75. package/lib/index.js.map +1 -1
  76. package/lib/model/inheritance.d.ts +4 -1
  77. package/lib/model/manifest.js +8 -2
  78. package/lib/model/manifest.js.map +1 -1
  79. package/lib/model/markdown-file.d.ts +2 -2
  80. package/lib/model/markdown-file.js +5 -4
  81. package/lib/model/markdown-file.js.map +1 -1
  82. package/lib/model/markdown-generation-util/doc-comment-annotation-util.js +3 -4
  83. package/lib/model/markdown-generation-util/doc-comment-annotation-util.js.map +1 -1
  84. package/lib/model/markdown-generation-util/field-declaration-util.js +1 -2
  85. package/lib/model/markdown-generation-util/field-declaration-util.js.map +1 -1
  86. package/lib/model/markdown-generation-util/method-declaration-util.js +2 -3
  87. package/lib/model/markdown-generation-util/method-declaration-util.js.map +1 -1
  88. package/lib/model/markdown-generation-util/type-declaration-util.js +1 -2
  89. package/lib/model/markdown-generation-util/type-declaration-util.js.map +1 -1
  90. package/lib/model/markdown-home-file.js.map +1 -1
  91. package/lib/model/markdown-type-file.js.map +1 -1
  92. package/lib/model/openapi/open-api.js.map +1 -1
  93. package/lib/model/outputFile.d.ts +1 -1
  94. package/lib/model/outputFile.js +2 -9
  95. package/lib/model/outputFile.js.map +1 -1
  96. package/lib/model/types-repository.js.map +1 -1
  97. package/lib/service/apex-file-reader.js.map +1 -1
  98. package/lib/service/file-writer.js.map +1 -1
  99. package/lib/service/manifest-factory.js +1 -2
  100. package/lib/service/manifest-factory.js.map +1 -1
  101. package/lib/service/metadata-processor.js.map +1 -1
  102. package/lib/service/parser.js +1 -1
  103. package/lib/service/parser.js.map +1 -1
  104. package/lib/service/state.js.map +1 -1
  105. package/lib/service/walkers/class-walker.js.map +1 -1
  106. package/lib/service/walkers/interface-walker.js.map +1 -1
  107. package/lib/service/walkers/walker-factory.js.map +1 -1
  108. package/lib/service/walkers/walker.js.map +1 -1
  109. package/lib/settings.d.ts +0 -6
  110. package/lib/settings.js +0 -11
  111. package/lib/settings.js.map +1 -1
  112. package/lib/test-helpers/ClassMirrorBuilder.d.ts +4 -0
  113. package/lib/test-helpers/ClassMirrorBuilder.js +11 -1
  114. package/lib/test-helpers/ClassMirrorBuilder.js.map +1 -1
  115. package/lib/transpiler/factory.js.map +1 -1
  116. package/lib/transpiler/markdown/class-file-generatorHelper.d.ts +1 -1
  117. package/lib/transpiler/markdown/class-file-generatorHelper.js.map +1 -1
  118. package/lib/transpiler/markdown/jekyll/jekyll-docsProcessor.js.map +1 -1
  119. package/lib/transpiler/markdown/plain-markdown/class-template.js +53 -12
  120. package/lib/transpiler/markdown/plain-markdown/class-template.js.map +1 -1
  121. package/lib/transpiler/markdown/plain-markdown/constructors-partial-template.js +13 -16
  122. package/lib/transpiler/markdown/plain-markdown/constructors-partial-template.js.map +1 -1
  123. package/lib/transpiler/markdown/plain-markdown/documentable-partial-template.js +11 -8
  124. package/lib/transpiler/markdown/plain-markdown/documentable-partial-template.js.map +1 -1
  125. package/lib/transpiler/markdown/plain-markdown/enum-template.js +7 -6
  126. package/lib/transpiler/markdown/plain-markdown/enum-template.js.map +1 -1
  127. package/lib/transpiler/markdown/plain-markdown/fieldsPartialTemplate.js +9 -6
  128. package/lib/transpiler/markdown/plain-markdown/fieldsPartialTemplate.js.map +1 -1
  129. package/lib/transpiler/markdown/plain-markdown/grouped-members-partial-template.d.ts +1 -0
  130. package/lib/transpiler/markdown/plain-markdown/grouped-members-partial-template.js +10 -0
  131. package/lib/transpiler/markdown/plain-markdown/grouped-members-partial-template.js.map +1 -0
  132. package/lib/transpiler/markdown/plain-markdown/interface-template.js +4 -4
  133. package/lib/transpiler/markdown/plain-markdown/methods-partial-template.js +18 -20
  134. package/lib/transpiler/markdown/plain-markdown/methods-partial-template.js.map +1 -1
  135. package/lib/transpiler/markdown/plain-markdown/plain-docsProcessor.d.ts +0 -4
  136. package/lib/transpiler/markdown/plain-markdown/plain-docsProcessor.js +6 -86
  137. package/lib/transpiler/markdown/plain-markdown/plain-docsProcessor.js.map +1 -1
  138. package/lib/transpiler/markdown/plain-markdown/type-doc-partial.d.ts +1 -0
  139. package/lib/transpiler/markdown/plain-markdown/type-doc-partial.js +31 -0
  140. package/lib/transpiler/markdown/plain-markdown/type-doc-partial.js.map +1 -0
  141. package/lib/transpiler/openapi/open-api-docs-processor.js.map +1 -1
  142. package/lib/transpiler/openapi/parsers/Builder.js.map +1 -1
  143. package/lib/transpiler/openapi/parsers/MethodParser.js.map +1 -1
  144. package/lib/transpiler/openapi/parsers/ReferenceBuilder.js +3 -2
  145. package/lib/transpiler/openapi/parsers/ReferenceBuilder.js.map +1 -1
  146. package/lib/transpiler/openapi/parsers/ResponsesBuilder.js.map +1 -1
  147. package/lib/transpiler/transpiler.js.map +1 -1
  148. package/lib/util/error-logger.js.map +1 -1
  149. package/lib/util/logger.js +1 -1
  150. package/lib/util/logger.js.map +1 -1
  151. package/lib/util/string-utils.js +2 -2
  152. package/lib/util/string-utils.js.map +1 -1
  153. package/package.json +17 -17
  154. package/src/__spec__/core/expect-extensions.ts +32 -0
  155. package/src/__spec__/core/generating-class-docs.spec.ts +511 -0
  156. package/src/__spec__/core/generating-enum-docs.spec.ts +355 -0
  157. package/src/__spec__/core/generating-interface-docs.spec.ts +431 -0
  158. package/src/__spec__/core/generating-reference-guide.spec.ts +163 -0
  159. package/src/adapters/__tests__/interface-adapter.spec.ts +15 -11
  160. package/src/adapters/__tests__/references.spec.ts +1 -1
  161. package/src/adapters/apex-types.ts +205 -41
  162. package/src/adapters/documentables.ts +44 -9
  163. package/src/adapters/fields-and-properties.ts +31 -7
  164. package/src/adapters/methods-and-constructors.ts +65 -22
  165. package/src/adapters/references.ts +1 -1
  166. package/src/adapters/type-utils.ts +1 -1
  167. package/src/application/Apexdocs.ts +21 -15
  168. package/src/application/flows/generate-markdown-files.ts +47 -0
  169. package/src/cli/generate.ts +96 -96
  170. package/src/core/__test__/inheritance-chain.test.ts +54 -0
  171. package/src/core/generate-docs.ts +396 -0
  172. package/src/core/inheritance-chain.ts +23 -0
  173. package/src/core/renderable/types.d.ts +131 -0
  174. package/src/core/template.ts +108 -0
  175. package/src/core/templates/reference-guide.ts +14 -0
  176. package/src/index.ts +3 -1
  177. package/src/model/inheritance.ts +2 -1
  178. package/src/model/manifest.ts +12 -2
  179. package/src/model/markdown-file.ts +5 -4
  180. package/src/model/markdown-generation-util/doc-comment-annotation-util.ts +1 -1
  181. package/src/model/markdown-generation-util/method-declaration-util.ts +1 -1
  182. package/src/model/outputFile.ts +2 -11
  183. package/src/service/parser.ts +1 -1
  184. package/src/settings.ts +0 -15
  185. package/src/test-helpers/ClassMirrorBuilder.ts +14 -1
  186. package/src/transpiler/markdown/class-file-generatorHelper.ts +1 -1
  187. package/src/transpiler/markdown/plain-markdown/class-template.ts +53 -12
  188. package/src/transpiler/markdown/plain-markdown/constructors-partial-template.ts +13 -16
  189. package/src/transpiler/markdown/plain-markdown/documentable-partial-template.ts +11 -8
  190. package/src/transpiler/markdown/plain-markdown/enum-template.ts +7 -6
  191. package/src/transpiler/markdown/plain-markdown/fieldsPartialTemplate.ts +9 -6
  192. package/src/transpiler/markdown/plain-markdown/grouped-members-partial-template.ts +6 -0
  193. package/src/transpiler/markdown/plain-markdown/interface-template.ts +4 -4
  194. package/src/transpiler/markdown/plain-markdown/methods-partial-template.ts +18 -20
  195. package/src/transpiler/markdown/plain-markdown/plain-docsProcessor.ts +8 -100
  196. package/src/transpiler/markdown/plain-markdown/type-doc-partial.ts +27 -0
  197. package/src/transpiler/openapi/parsers/ReferenceBuilder.ts +3 -2
  198. package/src/util/logger.ts +1 -1
  199. package/tsconfig.json +1 -1
  200. package/.eslintrc.js +0 -12
  201. package/examples/plain-markdown/README.md +0 -301
  202. package/lib/templating/compile.d.ts +0 -7
  203. package/lib/templating/compile.js +0 -98
  204. package/lib/templating/compile.js.map +0 -1
  205. package/lib/templating/helpers.d.ts +0 -2
  206. package/lib/templating/helpers.js +0 -18
  207. package/lib/templating/helpers.js.map +0 -1
  208. package/lib/transpiler/markdown/plain-markdown/type-level-apex-doc-partial-template.d.ts +0 -1
  209. package/lib/transpiler/markdown/plain-markdown/type-level-apex-doc-partial-template.js +0 -31
  210. package/lib/transpiler/markdown/plain-markdown/type-level-apex-doc-partial-template.js.map +0 -1
  211. package/src/templating/__tests__/compile.spec.ts +0 -741
  212. package/src/templating/compile.ts +0 -187
  213. package/src/templating/helpers.ts +0 -14
  214. package/src/templating/types.d.ts +0 -104
  215. package/src/transpiler/markdown/plain-markdown/type-level-apex-doc-partial-template.ts +0 -27
@@ -9,99 +9,102 @@ import { cosmiconfig } from 'cosmiconfig';
9
9
  const result = cosmiconfig('apexdocs').search();
10
10
  result.then((config) => {
11
11
  yargs.config(config?.config);
12
- let argv = yargs.options({
13
- sourceDir: {
14
- type: 'string',
15
- alias: 's',
16
- demandOption: true,
17
- describe: 'The directory location which contains your apex .cls classes.',
18
- },
19
- targetDir: {
20
- type: 'string',
21
- alias: 't',
22
- default: './docs/',
23
- describe: 'The directory location where documentation will be generated to.',
24
- },
25
- recursive: {
26
- type: 'boolean',
27
- alias: 'r',
28
- default: true,
29
- describe: 'Whether .cls classes will be searched for recursively in the directory provided.',
30
- },
31
- scope: {
32
- type: 'array',
33
- alias: 'p',
34
- default: ['global'],
35
- describe:
36
- 'A list of scopes to document. Values should be separated by a space, e.g --scope global public namespaceaccessible. ' +
37
- 'Annotations are supported and should be passed lowercased and without the @ symbol, e.g. namespaceaccessible auraenabled. ' +
38
- 'Note that this setting is ignored if generating an OpenApi REST specification since that looks for classes annotated with @RestResource.',
39
- },
40
- targetGenerator: {
41
- type: 'string',
42
- alias: 'g',
43
- default: 'jekyll',
44
- choices: ['jekyll', 'docsify', 'plain-markdown', 'openapi'],
45
- describe:
46
- 'Define the static file generator for which the documents will be created. ' +
47
- 'Currently supports jekyll, docsify, plain markdown, and OpenAPI v3.1.0.',
48
- },
49
- indexOnly: {
50
- type: 'boolean',
51
- default: false,
52
- describe: 'Defines whether only the index file should be generated.',
53
- },
54
- defaultGroupName: {
55
- type: 'string',
56
- default: 'Miscellaneous',
57
- describe: 'Defines the @group name to be used when a file does not specify it.',
58
- },
59
- sanitizeHtml: {
60
- type: 'boolean',
61
- default: true,
62
- describe:
63
- 'When on, any special character within your ApexDocs is converted into its HTML code representation. ' +
64
- 'This is specially useful when generic objects are described within the docs, e.g. "List< Foo>", "Map<Foo, Bar>" ' +
65
- 'because otherwise the content within < and > would be treated as HTML tags and not shown in the output. ' +
66
- 'Content in @example blocks are never sanitized.',
67
- },
68
- openApiTitle: {
69
- type: 'string',
70
- default: 'Apex REST Api',
71
- describe: 'If using "openapi" as the target generator, this allows you to specify the OpenApi title value.',
72
- },
73
- title: {
74
- type: 'string',
75
- describe: "If this allows you to specify the title of the generated documentation's home file.",
76
- default: 'Classes',
77
- },
78
- namespace: {
79
- type: 'string',
80
- describe:
81
- 'The package namespace, if any. If this value is provided the namespace will be added as a prefix to all of the parsed files. ' +
82
- "If generating an OpenApi definition, it will be added to the file's Server Url.",
83
- },
84
- openApiFileName: {
85
- type: 'string',
86
- describe: 'If using "openapi" as the target generator, this allows you to specify the name of the output file.',
87
- default: 'openapi',
88
- },
89
- sortMembersAlphabetically: {
90
- type: 'boolean',
91
- describe: 'Whether to sort members alphabetically.',
92
- default: false,
93
- },
94
- includeMetadata: {
95
- type: 'boolean',
96
- describe: "Whether to include the file's meta.xml information: Whether it is active and and the API version",
97
- default: false,
98
- },
99
- documentationRootDir: {
100
- type: 'string',
101
- describe:
102
- 'Allows you to specify the root documentation directory where the files are being generated. This can be helpful when embedding the generated docs into an existing site so that the links are generated correctly.',
103
- },
104
- }).argv;
12
+ let argv = yargs
13
+ .options({
14
+ sourceDir: {
15
+ type: 'string',
16
+ alias: 's',
17
+ demandOption: true,
18
+ describe: 'The directory location which contains your apex .cls classes.',
19
+ },
20
+ targetDir: {
21
+ type: 'string',
22
+ alias: 't',
23
+ default: './docs/',
24
+ describe: 'The directory location where documentation will be generated to.',
25
+ },
26
+ recursive: {
27
+ type: 'boolean',
28
+ alias: 'r',
29
+ default: true,
30
+ describe: 'Whether .cls classes will be searched for recursively in the directory provided.',
31
+ },
32
+ scope: {
33
+ type: 'string',
34
+ array: true,
35
+ alias: 'p',
36
+ default: ['global'],
37
+ describe:
38
+ 'A list of scopes to document. Values should be separated by a space, e.g --scope global public namespaceaccessible. ' +
39
+ 'Annotations are supported and should be passed lowercased and without the @ symbol, e.g. namespaceaccessible auraenabled. ' +
40
+ 'Note that this setting is ignored if generating an OpenApi REST specification since that looks for classes annotated with @RestResource.',
41
+ },
42
+ targetGenerator: {
43
+ type: 'string',
44
+ alias: 'g',
45
+ default: 'jekyll',
46
+ choices: ['jekyll', 'docsify', 'plain-markdown', 'openapi'],
47
+ describe:
48
+ 'Define the static file generator for which the documents will be created. ' +
49
+ 'Currently supports jekyll, docsify, plain markdown, and OpenAPI v3.1.0.',
50
+ },
51
+ indexOnly: {
52
+ type: 'boolean',
53
+ default: false,
54
+ describe: 'Defines whether only the index file should be generated.',
55
+ },
56
+ defaultGroupName: {
57
+ type: 'string',
58
+ default: 'Miscellaneous',
59
+ describe: 'Defines the @group name to be used when a file does not specify it.',
60
+ },
61
+ sanitizeHtml: {
62
+ type: 'boolean',
63
+ default: true,
64
+ describe:
65
+ 'When on, any special character within your ApexDocs is converted into its HTML code representation. ' +
66
+ 'This is specially useful when generic objects are described within the docs, e.g. "List< Foo>", "Map<Foo, Bar>" ' +
67
+ 'because otherwise the content within < and > would be treated as HTML tags and not shown in the output. ' +
68
+ 'Content in @example blocks are never sanitized.',
69
+ },
70
+ openApiTitle: {
71
+ type: 'string',
72
+ default: 'Apex REST Api',
73
+ describe: 'If using "openapi" as the target generator, this allows you to specify the OpenApi title value.',
74
+ },
75
+ title: {
76
+ type: 'string',
77
+ describe: "If this allows you to specify the title of the generated documentation's home file.",
78
+ default: 'Classes',
79
+ },
80
+ namespace: {
81
+ type: 'string',
82
+ describe:
83
+ 'The package namespace, if any. If this value is provided the namespace will be added as a prefix to all of the parsed files. ' +
84
+ "If generating an OpenApi definition, it will be added to the file's Server Url.",
85
+ },
86
+ openApiFileName: {
87
+ type: 'string',
88
+ describe: 'If using "openapi" as the target generator, this allows you to specify the name of the output file.',
89
+ default: 'openapi',
90
+ },
91
+ sortMembersAlphabetically: {
92
+ type: 'boolean',
93
+ describe: 'Whether to sort members alphabetically.',
94
+ default: false,
95
+ },
96
+ includeMetadata: {
97
+ type: 'boolean',
98
+ describe: "Whether to include the file's meta.xml information: Whether it is active and and the API version",
99
+ default: false,
100
+ },
101
+ documentationRootDir: {
102
+ type: 'string',
103
+ describe:
104
+ 'Allows you to specify the root documentation directory where the files are being generated. This can be helpful when embedding the generated docs into an existing site so that the links are generated correctly.',
105
+ },
106
+ })
107
+ .parseSync();
105
108
 
106
109
  if (config) {
107
110
  argv = { ...config.config, ...argv };
@@ -126,9 +129,6 @@ result.then((config) => {
126
129
  onAfterProcess: config?.config?.onAfterProcess,
127
130
  onBeforeFileWrite: config?.config?.onBeforeFileWrite,
128
131
  frontMatterHeader: config?.config?.frontMatterHeader,
129
- singleFile: config?.config?.singleFile,
130
- fileName: config?.config?.fileName,
131
- templateFilePath: config?.config?.templateFilePath,
132
132
  });
133
133
 
134
134
  try {
@@ -0,0 +1,54 @@
1
+ import { ClassMirrorBuilder } from '../../test-helpers/ClassMirrorBuilder';
2
+ import { createInheritanceChain } from '../inheritance-chain';
3
+
4
+ describe('inheritance chain for classes', () => {
5
+ test('returns an empty list of the class does not extend any other class', () => {
6
+ const classMirror = new ClassMirrorBuilder().build();
7
+ const repository = [classMirror];
8
+
9
+ const inheritanceChain = createInheritanceChain(repository, classMirror);
10
+
11
+ expect(inheritanceChain).toEqual([]);
12
+ });
13
+
14
+ test('returns the name of the extended class if it is not found in the repository', () => {
15
+ const classMirror = new ClassMirrorBuilder().withExtendedClass('ExtendedClass').build();
16
+ const repository = [classMirror];
17
+
18
+ const inheritanceChain = createInheritanceChain(repository, classMirror);
19
+
20
+ expect(inheritanceChain).toEqual(['ExtendedClass']);
21
+ });
22
+
23
+ test('returns the extended class when it is present in the repository', () => {
24
+ const classMirror = new ClassMirrorBuilder().withExtendedClass('ExtendedClass').build();
25
+ const extendedClass = new ClassMirrorBuilder().withName('ExtendedClass').build();
26
+ const repository = [classMirror, extendedClass];
27
+
28
+ const inheritanceChain = createInheritanceChain(repository, classMirror);
29
+
30
+ expect(inheritanceChain).toEqual(['ExtendedClass']);
31
+ });
32
+
33
+ test('returns the full inheritance chain when the extended class is also extended', () => {
34
+ const classMirror = new ClassMirrorBuilder().withExtendedClass('ExtendedClass').build();
35
+ const extendedClass = new ClassMirrorBuilder().withName('ExtendedClass').withExtendedClass('SuperClass').build();
36
+ const superClass = new ClassMirrorBuilder().withName('SuperClass').build();
37
+ const repository = [classMirror, extendedClass, superClass];
38
+
39
+ const inheritanceChain = createInheritanceChain(repository, classMirror);
40
+
41
+ expect(inheritanceChain).toEqual(['ExtendedClass', 'SuperClass']);
42
+ });
43
+
44
+ test('returns the inheritance for a class that extends an inner class', () => {
45
+ const classMirror = new ClassMirrorBuilder().withExtendedClass('OuterClass.InnerClass').build();
46
+ const innerClass = new ClassMirrorBuilder().withName('InnerClass').build();
47
+ const outerClass = new ClassMirrorBuilder().withName('OuterClass').addInnerClass(innerClass).build();
48
+ const repository = [classMirror, outerClass];
49
+
50
+ const inheritanceChain = createInheritanceChain(repository, classMirror);
51
+
52
+ expect(inheritanceChain).toEqual(['OuterClass.InnerClass']);
53
+ });
54
+ });
@@ -0,0 +1,396 @@
1
+ import { ClassMirror, InterfaceMirror, reflect as mirrorReflection, Type } from '@cparra/apex-reflection';
2
+ import { typeToRenderableType } from '../adapters/apex-types';
3
+ import { Link, Renderable, RenderableContent, RenderableEnum, StringOrLink } from './renderable/types';
4
+ import { classMarkdownTemplate } from '../transpiler/markdown/plain-markdown/class-template';
5
+ import { enumMarkdownTemplate } from '../transpiler/markdown/plain-markdown/enum-template';
6
+ import { interfaceMarkdownTemplate } from '../transpiler/markdown/plain-markdown/interface-template';
7
+ import * as E from 'fp-ts/Either';
8
+ import { flow, pipe } from 'fp-ts/function';
9
+ import { CompilationRequest, Template } from './template';
10
+ import Manifest from '../model/manifest';
11
+ import { referenceGuideTemplate } from './templates/reference-guide';
12
+ import { adaptDescribable } from '../adapters/documentables';
13
+ import { createInheritanceChain } from './inheritance-chain';
14
+
15
+ export const documentType = flow(typeToRenderableType, resolveApexTypeTemplate, compile);
16
+
17
+ export type DocumentationBundle = {
18
+ format: 'markdown';
19
+ referenceGuide: string; // Output file with links to all other files (e.g. index/table of contents)
20
+ docs: DocOutput[];
21
+ };
22
+
23
+ type DocumentationConfig = {
24
+ scope: string[];
25
+ outputDir: string;
26
+ namespace?: string;
27
+ sortMembersAlphabetically?: boolean;
28
+ defaultGroupName: string;
29
+ referenceGuideTemplate: string;
30
+ };
31
+
32
+ type DocOutput = {
33
+ directory: string;
34
+ docContents: string;
35
+ typeName: string;
36
+ type: 'class' | 'interface' | 'enum';
37
+ };
38
+
39
+ const configDefaults: DocumentationConfig = {
40
+ scope: ['public'],
41
+ outputDir: 'docs',
42
+ defaultGroupName: 'Miscellaneous',
43
+ referenceGuideTemplate: referenceGuideTemplate,
44
+ };
45
+
46
+ export function generateDocs(
47
+ input: string[],
48
+ config?: Partial<DocumentationConfig>,
49
+ ): E.Either<string[], DocumentationBundle> {
50
+ const configWithDefaults = { ...configDefaults, ...config };
51
+ return pipe(
52
+ input,
53
+ (input) => input.map(reflectSourceBody),
54
+ checkForReflectionErrors,
55
+ E.map((types) => filterTypesOutOfScope(types, configWithDefaults.scope)),
56
+ E.map((types) => types.map((type) => addInheritedMembers(type, types))),
57
+ E.map((types) => types.map((type) => addInheritanceChain(type, types))),
58
+ E.map((types) => typesToRenderableBundle(types, configWithDefaults)),
59
+ E.map(({ references, renderables }) => ({
60
+ referenceGuide: pipe(referencesToReferenceGuide(references, configWithDefaults.referenceGuideTemplate)),
61
+ docs: renderables.map((renderable) => renderableToOutputDoc(Object.values(references).flat(), renderable)),
62
+ })),
63
+ E.map(({ referenceGuide, docs }) => ({ format: 'markdown', referenceGuide: referenceGuide, docs })),
64
+ );
65
+ }
66
+
67
+ type ReferenceGuideReference = {
68
+ typeName: string;
69
+ directory: string;
70
+ title: Link;
71
+ description: RenderableContent[] | undefined;
72
+ };
73
+
74
+ type RenderableBundle = {
75
+ // References are grouped by their defined @group annotation
76
+ references: {
77
+ [key: string]: ReferenceGuideReference[];
78
+ };
79
+ renderables: Renderable[];
80
+ };
81
+
82
+ function typesToRenderableBundle(types: Type[], config: DocumentationConfig) {
83
+ return types.reduce<RenderableBundle>(
84
+ (acc, type) => {
85
+ const renderable = typeToRenderableType(
86
+ type,
87
+ (referenceName) => {
88
+ return linkFromTypeNameGenerator(type, types, referenceName, config);
89
+ },
90
+ config.namespace,
91
+ );
92
+ acc.renderables.push(renderable);
93
+
94
+ const descriptionLines = type.docComment?.descriptionLines;
95
+ const reference = {
96
+ typeName: type.name,
97
+ directory: getDirectoryFromRoot(config, type),
98
+ title: getLinkFromRoot(config, type),
99
+ description: adaptDescribable(descriptionLines, (referenceName) =>
100
+ getPossibleLinkFromRoot(config, referenceName, findType(types, referenceName)),
101
+ ).description,
102
+ };
103
+
104
+ const group = getTypeGroup(type, config);
105
+ if (!acc.references[group]) {
106
+ acc.references[group] = [];
107
+ }
108
+ acc.references[group].push(reference);
109
+
110
+ return acc;
111
+ },
112
+ {
113
+ references: {},
114
+ renderables: [],
115
+ },
116
+ );
117
+ }
118
+
119
+ function renderableToOutputDoc(referenceGuideReference: ReferenceGuideReference[], renderable: Renderable): DocOutput {
120
+ function buildDocOutput(renderable: Renderable, docContents: string): DocOutput {
121
+ const reference = referenceGuideReference.find(
122
+ (ref) => ref.typeName.toLowerCase() === renderable.name.toLowerCase(),
123
+ );
124
+ return {
125
+ directory: reference!.directory,
126
+ docContents,
127
+ typeName: renderable.name,
128
+ type: renderable.type,
129
+ };
130
+ }
131
+
132
+ return pipe(renderable, resolveApexTypeTemplate, compile, (docContents) => buildDocOutput(renderable, docContents));
133
+ }
134
+
135
+ function referencesToReferenceGuide(
136
+ references: { [key: string]: ReferenceGuideReference[] },
137
+ template: string,
138
+ ): string {
139
+ function alphabetizeReferences(references: { [key: string]: ReferenceGuideReference[] }): {
140
+ [key: string]: ReferenceGuideReference[];
141
+ } {
142
+ return Object.keys(references)
143
+ .sort((a, b) => a.localeCompare(b))
144
+ .reduce<{ [key: string]: ReferenceGuideReference[] }>((acc, key) => {
145
+ acc[key] = references[key].sort((a, b) => a.title.toString().localeCompare(b.title.toString()));
146
+ return acc;
147
+ }, {});
148
+ }
149
+
150
+ return pipe(references, alphabetizeReferences, (references) =>
151
+ compile({
152
+ template: template,
153
+ source: references,
154
+ }),
155
+ );
156
+ }
157
+
158
+ function filterTypesOutOfScope(types: Type[], scope: string[]): Type[] {
159
+ return new Manifest(types).filteredByAccessModifierAndAnnotations(scope);
160
+ }
161
+
162
+ function checkForReflectionErrors(reflectionResult: E.Either<string, Type>[]) {
163
+ function reduceReflectionResultIntoSingleEither(results: E.Either<string, Type>[]): {
164
+ errors: string[];
165
+ types: Type[];
166
+ } {
167
+ return results.reduce<{ errors: string[]; types: Type[] }>(
168
+ (acc, result) => {
169
+ E.isLeft(result) ? acc.errors.push(result.left) : acc.types.push(result.right);
170
+ return acc;
171
+ },
172
+ {
173
+ errors: [],
174
+ types: [],
175
+ },
176
+ );
177
+ }
178
+
179
+ return pipe(reflectionResult, reduceReflectionResultIntoSingleEither, ({ errors, types }) =>
180
+ errors.length ? E.left(errors) : E.right(types),
181
+ );
182
+ }
183
+
184
+ function reflectSourceBody(input: string): E.Either<string, Type> {
185
+ const result = mirrorReflection(input);
186
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
187
+ return result.error ? E.left(result.error.message) : E.right(result.typeMirror!);
188
+ }
189
+
190
+ function resolveApexTypeTemplate(renderable: Renderable): CompilationRequest {
191
+ function getTemplate(renderable: Renderable): string {
192
+ switch (renderable.type) {
193
+ case 'enum':
194
+ return enumMarkdownTemplate;
195
+ case 'interface':
196
+ return interfaceMarkdownTemplate;
197
+ case 'class':
198
+ return classMarkdownTemplate;
199
+ }
200
+ }
201
+
202
+ return {
203
+ template: getTemplate(renderable),
204
+ source: renderable as RenderableEnum,
205
+ };
206
+ }
207
+
208
+ function compile(request: CompilationRequest): string {
209
+ return Template.getInstance().compile(request);
210
+ }
211
+
212
+ function findType(repository: Type[], referenceName: string) {
213
+ return repository.find((currentType: Type) => currentType.name.toLowerCase() === referenceName.toLowerCase());
214
+ }
215
+
216
+ function addInheritedMembers<T extends Type>(current: T, repository: Type[]): T {
217
+ if (current.type_name === 'enum') {
218
+ return current;
219
+ } else if (current.type_name === 'interface') {
220
+ return addInheritedInterfaceMethods(current as InterfaceMirror, repository) as T;
221
+ } else {
222
+ return addInheritedClassMembers(current as ClassMirror, repository) as T;
223
+ }
224
+ }
225
+
226
+ function addInheritanceChain<T extends Type>(current: T, repository: Type[]): T {
227
+ if (current.type_name === 'enum' || current.type_name === 'interface') {
228
+ return current;
229
+ } else {
230
+ const inheritanceChain = createInheritanceChain(repository, current as ClassMirror);
231
+ return {
232
+ ...current,
233
+ inheritanceChain,
234
+ };
235
+ }
236
+ }
237
+
238
+ function getParents<T extends Type>(
239
+ extendedNamesExtractor: (current: T) => string[],
240
+ current: T,
241
+ repository: Type[],
242
+ ): T[] {
243
+ return pipe(
244
+ extendedNamesExtractor(current),
245
+ (interfaces: string[]) => interfaces.map((interfaceName) => repository.find((type) => type.name === interfaceName)),
246
+ (interfaces = []) => interfaces.filter((type) => type !== undefined) as T[],
247
+ (interfaces) =>
248
+ interfaces.reduce<T[]>(
249
+ (acc, current) => [...acc, ...getParents(extendedNamesExtractor, current, repository)],
250
+ interfaces,
251
+ ),
252
+ );
253
+ }
254
+
255
+ function addInheritedInterfaceMethods(interfaceMirror: InterfaceMirror, repository: Type[]): InterfaceMirror {
256
+ function methodAlreadyExists(memberName: string, members: { name: string }[]) {
257
+ return members.some((member) => member.name.toLowerCase() === memberName.toLowerCase());
258
+ }
259
+
260
+ function parentExtractor(interfaceMirror: InterfaceMirror): string[] {
261
+ return interfaceMirror.extended_interfaces;
262
+ }
263
+
264
+ const parents = getParents(parentExtractor, interfaceMirror, repository);
265
+ return {
266
+ ...interfaceMirror,
267
+ methods: parents.reduce(
268
+ (acc, currentValue) => [
269
+ ...acc,
270
+ ...currentValue.methods
271
+ .filter((method) => !methodAlreadyExists(method.name, acc))
272
+ .map((method) => ({
273
+ ...method,
274
+ inherited: true,
275
+ })),
276
+ ],
277
+ interfaceMirror.methods,
278
+ ),
279
+ };
280
+ }
281
+
282
+ function addInheritedClassMembers(classMirror: ClassMirror, repository: Type[]): ClassMirror {
283
+ function memberAlreadyExists(memberName: string, members: { name: string }[]) {
284
+ return members.some((member) => member.name.toLowerCase() === memberName.toLowerCase());
285
+ }
286
+
287
+ function parentExtractor(classMirror: ClassMirror): string[] {
288
+ return classMirror.extended_class ? [classMirror.extended_class] : [];
289
+ }
290
+
291
+ function filterMember<T extends { name: string; access_modifier: string }>(members: T[], existing: T[]): T[] {
292
+ return members
293
+ .filter((member) => member.access_modifier.toLowerCase() !== 'private')
294
+ .filter((member) => !memberAlreadyExists(member.name, existing))
295
+ .map((member) => ({
296
+ ...member,
297
+ inherited: true,
298
+ }));
299
+ }
300
+
301
+ const parents = getParents(parentExtractor, classMirror, repository);
302
+ return {
303
+ ...classMirror,
304
+ fields: parents.reduce(
305
+ (acc, currentValue) => [...acc, ...filterMember(currentValue.fields, acc)],
306
+ classMirror.fields,
307
+ ),
308
+ properties: parents.reduce(
309
+ (acc, currentValue) => [...acc, ...filterMember(currentValue.properties, acc)],
310
+ classMirror.properties,
311
+ ),
312
+ methods: parents.reduce(
313
+ (acc, currentValue) => [...acc, ...filterMember(currentValue.methods, acc)],
314
+ classMirror.methods,
315
+ ),
316
+ };
317
+ }
318
+
319
+ function linkFromTypeNameGenerator(
320
+ typeBeingDocumented: Type,
321
+ repository: Type[],
322
+ referenceName: string,
323
+ config: DocumentationConfig,
324
+ ): StringOrLink {
325
+ const type = findType(repository, referenceName);
326
+ if (!type) {
327
+ // If the type is not found, we return the type name as a string.
328
+ return referenceName;
329
+ }
330
+
331
+ const [fullClassName, fileLink] = getFileLinkTuple(typeBeingDocumented, type, config);
332
+ return {
333
+ title: fullClassName,
334
+ url: fileLink,
335
+ };
336
+ }
337
+
338
+ function getFileLinkTuple(
339
+ typeBeingDocumented: Type,
340
+ referencedType: Type,
341
+ config: DocumentationConfig,
342
+ ): [string, string] {
343
+ const namespacePrefix = config.namespace ? `${config.namespace}.` : '';
344
+ const directoryRoot = `${getDirectoryRoot(typeBeingDocumented, referencedType, config)}`;
345
+ // TODO: Instead of adding a "." to the name when there is a namespace, maybe we want to create a folder for everything
346
+ // within that namespace and put the files in there.
347
+ const fullClassName = `${namespacePrefix}${referencedType.name}`;
348
+ return [fullClassName, `${directoryRoot}${fullClassName}.md`];
349
+ }
350
+
351
+ function getDirectoryFromRoot(config: DocumentationConfig, type?: Type): string {
352
+ if (!type) {
353
+ return '';
354
+ }
355
+ return `./${getSanitizedGroup(type, config)}`;
356
+ }
357
+
358
+ function getPossibleLinkFromRoot(config: DocumentationConfig, fallback: string, type?: Type): StringOrLink {
359
+ if (!type) {
360
+ return fallback;
361
+ }
362
+ const namespacePrefix = config.namespace ? `${config.namespace}.` : '';
363
+ const title = `${namespacePrefix}${type.name}`;
364
+ return {
365
+ title: title,
366
+ url: `${getDirectoryFromRoot(config, type)}/${title}.md`,
367
+ };
368
+ }
369
+
370
+ function getLinkFromRoot(config: DocumentationConfig, type: Type): Link {
371
+ const namespacePrefix = config.namespace ? `${config.namespace}.` : '';
372
+ const title = `${namespacePrefix}${type.name}`;
373
+ return {
374
+ title: title,
375
+ url: `${getDirectoryFromRoot(config, type)}/${title}.md`,
376
+ };
377
+ }
378
+
379
+ function getDirectoryRoot(typeBeingDocumented: Type, referencedType: Type, config: DocumentationConfig) {
380
+ if (getTypeGroup(typeBeingDocumented, config) === getTypeGroup(referencedType, config)) {
381
+ // If the types the same groups then we simply link directly to that file
382
+ return './';
383
+ } else {
384
+ // If the types have different groups, then we have to go up a directory
385
+ return `../${getSanitizedGroup(referencedType, config)}/`;
386
+ }
387
+ }
388
+
389
+ function getTypeGroup(type: Type, config: DocumentationConfig): string {
390
+ const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group');
391
+ return groupAnnotation?.body ?? config.defaultGroupName;
392
+ }
393
+
394
+ function getSanitizedGroup(classModel: Type, config: DocumentationConfig) {
395
+ return getTypeGroup(classModel, config).replace(/ /g, '-').replace('.', '');
396
+ }
@@ -0,0 +1,23 @@
1
+ import { ClassMirror, Type } from '@cparra/apex-reflection';
2
+ import { pipe } from 'fp-ts/function';
3
+ import * as O from 'fp-ts/Option';
4
+
5
+ export function createInheritanceChain(repository: Type[], classMirror: ClassMirror): string[] {
6
+ return pipe(
7
+ O.fromNullable(classMirror.extended_class),
8
+ O.match(
9
+ () => [],
10
+ (extendedClassName) => inheritanceChainFromParentClassName(repository, extendedClassName),
11
+ ),
12
+ );
13
+ }
14
+
15
+ function inheritanceChainFromParentClassName(repository: Type[], className: string): string[] {
16
+ return pipe(
17
+ O.fromNullable(repository.find((type) => type.name.toLowerCase() === className.toLowerCase())),
18
+ O.match(
19
+ () => [className],
20
+ (extendedClass: Type) => [className, ...createInheritanceChain(repository, extendedClass as ClassMirror)],
21
+ ),
22
+ );
23
+ }