@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.
- package/LICENSE +1 -1
- package/README.md +246 -650
- package/dist/cli/generate.js +74 -3095
- package/dist/defaults-BcE8DTat.js +13 -0
- package/dist/defaults-D07y_bq4.js +40 -0
- package/dist/defaults-gPzwP66p.js +14 -0
- package/dist/index.d.ts +49 -19
- package/dist/index.js +90 -2
- package/dist/logger-BEbUIfqN.js +3282 -0
- package/dist/logger-BGuf1PnL.js +3281 -0
- package/dist/logger-CWBRF2za.js +3284 -0
- package/dist/logger-CdBmDEN1.js +3283 -0
- package/dist/logger-Ce4QqPFR.js +3278 -0
- package/dist/logger-CyEVYaAC.js +3284 -0
- package/dist/logger-D7a83ycP.js +3277 -0
- package/dist/logger-DGaHeBKk.js +3279 -0
- package/dist/logger-Dqhl_lO_.js +3278 -0
- package/dist/logger-aySSWi0G.js +3280 -0
- package/dist/logger-qLCcAtiy.js +3284 -0
- package/examples/README.md +5 -0
- package/examples/docsify/README.md +17 -0
- package/examples/docsify/apexdocs.config.ts +13 -0
- package/examples/docsify/classes/ASampleClass.cls +57 -0
- package/examples/docsify/classes/CodeControl.cls +19 -0
- package/examples/docsify/classes/SampleClass.cls +95 -0
- package/examples/docsify/classes/SampleInterface.cls +17 -0
- package/examples/docsify/classes/SomeDto.cls +122 -0
- package/examples/docsify/docs/.nojekyll +0 -0
- package/examples/docsify/docs/README.md +25 -0
- package/examples/docsify/docs/_config.yml +1 -0
- package/examples/docsify/docs/index.html +22 -0
- package/examples/docsify/docs/miscellaneous/ASampleClass.md +88 -0
- package/examples/docsify/docs/miscellaneous/CodeControl.md +107 -0
- package/examples/docsify/docs/miscellaneous/SomeDto.md +244 -0
- package/examples/docsify/docs/sample-classes/SampleClass.md +171 -0
- package/examples/docsify/docs/sample-interfaces/SampleInterface.md +36 -0
- package/examples/docsify/package-lock.json +2459 -0
- package/examples/docsify/package.json +14 -0
- package/examples/imported/.forceignore +12 -0
- package/examples/imported/README.md +6 -0
- package/examples/imported/config/project-scratch-def.json +5 -0
- package/examples/imported/docs/index.md +109 -0
- package/examples/imported/docs/miscellaneous/BaseClass.md +13 -0
- package/examples/imported/docs/miscellaneous/MultiInheritanceClass.md +69 -0
- package/examples/imported/docs/miscellaneous/ParentInterface.md +12 -0
- package/examples/imported/docs/miscellaneous/ReferencedEnum.md +5 -0
- package/examples/imported/docs/miscellaneous/SampleException.md +21 -0
- package/examples/imported/docs/miscellaneous/SampleInterface.md +113 -0
- package/examples/imported/docs/miscellaneous/Url.md +308 -0
- package/examples/imported/docs/sample-enums/SampleEnum.md +33 -0
- package/examples/imported/docs/samplegroup/SampleClass.md +167 -0
- package/examples/imported/force-app/classes/BaseClass.cls +3 -0
- package/examples/imported/force-app/classes/MultiInheritanceClass.cls +1 -0
- package/examples/imported/force-app/classes/ParentInterface.cls +3 -0
- package/examples/imported/force-app/classes/ReferencedEnum.cls +3 -0
- package/examples/imported/force-app/classes/SampleInterface.cls +50 -0
- package/examples/imported/force-app/classes/Url.cls +196 -0
- package/examples/imported/package-lock.json +665 -0
- package/examples/imported/package.json +6 -0
- package/examples/imported/scripts/process-docs.mjs +16 -0
- package/examples/imported/sfdx-project.json +12 -0
- package/examples/markdown/README.md +7 -0
- package/examples/markdown/docs/miscellaneous/Url.md +10 -8
- package/examples/markdown/force-app/classes/Url.cls +3 -1
- package/examples/markdown-jsconfig/.forceignore +12 -0
- package/examples/markdown-jsconfig/README.md +9 -0
- package/examples/markdown-jsconfig/apexdocs.config.mjs +22 -0
- package/examples/markdown-jsconfig/config/project-scratch-def.json +5 -0
- package/examples/markdown-jsconfig/docs/index.md +12 -0
- package/examples/markdown-jsconfig/docs/miscellaneous/Url.md +315 -0
- package/examples/markdown-jsconfig/force-app/classes/Url.cls +196 -0
- package/examples/markdown-jsconfig/package-lock.json +665 -0
- package/examples/markdown-jsconfig/package.json +15 -0
- package/examples/markdown-jsconfig/sfdx-project.json +12 -0
- package/examples/open-api/README.md +5 -0
- package/examples/open-api/docs/openapi.json +2 -570
- package/examples/vitepress/README.md +25 -0
- package/examples/vitepress/apexdocs.config.ts +9 -2
- package/examples/vitepress/docs/index.md +11 -11
- package/examples/vitepress/docs/miscellaneous/BaseClass.md +1 -1
- package/examples/vitepress/docs/miscellaneous/MultiInheritanceClass.md +2 -2
- package/examples/vitepress/docs/miscellaneous/SampleException.md +1 -1
- package/examples/vitepress/docs/miscellaneous/SampleInterface.md +6 -6
- package/examples/vitepress/docs/miscellaneous/Url.md +3 -3
- package/examples/vitepress/docs/sample-enums/SampleEnum.md +3 -3
- package/examples/vitepress/docs/samplegroup/SampleClass.md +5 -5
- package/examples/vitepress/force-app/main/default/classes/feature-a/SampleClass.cls +73 -0
- package/examples/vitepress/force-app/main/default/classes/feature-a/SampleEnum.cls +30 -0
- package/examples/vitepress/force-app/main/default/classes/feature-a/SampleException.cls +17 -0
- package/package.json +3 -3
- package/src/application/Apexdocs.ts +16 -19
- package/src/application/__tests__/apex-file-reader.spec.ts +108 -67
- package/src/application/apex-file-reader.ts +1 -0
- package/src/application/generators/openapi.ts +17 -13
- package/src/cli/args.ts +12 -3
- package/src/cli/commands/markdown.ts +15 -12
- package/src/cli/commands/openapi.ts +5 -5
- package/src/cli/generate.ts +20 -4
- package/src/core/markdown/__test__/generating-class-docs.spec.ts +15 -386
- package/src/core/markdown/__test__/generating-docs.spec.ts +378 -0
- package/src/core/markdown/__test__/generating-enum-docs.spec.ts +4 -328
- package/src/core/markdown/__test__/generating-interface-docs.spec.ts +4 -296
- package/src/core/markdown/__test__/generating-reference-guide.spec.ts +17 -1
- package/src/core/markdown/__test__/test-helpers.ts +3 -1
- package/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +3 -1
- package/src/core/markdown/adapters/renderable-to-page-data.ts +6 -4
- package/src/core/markdown/generate-docs.ts +13 -15
- package/src/core/markdown/reflection/__test__/filter-scope.spec.ts +290 -0
- package/src/core/markdown/reflection/__test__/helpers.ts +18 -0
- package/src/core/markdown/reflection/__test__/remove-excluded-tags.spec.ts +200 -0
- package/src/core/markdown/reflection/remove-excluded-tags.ts +168 -0
- package/src/core/markdown/reflection/{sort-members.ts → sort-types-and-members.ts} +7 -5
- package/src/core/markdown/templates/reference-guide.ts +2 -2
- package/src/core/openapi/__tests__/open-api-docs-processor.spec.ts +6 -3
- package/src/core/openapi/open-api-docs-processor.ts +3 -3
- package/src/core/openapi/parser.ts +5 -2
- package/src/core/shared/types.d.ts +18 -18
- package/src/defaults.ts +15 -3
- package/src/index.ts +88 -14
- package/src/util/error-logger.ts +36 -36
- package/src/util/logger.ts +18 -11
- /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleClass.cls +0 -0
- /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleEnum.cls +0 -0
- /package/examples/{vitepress/force-app/main/default → imported/force-app}/classes/SampleException.cls +0 -0
- /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
|
|
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
|
|
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
|
|
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: <script>alert('Hello');</script>
|
|
14
14
|
|
|
15
15
|
```apex
|
|
@@ -19,9 +19,9 @@ C -->|extends| D[GreatGrandParentInterface]
|
|
|
19
19
|
|
|
20
20
|
**Date** 2020-01-01
|
|
21
21
|
|
|
22
|
-
**See** [SampleEnum](../sample-enums/SampleEnum
|
|
22
|
+
**See** [SampleEnum](../sample-enums/SampleEnum)
|
|
23
23
|
|
|
24
|
-
**See** [ReferencedEnum](ReferencedEnum
|
|
24
|
+
**See** [ReferencedEnum](ReferencedEnum)
|
|
25
25
|
|
|
26
26
|
## Namespace
|
|
27
27
|
apexdocs
|
|
@@ -31,7 +31,7 @@ SampleInterface sampleInterface = new SampleInterface();
|
|
|
31
31
|
sampleInterface.sampleMethod();
|
|
32
32
|
|
|
33
33
|
**Extends**
|
|
34
|
-
[ParentInterface](ParentInterface
|
|
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
|
|
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
|
|
97
|
+
| theEnum | [SampleEnum](../sample-enums/SampleEnum) | This is an enum parameter |
|
|
98
98
|
|
|
99
99
|
#### Return Type
|
|
100
|
-
**[SampleEnum](../sample-enums/SampleEnum
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 -->|referenced by| A
|
|
|
27
27
|
|
|
28
28
|
**Date** 2022-01-01
|
|
29
29
|
|
|
30
|
-
**See** [ReferencedEnum](../miscellaneous/ReferencedEnum
|
|
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
|
|
22
|
+
[BaseClass](../miscellaneous/BaseClass)
|
|
23
23
|
|
|
24
24
|
**Implements**
|
|
25
25
|
|
|
26
|
-
[SampleInterface](../miscellaneous/SampleInterface
|
|
27
|
-
[ParentInterface](../miscellaneous/ParentInterface
|
|
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
|
|
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
|
|
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.
|
|
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<
|
|
20
|
-
|
|
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
|
-
|
|
34
|
+
await openApi(logger, fileBodies, config);
|
|
35
|
+
return E.right('✔️ Documentation generated successfully!');
|
|
36
36
|
}
|
|
37
37
|
} catch (error) {
|
|
38
|
-
|
|
38
|
+
return E.left([error]);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function generateMarkdownDocumentation(
|
|
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(() =>
|
|
49
|
+
TE.map(() => '✔️ Documentation generated successfully!'),
|
|
47
50
|
TE.mapLeft((error) => {
|
|
48
51
|
if (error._tag === 'HookError') {
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
return [
|
|
61
60
|
'Error(s) occurred while parsing files. Please review the following issues:',
|
|
62
61
|
...error.errors.map(formatReflectionError),
|
|
63
|
-
]
|
|
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
|
|
65
|
+
const fileSystem = new TestFileSystem([
|
|
6
66
|
{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
|
79
|
+
const fileSystem = new TestFileSystem([
|
|
35
80
|
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
|
59
|
-
const
|
|
97
|
+
it('returns the file contents for all Apex files', async () => {
|
|
98
|
+
const fileSystem = new TestFileSystem([
|
|
60
99
|
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
83
|
-
);
|
|
84
|
-
expect(result.length).toBe(
|
|
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(
|
|
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
|
-
|
|
40
|
+
logger.logSingle(`${file.outputDocPath} processed.`, 'green');
|
|
36
41
|
}),
|
|
37
|
-
TE.
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
72
|
+
logger.logSingle(filteredLogMessage, 'green');
|
|
73
|
+
logger.logSingle(`Creating documentation for ${filteredTypes.length} file(s)`, 'green');
|
|
70
74
|
return filteredTypes;
|
|
71
75
|
}
|