@cparra/apexdocs 3.0.0-beta.1 → 3.0.0

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 (125) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +246 -650
  3. package/dist/cli/generate.js +74 -3095
  4. package/dist/defaults-BcE8DTat.js +13 -0
  5. package/dist/defaults-D07y_bq4.js +40 -0
  6. package/dist/defaults-gPzwP66p.js +14 -0
  7. package/dist/index.d.ts +49 -19
  8. package/dist/index.js +90 -2
  9. package/dist/logger-BEbUIfqN.js +3282 -0
  10. package/dist/logger-BGuf1PnL.js +3281 -0
  11. package/dist/logger-CWBRF2za.js +3284 -0
  12. package/dist/logger-CdBmDEN1.js +3283 -0
  13. package/dist/logger-Ce4QqPFR.js +3278 -0
  14. package/dist/logger-CyEVYaAC.js +3284 -0
  15. package/dist/logger-D7a83ycP.js +3277 -0
  16. package/dist/logger-DGaHeBKk.js +3279 -0
  17. package/dist/logger-Dqhl_lO_.js +3278 -0
  18. package/dist/logger-aySSWi0G.js +3280 -0
  19. package/dist/logger-qLCcAtiy.js +3284 -0
  20. package/examples/README.md +5 -0
  21. package/examples/docsify/README.md +17 -0
  22. package/examples/docsify/apexdocs.config.ts +13 -0
  23. package/examples/docsify/classes/ASampleClass.cls +57 -0
  24. package/examples/docsify/classes/CodeControl.cls +19 -0
  25. package/examples/docsify/classes/SampleClass.cls +95 -0
  26. package/examples/docsify/classes/SampleInterface.cls +17 -0
  27. package/examples/docsify/classes/SomeDto.cls +122 -0
  28. package/examples/docsify/docs/.nojekyll +0 -0
  29. package/examples/docsify/docs/README.md +25 -0
  30. package/examples/docsify/docs/_config.yml +1 -0
  31. package/examples/docsify/docs/index.html +22 -0
  32. package/examples/docsify/docs/miscellaneous/ASampleClass.md +88 -0
  33. package/examples/docsify/docs/miscellaneous/CodeControl.md +107 -0
  34. package/examples/docsify/docs/miscellaneous/SomeDto.md +244 -0
  35. package/examples/docsify/docs/sample-classes/SampleClass.md +171 -0
  36. package/examples/docsify/docs/sample-interfaces/SampleInterface.md +36 -0
  37. package/examples/docsify/package-lock.json +2459 -0
  38. package/examples/docsify/package.json +14 -0
  39. package/examples/imported/.forceignore +12 -0
  40. package/examples/imported/README.md +6 -0
  41. package/examples/imported/config/project-scratch-def.json +5 -0
  42. package/examples/imported/docs/index.md +109 -0
  43. package/examples/imported/docs/miscellaneous/BaseClass.md +13 -0
  44. package/examples/imported/docs/miscellaneous/MultiInheritanceClass.md +69 -0
  45. package/examples/imported/docs/miscellaneous/ParentInterface.md +12 -0
  46. package/examples/imported/docs/miscellaneous/ReferencedEnum.md +5 -0
  47. package/examples/imported/docs/miscellaneous/SampleException.md +21 -0
  48. package/examples/imported/docs/miscellaneous/SampleInterface.md +113 -0
  49. package/examples/imported/docs/miscellaneous/Url.md +308 -0
  50. package/examples/imported/docs/sample-enums/SampleEnum.md +33 -0
  51. package/examples/imported/docs/samplegroup/SampleClass.md +167 -0
  52. package/examples/imported/force-app/classes/BaseClass.cls +3 -0
  53. package/examples/imported/force-app/classes/MultiInheritanceClass.cls +1 -0
  54. package/examples/imported/force-app/classes/ParentInterface.cls +3 -0
  55. package/examples/imported/force-app/classes/ReferencedEnum.cls +3 -0
  56. package/examples/imported/force-app/classes/SampleInterface.cls +50 -0
  57. package/examples/imported/force-app/classes/Url.cls +196 -0
  58. package/examples/imported/package-lock.json +665 -0
  59. package/examples/imported/package.json +6 -0
  60. package/examples/imported/scripts/process-docs.mjs +16 -0
  61. package/examples/imported/sfdx-project.json +12 -0
  62. package/examples/markdown/README.md +7 -0
  63. package/examples/markdown/docs/miscellaneous/Url.md +10 -8
  64. package/examples/markdown/force-app/classes/Url.cls +3 -1
  65. package/examples/markdown-jsconfig/.forceignore +12 -0
  66. package/examples/markdown-jsconfig/README.md +9 -0
  67. package/examples/markdown-jsconfig/apexdocs.config.mjs +22 -0
  68. package/examples/markdown-jsconfig/config/project-scratch-def.json +5 -0
  69. package/examples/markdown-jsconfig/docs/index.md +12 -0
  70. package/examples/markdown-jsconfig/docs/miscellaneous/Url.md +315 -0
  71. package/examples/markdown-jsconfig/force-app/classes/Url.cls +196 -0
  72. package/examples/markdown-jsconfig/package-lock.json +665 -0
  73. package/examples/markdown-jsconfig/package.json +15 -0
  74. package/examples/markdown-jsconfig/sfdx-project.json +12 -0
  75. package/examples/open-api/README.md +5 -0
  76. package/examples/open-api/docs/openapi.json +2 -570
  77. package/examples/vitepress/README.md +25 -0
  78. package/examples/vitepress/apexdocs.config.ts +9 -2
  79. package/examples/vitepress/docs/index.md +11 -11
  80. package/examples/vitepress/docs/miscellaneous/BaseClass.md +1 -1
  81. package/examples/vitepress/docs/miscellaneous/MultiInheritanceClass.md +2 -2
  82. package/examples/vitepress/docs/miscellaneous/SampleException.md +1 -1
  83. package/examples/vitepress/docs/miscellaneous/SampleInterface.md +6 -6
  84. package/examples/vitepress/docs/miscellaneous/Url.md +3 -3
  85. package/examples/vitepress/docs/sample-enums/SampleEnum.md +3 -3
  86. package/examples/vitepress/docs/samplegroup/SampleClass.md +5 -5
  87. package/examples/vitepress/force-app/main/default/classes/feature-a/SampleClass.cls +73 -0
  88. package/examples/vitepress/force-app/main/default/classes/feature-a/SampleEnum.cls +30 -0
  89. package/examples/vitepress/force-app/main/default/classes/feature-a/SampleException.cls +17 -0
  90. package/package.json +3 -3
  91. package/src/application/Apexdocs.ts +16 -19
  92. package/src/application/__tests__/apex-file-reader.spec.ts +108 -67
  93. package/src/application/apex-file-reader.ts +1 -0
  94. package/src/application/generators/openapi.ts +17 -13
  95. package/src/cli/args.ts +12 -3
  96. package/src/cli/commands/markdown.ts +15 -12
  97. package/src/cli/commands/openapi.ts +5 -5
  98. package/src/cli/generate.ts +20 -4
  99. package/src/core/markdown/__test__/generating-class-docs.spec.ts +15 -386
  100. package/src/core/markdown/__test__/generating-docs.spec.ts +378 -0
  101. package/src/core/markdown/__test__/generating-enum-docs.spec.ts +4 -328
  102. package/src/core/markdown/__test__/generating-interface-docs.spec.ts +4 -296
  103. package/src/core/markdown/__test__/generating-reference-guide.spec.ts +17 -1
  104. package/src/core/markdown/__test__/test-helpers.ts +3 -1
  105. package/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +3 -1
  106. package/src/core/markdown/adapters/renderable-to-page-data.ts +6 -4
  107. package/src/core/markdown/generate-docs.ts +13 -15
  108. package/src/core/markdown/reflection/__test__/filter-scope.spec.ts +290 -0
  109. package/src/core/markdown/reflection/__test__/helpers.ts +18 -0
  110. package/src/core/markdown/reflection/__test__/remove-excluded-tags.spec.ts +200 -0
  111. package/src/core/markdown/reflection/remove-excluded-tags.ts +168 -0
  112. package/src/core/markdown/reflection/{sort-members.ts → sort-types-and-members.ts} +7 -5
  113. package/src/core/markdown/templates/reference-guide.ts +2 -2
  114. package/src/core/openapi/__tests__/open-api-docs-processor.spec.ts +6 -3
  115. package/src/core/openapi/open-api-docs-processor.ts +3 -3
  116. package/src/core/openapi/parser.ts +5 -2
  117. package/src/core/shared/types.d.ts +18 -18
  118. package/src/defaults.ts +15 -3
  119. package/src/index.ts +88 -14
  120. package/src/util/error-logger.ts +36 -36
  121. package/src/util/logger.ts +18 -11
  122. /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleClass.cls +0 -0
  123. /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleEnum.cls +0 -0
  124. /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleException.cls +0 -0
  125. /package/examples/vitepress/force-app/main/default/classes/{SampleInterface.cls → feature-a/SampleInterface.cls} +0 -0
