@cparra/apexdocs 3.0.0-alpha.9 → 3.0.0-rc.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 +442 -325
- package/dist/cli/generate.js +295 -205
- package/dist/index.d.ts +15 -17
- 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/apexdocs.config.mjs +21 -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/config/project-scratch-def.json +13 -0
- package/examples/open-api/docs/openapi.json +582 -0
- package/examples/{force-app → open-api/force-app}/main/default/classes/SampleClass.cls +1 -0
- package/examples/open-api/package-lock.json +724 -0
- package/examples/open-api/package.json +20 -0
- package/examples/open-api/sfdx-project.json +12 -0
- package/examples/vitepress/apexdocs.config.ts +7 -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/SampleClass.cls +1 -1
- package/package.json +2 -2
- package/src/application/Apexdocs.ts +39 -7
- package/src/application/__tests__/apex-file-reader.spec.ts +0 -17
- package/src/application/file-writer.ts +37 -15
- package/src/application/generators/markdown.ts +10 -39
- package/src/application/generators/openapi.ts +22 -6
- package/src/cli/args.ts +4 -1
- package/src/cli/commands/markdown.ts +1 -3
- package/src/cli/commands/openapi.ts +36 -0
- package/src/core/markdown/__test__/generating-class-docs.spec.ts +1 -129
- package/src/core/markdown/__test__/generating-docs.spec.ts +111 -0
- package/src/core/markdown/__test__/generating-enum-docs.spec.ts +0 -64
- package/src/core/markdown/__test__/generating-interface-docs.spec.ts +0 -64
- package/src/core/markdown/adapters/documentables.ts +0 -1
- package/src/core/markdown/generate-docs.ts +2 -5
- package/src/core/markdown/reflection/__test__/filter-scope.spec.ts +306 -0
- package/src/core/markdown/reflection/reflect-source.ts +51 -50
- package/src/core/openApiSettings.ts +41 -0
- package/src/core/openapi/__tests__/open-api-docs-processor.spec.ts +2 -2
- package/src/core/openapi/open-api-docs-processor.ts +8 -4
- package/src/core/openapi/open-api.ts +5 -1
- package/src/core/openapi/openapi-type-file.ts +1 -1
- package/src/core/openapi/parser.ts +1 -15
- package/src/core/openapi/transpiler.ts +0 -5
- package/src/core/parse-apex-metadata.ts +21 -5
- package/src/core/shared/types.d.ts +18 -17
- package/src/index.ts +23 -10
- package/src/test-helpers/SettingsBuilder.ts +2 -6
- package/examples/force-app/main/default/classes/AnotherInterface.cls +0 -16
- package/examples/force-app/main/default/classes/EscapedAnnotations.cls +0 -5
- package/examples/force-app/main/default/classes/GrandparentClass.cls +0 -5
- package/examples/force-app/main/default/classes/GroupedClass.cls +0 -8
- package/examples/force-app/main/default/classes/InterfaceWithInheritance.cls +0 -1
- package/examples/force-app/main/default/classes/MemberGrouping.cls +0 -17
- package/examples/force-app/main/default/classes/ParentClass.cls +0 -16
- package/examples/force-app/main/default/classes/SampleClass.cls-meta.xml +0 -5
- package/examples/force-app/main/default/classes/SampleClassWithoutModifier.cls +0 -9
- package/examples/force-app/main/default/classes/SampleInterface.cls +0 -16
- package/src/core/settings.ts +0 -56
- /package/examples/{force-app → open-api/force-app}/main/default/classes/ChildClass.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResource.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResourceToSkip.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResourceWithInnerClass.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/SampleRestResourceWithoutApexDocs.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference1.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference2.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference3.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference4.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference5.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference6.cls +0 -0
- /package/examples/{force-app → open-api/force-app}/main/default/restapi/references/Reference7.cls +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openapi-example",
|
|
3
|
+
"scripts": {
|
|
4
|
+
"docs:clean": "rimraf docs",
|
|
5
|
+
"docs:gen": "ts-node ../../src/cli/generate.ts openapi",
|
|
6
|
+
"apexdocs:build": "npm run docs:clean && npm run docs:gen"
|
|
7
|
+
},
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"ts-node": "^10.9.2"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"rimraf": "^5.0.7"
|
|
13
|
+
},
|
|
14
|
+
"apexdocs": {
|
|
15
|
+
"sourceDir": "force-app",
|
|
16
|
+
"title": "Open API Example",
|
|
17
|
+
"namespace": "openapi",
|
|
18
|
+
"apiVersion": "3.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -29,10 +29,15 @@ export default defineMarkdownConfig({
|
|
|
29
29
|
sourceDir: 'force-app',
|
|
30
30
|
scope: ['global', 'public', 'protected', 'private', 'namespaceaccessible'],
|
|
31
31
|
namespace: 'apexdocs',
|
|
32
|
-
|
|
32
|
+
transformReference: (reference) => {
|
|
33
|
+
return {
|
|
34
|
+
// remove the trailing .md
|
|
35
|
+
referencePath: reference.referencePath.replace(/\.md$/, ''),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
transformReferenceGuide: async () => {
|
|
33
39
|
const frontMatter = await loadFileAsync('./docs/index-frontmatter.md');
|
|
34
40
|
return {
|
|
35
|
-
...referenceGuide,
|
|
36
41
|
frontmatter: frontMatter,
|
|
37
42
|
};
|
|
38
43
|
},
|
|
@@ -19,38 +19,38 @@ hero:
|
|
|
19
19
|
|
|
20
20
|
## Miscellaneous
|
|
21
21
|
|
|
22
|
-
### [BaseClass](miscellaneous/BaseClass
|
|
22
|
+
### [BaseClass](miscellaneous/BaseClass)
|
|
23
23
|
|
|
24
|
-
### [MultiInheritanceClass](miscellaneous/MultiInheritanceClass
|
|
24
|
+
### [MultiInheritanceClass](miscellaneous/MultiInheritanceClass)
|
|
25
25
|
|
|
26
|
-
### [ParentInterface](miscellaneous/ParentInterface
|
|
26
|
+
### [ParentInterface](miscellaneous/ParentInterface)
|
|
27
27
|
|
|
28
|
-
### [ReferencedEnum](miscellaneous/ReferencedEnum
|
|
28
|
+
### [ReferencedEnum](miscellaneous/ReferencedEnum)
|
|
29
29
|
|
|
30
|
-
### [SampleException](miscellaneous/SampleException
|
|
30
|
+
### [SampleException](miscellaneous/SampleException)
|
|
31
31
|
|
|
32
32
|
This is a sample exception.
|
|
33
33
|
|
|
34
|
-
### [SampleInterface](miscellaneous/SampleInterface
|
|
34
|
+
### [SampleInterface](miscellaneous/SampleInterface)
|
|
35
35
|
|
|
36
36
|
This is a sample interface
|
|
37
37
|
|
|
38
|
-
### [Url](miscellaneous/Url
|
|
38
|
+
### [Url](miscellaneous/Url)
|
|
39
39
|
|
|
40
40
|
Represents a uniform resource locator (URL) and provides access to parts of the URL.
|
|
41
41
|
Enables access to the base URL used to access your Salesforce org.
|
|
42
42
|
|
|
43
43
|
## Sample Enums
|
|
44
44
|
|
|
45
|
-
### [SampleEnum](sample-enums/SampleEnum
|
|
45
|
+
### [SampleEnum](sample-enums/SampleEnum)
|
|
46
46
|
|
|
47
|
-
This is a sample enum. This references [ReferencedEnum](miscellaneous/ReferencedEnum
|
|
47
|
+
This is a sample enum. This references [ReferencedEnum](miscellaneous/ReferencedEnum) .
|
|
48
48
|
|
|
49
49
|
This description has several lines
|
|
50
50
|
|
|
51
51
|
## SampleGroup
|
|
52
52
|
|
|
53
|
-
### [SampleClass](samplegroup/SampleClass
|
|
53
|
+
### [SampleClass](samplegroup/SampleClass)
|
|
54
54
|
|
|
55
55
|
aliquip ex sunt officia ullamco anim deserunt magna aliquip nisi eiusmod in sit officia veniam ex
|
|
56
|
-
deserunt ea officia exercitation laboris enim in duis quis enim eiusmod eu amet cupidatat.
|
|
56
|
+
**deserunt** ea officia exercitation laboris enim in duis quis enim eiusmod eu amet cupidatat.
|
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
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.
|
|
3
|
+
* **deserunt** ea officia exercitation laboris enim in duis quis enim eiusmod eu amet cupidatat.
|
|
4
4
|
* @group SampleGroup
|
|
5
5
|
* @example
|
|
6
6
|
* SampleClass sample = new SampleClass();
|
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-rc.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",
|
|
@@ -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": {
|
|
@@ -4,7 +4,10 @@ import openApi from './generators/openapi';
|
|
|
4
4
|
import { ApexFileReader } from './apex-file-reader';
|
|
5
5
|
import { DefaultFileSystem } from './file-system';
|
|
6
6
|
import { Logger } from '#utils/logger';
|
|
7
|
-
import { UserDefinedConfig } from '../core/shared/types';
|
|
7
|
+
import { UnparsedSourceFile, UserDefinedConfig, UserDefinedMarkdownConfig } from '../core/shared/types';
|
|
8
|
+
import { pipe } from 'fp-ts/function';
|
|
9
|
+
import * as TE from 'fp-ts/TaskEither';
|
|
10
|
+
import { ReflectionError } from '../core/markdown/reflection/reflect-source';
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Application entry-point to generate documentation out of Apex source files.
|
|
@@ -20,21 +23,50 @@ export class Apexdocs {
|
|
|
20
23
|
const fileBodies = await ApexFileReader.processFiles(
|
|
21
24
|
new DefaultFileSystem(),
|
|
22
25
|
config.sourceDir,
|
|
23
|
-
config.includeMetadata,
|
|
26
|
+
config.targetGenerator === 'markdown' ? config.includeMetadata : false,
|
|
24
27
|
);
|
|
25
28
|
|
|
26
29
|
switch (config.targetGenerator) {
|
|
27
30
|
case 'markdown':
|
|
28
|
-
await
|
|
31
|
+
await generateMarkdownDocumentation(fileBodies, config)();
|
|
29
32
|
break;
|
|
30
33
|
case 'openapi':
|
|
31
|
-
openApi(fileBodies, config);
|
|
34
|
+
await openApi(fileBodies, config);
|
|
32
35
|
break;
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
Logger.logSingle('✔️ Documentation generated successfully!');
|
|
36
37
|
} catch (error) {
|
|
37
|
-
Logger.logSingle(
|
|
38
|
+
Logger.logSingle(`❌ An error occurred while generating the documentation: ${error}`, 'red');
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
|
|
43
|
+
function generateMarkdownDocumentation(fileBodies: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
|
|
44
|
+
return pipe(
|
|
45
|
+
markdown(fileBodies, config),
|
|
46
|
+
TE.map(() => Logger.logSingle('✔️ Documentation generated successfully!')),
|
|
47
|
+
TE.mapLeft((error) => {
|
|
48
|
+
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
|
+
}
|
|
53
|
+
|
|
54
|
+
if (error._tag === 'FileWritingError') {
|
|
55
|
+
Logger.error(error.message);
|
|
56
|
+
Logger.error(error.error);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const errorMessages = [
|
|
61
|
+
'Error(s) occurred while parsing files. Please review the following issues:',
|
|
62
|
+
...error.errors.map(formatReflectionError),
|
|
63
|
+
].join('\n');
|
|
64
|
+
|
|
65
|
+
Logger.error(errorMessages);
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function formatReflectionError(error: ReflectionError) {
|
|
71
|
+
return `Source file: ${error.file}\n${error.message}\n`;
|
|
72
|
+
}
|
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
import { Settings, SettingsConfig } from '../../core/settings';
|
|
2
1
|
import { ApexFileReader } from '../apex-file-reader';
|
|
3
2
|
|
|
4
3
|
describe('File Reader', () => {
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
Settings.build({
|
|
7
|
-
sourceDirectory: '',
|
|
8
|
-
recursive: true,
|
|
9
|
-
scope: [],
|
|
10
|
-
outputDir: '',
|
|
11
|
-
targetGenerator: 'markdown',
|
|
12
|
-
indexOnly: false,
|
|
13
|
-
defaultGroupName: 'Misc',
|
|
14
|
-
sanitizeHtml: true,
|
|
15
|
-
openApiFileName: 'openapi',
|
|
16
|
-
title: 'Classes',
|
|
17
|
-
includeMetadata: false,
|
|
18
|
-
} as SettingsConfig);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
4
|
it('returns an empty list when there are no files in the directory', async () => {
|
|
22
5
|
const result = await ApexFileReader.processFiles(
|
|
23
6
|
{
|
|
@@ -1,21 +1,43 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import * as TE from 'fp-ts/lib/TaskEither';
|
|
3
4
|
import { PageData } from '../core/shared/types';
|
|
5
|
+
import { pipe } from 'fp-ts/function';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
files.forEach((file) => {
|
|
8
|
-
const { outputDocPath, content } = this.getTargetLocation(file, outputDir);
|
|
9
|
-
fs.mkdirSync(path.dirname(outputDocPath), { recursive: true });
|
|
10
|
-
fs.writeFileSync(outputDocPath, content, 'utf8');
|
|
11
|
-
onWriteCallback?.(file);
|
|
12
|
-
});
|
|
13
|
-
}
|
|
7
|
+
const mkdir: (path: fs.PathLike, options: fs.MakeDirectoryOptions) => TE.TaskEither<NodeJS.ErrnoException, void> =
|
|
8
|
+
TE.taskify(fs.mkdir);
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const writeFile: (
|
|
11
|
+
path: fs.PathOrFileDescriptor,
|
|
12
|
+
data: string,
|
|
13
|
+
options?: fs.WriteFileOptions,
|
|
14
|
+
) => TE.TaskEither<NodeJS.ErrnoException, void> = TE.taskify(fs.writeFile);
|
|
15
|
+
|
|
16
|
+
export function writeFiles(files: PageData[], outputDir: string, onWriteCallback?: (file: PageData) => void) {
|
|
17
|
+
return pipe(
|
|
18
|
+
files,
|
|
19
|
+
TE.traverseArray((file) => writeSingle(file, outputDir, onWriteCallback)),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function writeSingle(file: PageData, outputDir: string, onWriteCallback?: (file: PageData) => void) {
|
|
24
|
+
const ensureDirectoryExists = ({ outputDocPath }: PageData) =>
|
|
25
|
+
mkdir(path.dirname(outputDocPath), { recursive: true });
|
|
26
|
+
|
|
27
|
+
const writeContents = (file: PageData) => writeFile(file.outputDocPath, file.content, 'utf8');
|
|
28
|
+
|
|
29
|
+
return pipe(
|
|
30
|
+
resolveTargetLocation(file, outputDir),
|
|
31
|
+
(file) => TE.right(file),
|
|
32
|
+
TE.tapIO(ensureDirectoryExists),
|
|
33
|
+
TE.flatMap(writeContents),
|
|
34
|
+
TE.map(() => onWriteCallback?.(file)),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveTargetLocation(file: PageData, outputDir: string): PageData {
|
|
39
|
+
return {
|
|
40
|
+
...file,
|
|
41
|
+
outputDocPath: path.join(outputDir, file.outputDocPath),
|
|
42
|
+
};
|
|
21
43
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { generateDocs } from '../../core/markdown/generate-docs';
|
|
2
|
-
import { FileWriter } from '../file-writer';
|
|
3
|
-
import { Logger } from '#utils/logger';
|
|
4
2
|
import { pipe } from 'fp-ts/function';
|
|
5
3
|
import {
|
|
6
4
|
PageData,
|
|
@@ -11,10 +9,11 @@ import {
|
|
|
11
9
|
import { referenceGuideTemplate } from '../../core/markdown/templates/reference-guide';
|
|
12
10
|
import * as TE from 'fp-ts/TaskEither';
|
|
13
11
|
import { isSkip } from '../../core/shared/utils';
|
|
14
|
-
import {
|
|
12
|
+
import { writeFiles } from '../file-writer';
|
|
15
13
|
|
|
16
14
|
class FileWritingError {
|
|
17
15
|
readonly _tag = 'FileWritingError';
|
|
16
|
+
|
|
18
17
|
constructor(
|
|
19
18
|
public message: string,
|
|
20
19
|
public error: unknown,
|
|
@@ -25,27 +24,7 @@ export default function generate(bundles: UnparsedSourceFile[], config: UserDefi
|
|
|
25
24
|
return pipe(
|
|
26
25
|
generateDocumentationBundle(bundles, config),
|
|
27
26
|
TE.flatMap((files) => writeFilesToSystem(files, config.targetDir)),
|
|
28
|
-
|
|
29
|
-
if (error._tag === 'HookError') {
|
|
30
|
-
Logger.error('Error(s) occurred while processing hooks. Please review the following issues:');
|
|
31
|
-
Logger.error(error.error);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (error._tag === 'FileWritingError') {
|
|
36
|
-
Logger.error(error.message);
|
|
37
|
-
Logger.error(error.error);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const errorMessages = [
|
|
42
|
-
'Error(s) occurred while parsing files. Please review the following issues:',
|
|
43
|
-
...error.errors.map(formatReflectionError),
|
|
44
|
-
].join('\n');
|
|
45
|
-
|
|
46
|
-
Logger.error(errorMessages);
|
|
47
|
-
}),
|
|
48
|
-
)();
|
|
27
|
+
);
|
|
49
28
|
}
|
|
50
29
|
|
|
51
30
|
function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
|
|
@@ -56,19 +35,11 @@ function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: User
|
|
|
56
35
|
}
|
|
57
36
|
|
|
58
37
|
function writeFilesToSystem(files: PostHookDocumentationBundle, outputDir: string) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return TE.right(undefined);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
return TE.left(new FileWritingError('An error occurred while writing files to the system.', error));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function formatReflectionError(error: ReflectionError) {
|
|
73
|
-
return `Source file: ${error.file}\n${error.message}\n`;
|
|
38
|
+
return pipe(
|
|
39
|
+
[files.referenceGuide, ...files.docs].filter((file) => !isSkip(file)),
|
|
40
|
+
(files) => writeFiles(files as PageData[], outputDir),
|
|
41
|
+
TE.mapLeft((error) => {
|
|
42
|
+
return new FileWritingError('An error occurred while writing files to the system.', error);
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
74
45
|
}
|
|
@@ -2,15 +2,27 @@ import { createManifest } from '../../core/openapi/manifest-factory';
|
|
|
2
2
|
import { RawBodyParser } from '../../core/openapi/parser';
|
|
3
3
|
import { TypesRepository } from '../../core/openapi/types-repository';
|
|
4
4
|
import Transpiler from '../../core/openapi/transpiler';
|
|
5
|
-
import { FileWriter } from '../file-writer';
|
|
6
5
|
import { Logger } from '#utils/logger';
|
|
7
6
|
import ErrorLogger from '#utils/error-logger';
|
|
8
7
|
import { reflect, ReflectionResult } from '@cparra/apex-reflection';
|
|
9
8
|
import Manifest from '../../core/manifest';
|
|
10
9
|
import { PageData, UnparsedSourceFile, UserDefinedOpenApiConfig } from '../../core/shared/types';
|
|
11
10
|
import { OpenApiDocsProcessor } from '../../core/openapi/open-api-docs-processor';
|
|
11
|
+
import { writeFiles } from '../file-writer';
|
|
12
|
+
import { pipe } from 'fp-ts/function';
|
|
13
|
+
import * as TE from 'fp-ts/TaskEither';
|
|
14
|
+
import { OpenApiSettings } from '../../core/openApiSettings';
|
|
15
|
+
|
|
16
|
+
export default async function openApi(fileBodies: UnparsedSourceFile[], config: UserDefinedOpenApiConfig) {
|
|
17
|
+
OpenApiSettings.build({
|
|
18
|
+
sourceDirectory: config.sourceDir,
|
|
19
|
+
outputDir: config.targetDir,
|
|
20
|
+
openApiFileName: config.fileName,
|
|
21
|
+
openApiTitle: config.title,
|
|
22
|
+
namespace: config.namespace,
|
|
23
|
+
version: config.apiVersion,
|
|
24
|
+
});
|
|
12
25
|
|
|
13
|
-
export default function openApi(fileBodies: UnparsedSourceFile[], config: UserDefinedOpenApiConfig) {
|
|
14
26
|
const manifest = createManifest(new RawBodyParser(fileBodies), reflectionWithLogger);
|
|
15
27
|
TypesRepository.getInstance().populateAll(manifest.types);
|
|
16
28
|
const filteredTypes = filterByScopes(manifest);
|
|
@@ -18,11 +30,15 @@ export default function openApi(fileBodies: UnparsedSourceFile[], config: UserDe
|
|
|
18
30
|
Transpiler.generate(filteredTypes, processor);
|
|
19
31
|
const generatedFiles = processor.fileBuilder().files();
|
|
20
32
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
await pipe(
|
|
34
|
+
writeFiles(generatedFiles, config.targetDir, (file: PageData) => {
|
|
35
|
+
Logger.logSingle(`${file.outputDocPath} processed.`, 'green');
|
|
36
|
+
}),
|
|
37
|
+
TE.map(() => Logger.logSingle('✔️ Documentation generated successfully!')),
|
|
38
|
+
TE.mapError((error) => Logger.error(error)),
|
|
39
|
+
)();
|
|
24
40
|
|
|
25
|
-
//
|
|
41
|
+
// Logs any errors that the types might have in their doc comment's error field
|
|
26
42
|
ErrorLogger.logErrors(filteredTypes);
|
|
27
43
|
}
|
|
28
44
|
|
package/src/cli/args.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as yargs from 'yargs';
|
|
|
3
3
|
import { UserDefinedMarkdownConfig } from '../core/shared/types';
|
|
4
4
|
import { TypeScriptLoader } from 'cosmiconfig-typescript-loader';
|
|
5
5
|
import { markdownOptions } from './commands/markdown';
|
|
6
|
+
import { openApiOptions } from './commands/openapi';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Extracts configuration from a configuration file or the package.json
|
|
@@ -26,7 +27,9 @@ function _extractYargs(config?: CosmiconfigResult) {
|
|
|
26
27
|
.command('markdown', 'Generate documentation from Apex classes as a Markdown site.', (yargs) =>
|
|
27
28
|
yargs.options(markdownOptions),
|
|
28
29
|
)
|
|
29
|
-
.command('openapi', 'Generate an OpenApi REST specification from Apex classes.')
|
|
30
|
+
.command('openapi', 'Generate an OpenApi REST specification from Apex classes.', () =>
|
|
31
|
+
yargs.options(openApiOptions),
|
|
32
|
+
)
|
|
30
33
|
.demandCommand()
|
|
31
34
|
.parseSync();
|
|
32
35
|
}
|
|
@@ -30,9 +30,7 @@ export const markdownOptions: { [key: string]: Options } = {
|
|
|
30
30
|
},
|
|
31
31
|
namespace: {
|
|
32
32
|
type: 'string',
|
|
33
|
-
describe:
|
|
34
|
-
'The package namespace, if any. If this value is provided the namespace will be added as a prefix to all of the parsed files. ' +
|
|
35
|
-
"If generating an OpenApi definition, it will be added to the file's Server Url.",
|
|
33
|
+
describe: 'The package namespace, if any. If provided, it will be added to the generated files.',
|
|
36
34
|
},
|
|
37
35
|
sortMembersAlphabetically: {
|
|
38
36
|
type: 'boolean',
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Options } from 'yargs';
|
|
2
|
+
import { defaults } from '../../defaults';
|
|
3
|
+
|
|
4
|
+
export const openApiOptions: { [key: string]: Options } = {
|
|
5
|
+
sourceDir: {
|
|
6
|
+
type: 'string',
|
|
7
|
+
alias: 's',
|
|
8
|
+
demandOption: true,
|
|
9
|
+
describe: 'The directory location which contains your apex .cls classes.',
|
|
10
|
+
},
|
|
11
|
+
targetDir: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
alias: 't',
|
|
14
|
+
default: defaults.targetDir,
|
|
15
|
+
describe: 'The directory location where the OpenApi file will be generated.',
|
|
16
|
+
},
|
|
17
|
+
fileName: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
default: 'openapi',
|
|
20
|
+
describe: 'The name of the OpenApi file to be generated.',
|
|
21
|
+
},
|
|
22
|
+
namespace: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
describe: 'The package namespace, if any. This will be added to the API file Server Url.',
|
|
25
|
+
},
|
|
26
|
+
title: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
default: 'Apex REST API',
|
|
29
|
+
describe: 'The title of the OpenApi file.',
|
|
30
|
+
},
|
|
31
|
+
apiVersion: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
default: '1.0.0',
|
|
34
|
+
describe: 'The version of the OpenApi file.',
|
|
35
|
+
},
|
|
36
|
+
};
|