@@ -9,7 +9,7 @@ apexdocs
9
9
 
10
10
  **Inheritance**
11
11
 
12
- [SampleClass](../samplegroup/SampleClass.md) < [BaseClass](BaseClass.md)
12
+ [SampleClass](../samplegroup/SampleClass) < [BaseClass](BaseClass)
13
13
 
14
14
  ## Fields
15
15
  ### `sampleEnumFromBase`
@@ -22,7 +22,7 @@ public sampleEnumFromBase
22
22
  ```
23
23
 
24
24
  #### Type
25
- [SampleEnum](../sample-enums/SampleEnum.md)
25
+ [SampleEnum](../sample-enums/SampleEnum)
26
26
 
27
27
  ## Properties
28
28
  ### Group Name
@@ -9,7 +9,7 @@ This is a sample exception.
9
9
  **Usage**
10
10
 
11
11
  You can use the exception the following way.
12
- You can also take a look at [SampleClass](../samplegroup/SampleClass.md) to see how it is used.
12
+ You can also take a look at [SampleClass](../samplegroup/SampleClass) to see how it is used.
13
13
  This is a dangerous HTML tag: &lt;script&gt;alert(&#x27;Hello&#x27;);&lt;/script&gt;
14
14
 
15
15
  ```apex
@@ -19,9 +19,9 @@ C --&gt;|extends| D[GreatGrandParentInterface]
19
19
 
20
20
  **Date** 2020-01-01
21
21
 
22
- **See** [SampleEnum](../sample-enums/SampleEnum.md)
22
+ **See** [SampleEnum](../sample-enums/SampleEnum)
23
23
 
24
- **See** [ReferencedEnum](ReferencedEnum.md)
24
+ **See** [ReferencedEnum](ReferencedEnum)
25
25
 
26
26
  ## Namespace
27
27
  apexdocs
@@ -31,7 +31,7 @@ SampleInterface sampleInterface &#x3D; new SampleInterface();
31
31
  sampleInterface.sampleMethod();
32
32
 
33
33
  **Extends**
34
- [ParentInterface](ParentInterface.md)
34
+ [ParentInterface](ParentInterface)
35
35
 
36
36
  ## Methods
37
37
  ### `sampleMethod()`
@@ -66,7 +66,7 @@ public String sampleMethod()
66
66
  Some return value
67
67
 
68
68
  #### Throws
69
- [SampleException](SampleException.md): This is a sample exception
69
+ [SampleException](SampleException): This is a sample exception
70
70
 
71
71
  AnotherSampleException: This is another sample exception
72
72
 
@@ -94,10 +94,10 @@ public SampleEnum sampleMethodWithParams(String param1, Integer param2, SampleEn
94
94
  |------|------|-------------|
95
95
  | param1 | String | This is the first parameter |
96
96
  | param2 | Integer | This is the second parameter |
97
- | theEnum | [SampleEnum](../sample-enums/SampleEnum.md) | This is an enum parameter |
97
+ | theEnum | [SampleEnum](../sample-enums/SampleEnum) | This is an enum parameter |
98
98
 
99
99
  #### Return Type
100
- **[SampleEnum](../sample-enums/SampleEnum.md)**
100
+ **[SampleEnum](../sample-enums/SampleEnum)**
101
101
 
102
102
  Some return value
103
103
 
@@ -120,7 +120,7 @@ global Url(Url context, String spec)
120
120
  #### Parameters
121
121
  | Name | Type | Description |
122
122
  |------|------|-------------|
123
- | context | [Url](Url.md) | The context in which to parse the specification. |
123
+ | context | [Url](Url) | The context in which to parse the specification. |
124
124
  | spec | String | The string to parse as a URL. |
125
125
 
126
126
  ---
@@ -191,7 +191,7 @@ global static Url getCurrentRequestUrl()
191
191
  ```
192
192
 
193
193
  #### Return Type
194
- **[Url](Url.md)**
194
+ **[Url](Url)**
195
195
 
196
196
  The URL of the entire request.
197
197
 
@@ -299,7 +299,7 @@ global static Url getOrgDomainUrl()
299
299
  ```
300
300
 
301
301
  #### Return Type
302
- **[Url](Url.md)**
302
+ **[Url](Url)**
303
303
 
304
304
  getOrgDomainUrl() always returns the login URL for your org, regardless of context. Use that URL when making API calls to your org.
305
305
 
@@ -6,13 +6,13 @@ title: SampleEnum
6
6
 
7
7
  `NAMESPACEACCESSIBLE`
8
8
 
9
- This is a sample enum. This references [ReferencedEnum](../miscellaneous/ReferencedEnum.md) .
9
+ This is a sample enum. This references [ReferencedEnum](../miscellaneous/ReferencedEnum) .
10
10
 
11
11
  This description has several lines
12
12
 
13
13
  **Some Custom**
14
14
 
15
- Test. I can also have a [ReferencedEnum](../miscellaneous/ReferencedEnum.md) here.
15
+ Test. I can also have a [ReferencedEnum](../miscellaneous/ReferencedEnum) here.
16
16
  And it can be multiline.
17
17
 
18
18
  **Mermaid**
@@ -27,7 +27,7 @@ B --&gt;|referenced by| A
27
27
 
28
28
  **Date** 2022-01-01
29
29
 
30
- **See** [ReferencedEnum](../miscellaneous/ReferencedEnum.md)
30
+ **See** [ReferencedEnum](../miscellaneous/ReferencedEnum)
31
31
 
32
32
  ## Namespace
33
33
  apexdocs
@@ -6,7 +6,7 @@ title: SampleClass
6
6
  `virtual`
7
7
 
8
8
  aliquip ex sunt officia ullamco anim deserunt magna aliquip nisi eiusmod in sit officia veniam ex
9
- deserunt ea officia exercitation laboris enim in duis quis enim eiusmod eu amet cupidatat.
9
+ **deserunt** ea officia exercitation laboris enim in duis quis enim eiusmod eu amet cupidatat.
10
10
 
11
11
  **Group** SampleGroup
12
12
 
@@ -19,12 +19,12 @@ sample.doSomething();
19
19
 
20
20
  **Inheritance**
21
21
 
22
- [BaseClass](../miscellaneous/BaseClass.md)
22
+ [BaseClass](../miscellaneous/BaseClass)
23
23
 
24
24
  **Implements**
25
25
 
26
- [SampleInterface](../miscellaneous/SampleInterface.md),
27
- [ParentInterface](../miscellaneous/ParentInterface.md)
26
+ [SampleInterface](../miscellaneous/SampleInterface),
27
+ [ParentInterface](../miscellaneous/ParentInterface)
28
28
 
29
29
  ## Fields
30
30
  ### Group Name
@@ -51,7 +51,7 @@ public sampleEnumFromBase
51
51
  ```
52
52
 
53
53
  ##### Type
54
- [SampleEnum](../sample-enums/SampleEnum.md)
54
+ [SampleEnum](../sample-enums/SampleEnum)
55
55
 
56
56
  ## Properties
57
57
  ### Group Name
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @description aliquip ex sunt officia ullamco anim deserunt magna aliquip nisi eiusmod in sit officia veniam ex
3
+ * **deserunt** ea officia exercitation laboris enim in duis quis enim eiusmod eu amet cupidatat.
4
+ * @group SampleGroup
5
+ * @example
6
+ * SampleClass sample = new SampleClass();
7
+ * sample.doSomething();
8
+ * @internal This should not appear in the docs
9
+ */
10
+ public with sharing virtual class SampleClass extends BaseClass implements SampleInterface, ParentInterface {
11
+ // @start-group Group Name
12
+ /**
13
+ * @description This is a sample field.
14
+ */
15
+ private final String name;
16
+ public String someProperty { get; private set; }
17
+ // @end-group
18
+
19
+ /**
20
+ * @description This is a sample constructor.
21
+ */
22
+ public SampleClass() {}
23
+
24
+ // @start-group Other Constructors
25
+ public SampleClass(String name) {
26
+ this.name = name;
27
+ }
28
+ // @end-group
29
+
30
+ // @start-group Available Methods
31
+ public void doSomething() {
32
+ System.debug('Doing something');
33
+ }
34
+ // @end-group
35
+
36
+ // @start-group Deprecated Methods
37
+
38
+ /**
39
+ * @description This is a sample method.
40
+ * @return A string value.
41
+ * @example
42
+ * SampleClass sample = new SampleClass();
43
+ * sample.doSomething();
44
+ */
45
+ @Deprecated
46
+ public virtual String sayHello() {
47
+ return 'Hello';
48
+ }
49
+
50
+ // @end-group
51
+
52
+ public class SomeInnerClass {
53
+ public String someInnerField;
54
+
55
+ public void doSomething() {
56
+ System.debug('Doing something');
57
+ }
58
+ }
59
+
60
+ /**
61
+ * @description This enum is used for foo and bar.
62
+ */
63
+ public enum SomeEnum {
64
+ /** @description This is a test. */
65
+ TEST_1,
66
+ TEST_2,
67
+ TEST_3
68
+ }
69
+
70
+ public interface SomeInterface {
71
+ void doSomething();
72
+ }
73
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @description This is a sample enum. This references {@link ReferencedEnum}.
3
+ *
4
+ * This description has several lines
5
+ * @group Sample Enums
6
+ * @author John Doe
7
+ * @date 2022-01-01
8
+ * @some-custom Test. I can also have a {@link ReferencedEnum} here.
9
+ * And it can be multiline.
10
+ * @see ReferencedEnum
11
+ * @mermaid
12
+ * graph TD
13
+ * A[SampleEnum] -->|references| B[ReferencedEnum]
14
+ * B -->|referenced by| A
15
+ */
16
+ @NamespaceAccessible
17
+ public enum SampleEnum {
18
+ /**
19
+ * @description This is value 1
20
+ */
21
+ VALUE1,
22
+ /**
23
+ * @description This is value 2
24
+ */
25
+ VALUE2,
26
+ /**
27
+ * @description This is value 3
28
+ */
29
+ VALUE3
30
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @description This is a sample exception.
3
+ * @usage
4
+ *
5
+ * You can use the exception the following way.
6
+ * You can also take a look at {@link SampleClass} to see how it is used.
7
+ * This is a dangerous HTML tag: <script>alert('Hello');</script>
8
+ *
9
+ * ```
10
+ * try {
11
+ * throw new SampleException();
12
+ * } catch (SampleException e) {
13
+ * System.debug('Caught exception');
14
+ * }
15
+ * ```
16
+ */
17
+ public class SampleException extends Exception {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cparra/apexdocs",
3
- "version": "3.0.0-beta.1",
3
+ "version": "3.0.0",
4
4
  "description": "Library with CLI capabilities to generate documentation for Salesforce Apex classes.",
5
5
  "keywords": [
6
6
  "apex",
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "scripts": {
19
19
  "test": "npm run build && jest",
20
+ "test:cov": "npm run build && jest --coverage",
20
21
  "build": "rimraf ./lib && npm run lint && tsc --noEmit && pkgroll",
21
22
  "lint": "eslint \"./src/**/*.{js,ts}\" --quiet --fix",
22
23
  "prepare": "npm run build",
@@ -58,7 +59,7 @@
58
59
  ]
59
60
  },
60
61
  "dependencies": {
61
- "@cparra/apex-reflection": "2.12.1",
62
+ "@cparra/apex-reflection": "2.13.1",
62
63
  "@types/js-yaml": "^4.0.9",
63
64
  "@types/yargs": "^17.0.32",
64
65
  "chalk": "^4.1.2",
@@ -68,7 +69,6 @@
68
69
  "fp-ts": "^2.16.8",
69
70
  "handlebars": "^4.7.8",
70
71
  "js-yaml": "^4.1.0",
71
- "type-fest": "^4.23.0",
72
72
  "yargs": "^17.7.2"
73
73
  },
74
74
  "imports": {
@@ -7,6 +7,7 @@ import { Logger } from '#utils/logger';
7
7
  import { UnparsedSourceFile, UserDefinedConfig, UserDefinedMarkdownConfig } from '../core/shared/types';
8
8
  import { pipe } from 'fp-ts/function';
9
9
  import * as TE from 'fp-ts/TaskEither';
10
+ import * as E from 'fp-ts/Either';
10
11
  import { ReflectionError } from '../core/markdown/reflection/reflect-source';
11
12
 
12
13
  /**
@@ -16,8 +17,8 @@ export class Apexdocs {
16
17
  /**
17
18
  * Generates documentation out of Apex source files.
18
19
  */
19
- static async generate(config: UserDefinedConfig): Promise<void> {
20
- Logger.logSingle(`Generating ${config.targetGenerator} documentation...`);
20
+ static async generate(config: UserDefinedConfig, logger: Logger): Promise<E.Either<unknown[], string>> {
21
+ logger.logSingle(`Generating ${config.targetGenerator} documentation...`);
21
22
 
22
23
  try {
23
24
  const fileBodies = await ApexFileReader.processFiles(
@@ -28,41 +29,37 @@ export class Apexdocs {
28
29
 
29
30
  switch (config.targetGenerator) {
30
31
  case 'markdown':
31
- await generateMarkdownDocumentation(fileBodies, config)();
32
- break;
32
+ return await generateMarkdownDocumentation(fileBodies, config)();
33
33
  case 'openapi':
34
- await openApi(fileBodies, config);
35
- break;
34
+ await openApi(logger, fileBodies, config);
35
+ return E.right('✔️ Documentation generated successfully!');
36
36
  }
37
37
  } catch (error) {
38
- Logger.logSingle(`❌ An error occurred while generating the documentation: ${error}`, 'red');
38
+ return E.left([error]);
39
39
  }
40
40
  }
41
41
  }
42
42
 
43
- function generateMarkdownDocumentation(fileBodies: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
43
+ function generateMarkdownDocumentation(
44
+ fileBodies: UnparsedSourceFile[],
45
+ config: UserDefinedMarkdownConfig,
46
+ ): TE.TaskEither<unknown[], string> {
44
47
  return pipe(
45
48
  markdown(fileBodies, config),
46
- TE.map(() => Logger.logSingle('✔️ Documentation generated successfully!')),
49
+ TE.map(() => '✔️ Documentation generated successfully!'),
47
50
  TE.mapLeft((error) => {
48
51
  if (error._tag === 'HookError') {
49
- Logger.error('Error(s) occurred while processing hooks. Please review the following issues:');
50
- Logger.error(error.error);
51
- return;
52
+ return ['Error(s) occurred while processing hooks. Please review the following issues:', error.error];
52
53
  }
53
54
 
54
55
  if (error._tag === 'FileWritingError') {
55
- Logger.error(error.message);
56
- Logger.error(error.error);
57
- return;
56
+ return ['Error(s) occurred while writing files. Please review the following issues:', error.error];
58
57
  }
59
58
 
60
- const errorMessages = [
59
+ return [
61
60
  'Error(s) occurred while parsing files. Please review the following issues:',
62
61
  ...error.errors.map(formatReflectionError),
63
- ].join('\n');
64
-
65
- Logger.error(errorMessages);
62
+ ];
66
63
  }),
67
64
  );
68
65
  }
@@ -1,87 +1,128 @@
1
1
  import { ApexFileReader } from '../apex-file-reader';
2
+ import { FileSystem } from '../file-system';
3
+
4
+ type File = {
5
+ type: 'file';
6
+ path: string;
7
+ content: string;
8
+ };
9
+
10
+ type Directory = {
11
+ type: 'directory';
12
+ path: string;
13
+ files: (File | Directory)[];
14
+ };
15
+
16
+ type Path = File | Directory;
17
+
18
+ class TestFileSystem implements FileSystem {
19
+ constructor(private readonly paths: Path[]) {}
20
+
21
+ async isDirectory(path: string): Promise<boolean> {
22
+ const directory = this.findPath(path);
23
+ return directory ? directory.type === 'directory' : false;
24
+ }
25
+
26
+ joinPath(...paths: string[]): string {
27
+ return paths.join('/');
28
+ }
29
+
30
+ async readDirectory(sourceDirectory: string): Promise<string[]> {
31
+ const directory = this.findPath(sourceDirectory);
32
+ if (!directory || directory.type !== 'directory') {
33
+ throw new Error('Directory not found');
34
+ }
35
+ return directory.files.map((f) => f.path);
36
+ }
37
+
38
+ async readFile(path: string): Promise<string> {
39
+ const file = this.findPath(path);
40
+ if (!file || file.type !== 'file') {
41
+ throw new Error('File not found');
42
+ }
43
+ return file.content;
44
+ }
45
+
46
+ exists(path: string): boolean {
47
+ return this.paths.some((p) => p.path === path);
48
+ }
49
+
50
+ findPath(path: string): Path | undefined {
51
+ const splitPath = path.split('/');
52
+ let currentPath = this.paths.find((p) => p.path === splitPath[0]);
53
+ for (let i = 1; i < splitPath.length; i++) {
54
+ if (!currentPath || currentPath.type !== 'directory') {
55
+ return undefined;
56
+ }
57
+ currentPath = currentPath.files.find((f) => f.path === splitPath[i]);
58
+ }
59
+ return currentPath;
60
+ }
61
+ }
2
62
 
3
63
  describe('File Reader', () => {
4
64
  it('returns an empty list when there are no files in the directory', async () => {
5
- const result = await ApexFileReader.processFiles(
65
+ const fileSystem = new TestFileSystem([
6
66
  {
7
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
- isDirectory(_: string): Promise<boolean> {
9
- return Promise.resolve(false);
10
- },
11
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
- joinPath(_: string): string {
13
- return '';
14
- },
15
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
- readDirectory(_: string): Promise<string[]> {
17
- return Promise.resolve([]);
18
- },
19
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
- readFile(_: string): Promise<string> {
21
- return Promise.resolve('');
22
- },
23
- exists(): boolean {
24
- return true;
25
- },
67
+ type: 'directory',
68
+ path: '',
69
+ files: [],
26
70
  },
27
- '',
28
- false,
29
- );
71
+ ]);
72
+
73
+ const result = await ApexFileReader.processFiles(fileSystem, '', false);
74
+
30
75
  expect(result.length).toBe(0);
31
76
  });
32
77
 
33
78
  it('returns an empty list when there are no Apex files in the directory', async () => {
34
- const result = await ApexFileReader.processFiles(
79
+ const fileSystem = new TestFileSystem([
35
80
  {
36
- isDirectory(): Promise<boolean> {
37
- return Promise.resolve(false);
38
- },
39
- joinPath(): string {
40
- return '';
41
- },
42
- readDirectory(): Promise<string[]> {
43
- return Promise.resolve(['SomeFile.md']);
44
- },
45
- readFile(): Promise<string> {
46
- return Promise.resolve('');
47
- },
48
- exists(): boolean {
49
- return true;
50
- },
81
+ type: 'directory',
82
+ path: '',
83
+ files: [
84
+ {
85
+ type: 'file',
86
+ path: 'SomeFile.md',
87
+ content: '## Some Markdown',
88
+ },
89
+ ],
51
90
  },
52
- '',
53
- false,
54
- );
91
+ ]);
92
+
93
+ const result = await ApexFileReader.processFiles(fileSystem, '', false);
55
94
  expect(result.length).toBe(0);
56
95
  });
57
96
 
58
- it('returns the file contents for an Apex file', async () => {
59
- const result = await ApexFileReader.processFiles(
97
+ it('returns the file contents for all Apex files', async () => {
98
+ const fileSystem = new TestFileSystem([
60
99
  {
61
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
62
- isDirectory(_: string): Promise<boolean> {
63
- return Promise.resolve(false);
64
- },
65
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
- joinPath(_: string): string {
67
- return 'SomeApexFile.cls';
68
- },
69
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
- readDirectory(_: string): Promise<string[]> {
71
- return Promise.resolve(['SomeApexFile.cls']);
72
- },
73
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
74
- readFile(_: string): Promise<string> {
75
- return Promise.resolve('public class MyClass{}');
76
- },
77
- exists(): boolean {
78
- return true;
79
- },
100
+ type: 'directory',
101
+ path: '',
102
+ files: [
103
+ {
104
+ type: 'file',
105
+ path: 'SomeFile.cls',
106
+ content: 'public class MyClass{}',
107
+ },
108
+ {
109
+ type: 'directory',
110
+ path: 'subdir',
111
+ files: [
112
+ {
113
+ type: 'file',
114
+ path: 'AnotherFile.cls',
115
+ content: 'public class AnotherClass{}',
116
+ },
117
+ ],
118
+ },
119
+ ],
80
120
  },
81
- '',
82
- false,
83
- );
84
- expect(result.length).toBe(1);
121
+ ]);
122
+
123
+ const result = await ApexFileReader.processFiles(fileSystem, '', false);
124
+ expect(result.length).toBe(2);
85
125
  expect(result[0].content).toBe('public class MyClass{}');
126
+ expect(result[1].content).toBe('public class AnotherClass{}');
86
127
  });
87
128
  });
@@ -28,6 +28,7 @@ export class ApexFileReader {
28
28
  const currentPath = fileSystem.joinPath(rootPath, filePath);
29
29
  if (await fileSystem.isDirectory(currentPath)) {
30
30
  paths.push(...(await this.getFilePaths(fileSystem, currentPath)));
31
+ return paths;
31
32
  }
32
33
  paths.push(currentPath);
33
34
  }
@@ -12,8 +12,13 @@ import { writeFiles } from '../file-writer';
12
12
  import { pipe } from 'fp-ts/function';
13
13
  import * as TE from 'fp-ts/TaskEither';
14
14
  import { OpenApiSettings } from '../../core/openApiSettings';
15
+ import { apply } from '#utils/fp';
15
16
 
16
- export default async function openApi(fileBodies: UnparsedSourceFile[], config: UserDefinedOpenApiConfig) {
17
+ export default async function openApi(
18
+ logger: Logger,
19
+ fileBodies: UnparsedSourceFile[],
20
+ config: UserDefinedOpenApiConfig,
21
+ ) {
17
22
  OpenApiSettings.build({
18
23
  sourceDirectory: config.sourceDir,
19
24
  outputDir: config.targetDir,
@@ -23,34 +28,33 @@ export default async function openApi(fileBodies: UnparsedSourceFile[], config:
23
28
  version: config.apiVersion,
24
29
  });
25
30
 
26
- const manifest = createManifest(new RawBodyParser(fileBodies), reflectionWithLogger);
31
+ const manifest = createManifest(new RawBodyParser(logger, fileBodies), apply(reflectionWithLogger, logger));
27
32
  TypesRepository.getInstance().populateAll(manifest.types);
28
- const filteredTypes = filterByScopes(manifest);
29
- const processor = new OpenApiDocsProcessor();
33
+ const filteredTypes = filterByScopes(logger, manifest);
34
+ const processor = new OpenApiDocsProcessor(logger);
30
35
  Transpiler.generate(filteredTypes, processor);
31
36
  const generatedFiles = processor.fileBuilder().files();
32
37
 
33
38
  await pipe(
34
39
  writeFiles(generatedFiles, config.targetDir, (file: PageData) => {
35
- Logger.logSingle(`${file.outputDocPath} processed.`, 'green');
40
+ logger.logSingle(`${file.outputDocPath} processed.`, 'green');
36
41
  }),
37
- TE.map(() => Logger.logSingle('✔️ Documentation generated successfully!')),
38
- TE.mapError((error) => Logger.error(error)),
42
+ TE.mapError((error) => logger.error(error)),
39
43
  )();
40
44
 
41
45
  // Logs any errors that the types might have in their doc comment's error field
42
- ErrorLogger.logErrors(filteredTypes);
46
+ ErrorLogger.logErrors(logger, filteredTypes);
43
47
  }
44
48
 
45
- function reflectionWithLogger(apexBundle: UnparsedSourceFile): ReflectionResult {
49
+ function reflectionWithLogger(logger: Logger, apexBundle: UnparsedSourceFile): ReflectionResult {
46
50
  const result = reflect(apexBundle.content);
47
51
  if (result.error) {
48
- Logger.error(`${apexBundle.filePath} - Parsing error ${result.error?.message}`);
52
+ logger.error(`${apexBundle.filePath} - Parsing error ${result.error?.message}`);
49
53
  }
50
54
  return result;
51
55
  }
52
56
 
53
- function filterByScopes(manifest: Manifest) {
57
+ function filterByScopes(logger: Logger, manifest: Manifest) {
54
58
  // If we are dealing with an OpenApi generator, we ignore the passed in access modifiers, and instead
55
59
  // we only keep classes annotated as @RestResource
56
60
  const filteredTypes = manifest.filteredByAccessModifierAndAnnotations([
@@ -65,7 +69,7 @@ function filterByScopes(manifest: Manifest) {
65
69
  manifest.types.length - filteredTypes.length
66
70
  } file(s), only keeping classes annotated as @RestResource.`;
67
71
 
68
- Logger.logSingle(filteredLogMessage, 'green');
69
- Logger.logSingle(`Creating documentation for ${filteredTypes.length} file(s)`, 'green');
72
+ logger.logSingle(filteredLogMessage, 'green');
73
+ logger.logSingle(`Creating documentation for ${filteredTypes.length} file(s)`, 'green');
70
74
  return filteredTypes;
71
75
  }