@cparra/apexdocs 3.0.0-alpha.3 → 3.0.0-alpha.4
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/dist/cli/generate.js +86 -35
- package/dist/defaults-DGKfeZq-.js +13 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +1 -1
- package/examples/markdown/docs/miscellaneous/MultiInheritanceClass.md +1 -1
- package/examples/markdown/docs/miscellaneous/SampleInterface.md +12 -8
- package/examples/markdown/docs/miscellaneous/Url.md +3 -3
- package/examples/markdown/force-app/classes/SampleInterface.cls +4 -0
- package/examples/vitepress/docs/miscellaneous/BaseClass.md +1 -1
- package/package.json +1 -1
- package/src/application/Apexdocs.ts +13 -8
- package/src/cli/commands/markdown.ts +4 -4
- package/src/core/markdown/__test__/generating-class-docs.spec.ts +2 -4
- package/src/core/markdown/__test__/generating-enum-docs.spec.ts +2 -2
- package/src/core/markdown/__test__/generating-interface-docs.spec.ts +2 -4
- package/src/core/markdown/__test__/generating-reference-guide.spec.ts +3 -3
- package/src/core/markdown/__test__/test-helpers.ts +1 -1
- package/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +1 -1
- package/src/core/markdown/adapters/__tests__/link-generator.spec.ts +130 -0
- package/src/core/markdown/adapters/generate-link.ts +82 -0
- package/src/core/markdown/adapters/renderable-bundle.ts +3 -40
- package/src/core/markdown/generate-docs.ts +1 -1
- package/src/core/shared/types.d.ts +12 -1
- package/src/defaults.ts +1 -1
- package/src/index.ts +1 -6
- package/src/util/logger.ts +18 -0
package/dist/cli/generate.js
CHANGED
|
@@ -10,7 +10,7 @@ var apexReflection = require('@cparra/apex-reflection');
|
|
|
10
10
|
var O = require('fp-ts/Option');
|
|
11
11
|
var fastXmlParser = require('fast-xml-parser');
|
|
12
12
|
var Handlebars = require('handlebars');
|
|
13
|
-
var defaults = require('../defaults-
|
|
13
|
+
var defaults = require('../defaults-DGKfeZq-.js');
|
|
14
14
|
var fs = require('fs');
|
|
15
15
|
var chalk = require('chalk');
|
|
16
16
|
var logUpdate = require('log-update');
|
|
@@ -569,8 +569,59 @@ function singleGroup(headingLevel, groupName, adapter, members, linkGenerator) {
|
|
|
569
569
|
};
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
const generateLink = (strategy) => {
|
|
573
|
+
switch (strategy) {
|
|
574
|
+
case "relative":
|
|
575
|
+
return generateRelativeLink;
|
|
576
|
+
case "no-link":
|
|
577
|
+
return generateNoLink;
|
|
578
|
+
case "none":
|
|
579
|
+
return returnReferenceAsIs;
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
const generateRelativeLink = (references, from, referenceName) => {
|
|
583
|
+
function getRelativePath(fromPath, toPath) {
|
|
584
|
+
return path.relative(path.parse(path.join("/", fromPath)).dir, path.join("/", toPath));
|
|
585
|
+
}
|
|
586
|
+
const referenceTo = references[referenceName];
|
|
587
|
+
if (!referenceTo) {
|
|
588
|
+
return referenceName;
|
|
589
|
+
}
|
|
590
|
+
if (referenceTo && from === "__base__") {
|
|
591
|
+
return {
|
|
592
|
+
__type: "link",
|
|
593
|
+
title: referenceTo.displayName,
|
|
594
|
+
url: getRelativePath("", referenceTo.referencePath)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
const referenceFrom = references[from];
|
|
598
|
+
if (!referenceFrom) {
|
|
599
|
+
return referenceTo.displayName;
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
__type: "link",
|
|
603
|
+
title: referenceTo.displayName,
|
|
604
|
+
url: getRelativePath(referenceFrom.referencePath, referenceTo.referencePath)
|
|
605
|
+
};
|
|
606
|
+
};
|
|
607
|
+
const generateNoLink = (references, _from, referenceName) => {
|
|
608
|
+
const referenceTo = references[referenceName];
|
|
609
|
+
return referenceTo ? referenceTo.displayName : referenceName;
|
|
610
|
+
};
|
|
611
|
+
const returnReferenceAsIs = (references, _from, referenceName) => {
|
|
612
|
+
const referenceTo = references[referenceName];
|
|
613
|
+
if (!referenceTo) {
|
|
614
|
+
return referenceName;
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
__type: "link",
|
|
618
|
+
title: referenceTo.displayName,
|
|
619
|
+
url: referenceTo.referencePath
|
|
620
|
+
};
|
|
621
|
+
};
|
|
622
|
+
|
|
572
623
|
function parsedFilesToRenderableBundle(config, parsedFiles, references) {
|
|
573
|
-
const referenceFinder = apply(
|
|
624
|
+
const referenceFinder = apply(generateLink(config.linkingStrategy), references);
|
|
574
625
|
function toReferenceGuide(parsedFiles2) {
|
|
575
626
|
return parsedFiles2.reduce(
|
|
576
627
|
addToReferenceGuide(apply(referenceFinder, "__base__"), config, references),
|
|
@@ -604,28 +655,6 @@ function addToReferenceGuide(findLinkFromHome, config, references) {
|
|
|
604
655
|
return acc;
|
|
605
656
|
};
|
|
606
657
|
}
|
|
607
|
-
const linkGenerator = (references, documentationRootDir, from, referenceName) => {
|
|
608
|
-
const referenceTo = references[referenceName];
|
|
609
|
-
if (referenceTo && from === "__base__") {
|
|
610
|
-
return {
|
|
611
|
-
__type: "link",
|
|
612
|
-
title: referenceTo.displayName,
|
|
613
|
-
url: path__namespace.join(documentationRootDir, referenceTo.referencePath)
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
const referenceFrom = references[from];
|
|
617
|
-
if (!referenceFrom || !referenceTo) {
|
|
618
|
-
return referenceName;
|
|
619
|
-
}
|
|
620
|
-
const fromPath = path__namespace.parse(path__namespace.join("/", documentationRootDir, referenceFrom.referencePath)).dir;
|
|
621
|
-
const toPath = path__namespace.join("/", documentationRootDir, referenceTo.referencePath);
|
|
622
|
-
const relativePath = path__namespace.relative(fromPath, toPath);
|
|
623
|
-
return {
|
|
624
|
-
__type: "link",
|
|
625
|
-
title: referenceTo.displayName,
|
|
626
|
-
url: relativePath
|
|
627
|
-
};
|
|
628
|
-
};
|
|
629
658
|
function getTypeGroup$1(type, config) {
|
|
630
659
|
var _a, _b;
|
|
631
660
|
const groupAnnotation = (_a = type.docComment) == null ? void 0 : _a.annotations.find((annotation) => annotation.name.toLowerCase() === "group");
|
|
@@ -1716,9 +1745,25 @@ class Logger {
|
|
|
1716
1745
|
static clear() {
|
|
1717
1746
|
logUpdate.clear();
|
|
1718
1747
|
}
|
|
1748
|
+
static startSpinner() {
|
|
1749
|
+
if (this.spinnerInterval) {
|
|
1750
|
+
clearInterval(this.spinnerInterval);
|
|
1751
|
+
}
|
|
1752
|
+
this.spinnerInterval = setInterval(() => {
|
|
1753
|
+
this.logSingle("Processing...", true, "green", true);
|
|
1754
|
+
}, 80);
|
|
1755
|
+
}
|
|
1756
|
+
static stopSpinner() {
|
|
1757
|
+
if (this.spinnerInterval) {
|
|
1758
|
+
clearInterval(this.spinnerInterval);
|
|
1759
|
+
this.spinnerInterval = null;
|
|
1760
|
+
this.clear();
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1719
1763
|
}
|
|
1720
1764
|
Logger.currentFrame = 0;
|
|
1721
1765
|
Logger.frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1766
|
+
Logger.spinnerInterval = null;
|
|
1722
1767
|
|
|
1723
1768
|
const referenceGuideTemplate = `
|
|
1724
1769
|
# Apex Reference Guide
|
|
@@ -2845,14 +2890,19 @@ class Apexdocs {
|
|
|
2845
2890
|
static generate(config) {
|
|
2846
2891
|
return __async$1(this, null, function* () {
|
|
2847
2892
|
Logger.logSingle("Initializing...", false);
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2893
|
+
Logger.startSpinner();
|
|
2894
|
+
try {
|
|
2895
|
+
const fileBodies = ApexFileReader.processFiles(new DefaultFileSystem(), config.sourceDir, config.includeMetadata);
|
|
2896
|
+
switch (config.targetGenerator) {
|
|
2897
|
+
case "markdown":
|
|
2898
|
+
yield generate(fileBodies, config);
|
|
2899
|
+
break;
|
|
2900
|
+
case "openapi":
|
|
2901
|
+
openApi(fileBodies, config);
|
|
2902
|
+
break;
|
|
2903
|
+
}
|
|
2904
|
+
} finally {
|
|
2905
|
+
Logger.stopSpinner();
|
|
2856
2906
|
}
|
|
2857
2907
|
});
|
|
2858
2908
|
}
|
|
@@ -2897,10 +2947,11 @@ const markdownOptions = {
|
|
|
2897
2947
|
describe: "Whether to include the file's meta.xml information: Whether it is active and and the API version",
|
|
2898
2948
|
default: defaults.defaults.includeMetadata
|
|
2899
2949
|
},
|
|
2900
|
-
|
|
2950
|
+
linkingStrategy: {
|
|
2901
2951
|
type: "string",
|
|
2902
|
-
describe: "The
|
|
2903
|
-
|
|
2952
|
+
describe: "The strategy to use when linking to other documentation pages.",
|
|
2953
|
+
choices: ["relative", "no-link", "none"],
|
|
2954
|
+
default: defaults.defaults.linkingStrategy
|
|
2904
2955
|
}
|
|
2905
2956
|
};
|
|
2906
2957
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const defaults = {
|
|
4
|
+
targetGenerator: "markdown",
|
|
5
|
+
targetDir: "./docs/",
|
|
6
|
+
scope: ["global"],
|
|
7
|
+
defaultGroupName: "Miscellaneous",
|
|
8
|
+
includeMetadata: false,
|
|
9
|
+
sortMembersAlphabetically: false,
|
|
10
|
+
linkingStrategy: "relative"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
exports.defaults = defaults;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,17 @@ type ConfigurableHooks = {
|
|
|
10
10
|
transformReference: TransformReference;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
type LinkingStrategy =
|
|
14
|
+
// Links will be generated using relative paths.
|
|
15
|
+
| 'relative'
|
|
16
|
+
// No links will be generated.
|
|
17
|
+
// If the reference is found, the display name will be used.
|
|
18
|
+
// Otherwise, the name
|
|
19
|
+
// of the reference itself will be used.
|
|
20
|
+
| 'no-link'
|
|
21
|
+
// No logic will be applied, the reference path will be used as is.
|
|
22
|
+
| 'none';
|
|
23
|
+
|
|
13
24
|
type UserDefinedMarkdownConfig = {
|
|
14
25
|
targetGenerator: 'markdown';
|
|
15
26
|
sourceDir: string;
|
|
@@ -19,7 +30,7 @@ type UserDefinedMarkdownConfig = {
|
|
|
19
30
|
namespace?: string;
|
|
20
31
|
sortMembersAlphabetically: boolean;
|
|
21
32
|
includeMetadata: boolean;
|
|
22
|
-
|
|
33
|
+
linkingStrategy: LinkingStrategy;
|
|
23
34
|
} & Partial<ConfigurableHooks>;
|
|
24
35
|
|
|
25
36
|
type SourceFileMetadata = {
|
|
@@ -99,7 +110,7 @@ type TransformDocPage = (
|
|
|
99
110
|
doc: DocPageData,
|
|
100
111
|
) => Partial<ConfigurableDocPageData> | Promise<Partial<ConfigurableDocPageData>>;
|
|
101
112
|
|
|
102
|
-
type ConfigurableMarkdownConfig = Omit<SetOptional<UserDefinedMarkdownConfig, 'targetDir' | 'scope' | 'defaultGroupName' | 'includeMetadata' | 'sortMembersAlphabetically' | '
|
|
113
|
+
type ConfigurableMarkdownConfig = Omit<SetOptional<UserDefinedMarkdownConfig, 'targetDir' | 'scope' | 'defaultGroupName' | 'includeMetadata' | 'sortMembersAlphabetically' | 'linkingStrategy'>, 'targetGenerator'>;
|
|
103
114
|
declare function defineMarkdownConfig(config: ConfigurableMarkdownConfig): UserDefinedMarkdownConfig;
|
|
104
115
|
declare function skip(): Skip;
|
|
105
116
|
|
package/dist/index.js
CHANGED
|
@@ -6,10 +6,12 @@ This is a sample interface
|
|
|
6
6
|
|
|
7
7
|
**Mermaid**
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TD
|
|
11
|
+
A[SampleInterface] -->|extends| B[ParentInterface]
|
|
12
|
+
B -->|extends| C[GrandParentInterface]
|
|
13
|
+
C -->|extends| D[GreatGrandParentInterface]
|
|
14
|
+
```
|
|
13
15
|
|
|
14
16
|
**Author** John Doe
|
|
15
17
|
|
|
@@ -17,17 +19,19 @@ C -->|extends| D[GreatGrandParentInterface]
|
|
|
17
19
|
|
|
18
20
|
**See** [SampleEnum](../sample-enums/SampleEnum.md)
|
|
19
21
|
|
|
20
|
-
**See** [ReferencedEnum](
|
|
22
|
+
**See** [ReferencedEnum](ReferencedEnum.md)
|
|
21
23
|
|
|
22
24
|
## Namespace
|
|
23
25
|
ns
|
|
24
26
|
|
|
25
27
|
## Example
|
|
26
|
-
|
|
28
|
+
```apex
|
|
29
|
+
SampleInterface sampleInterface = new SampleInterface();
|
|
27
30
|
sampleInterface.sampleMethod();
|
|
31
|
+
```
|
|
28
32
|
|
|
29
33
|
**Extends**
|
|
30
|
-
[ParentInterface](
|
|
34
|
+
[ParentInterface](ParentInterface.md)
|
|
31
35
|
|
|
32
36
|
## Methods
|
|
33
37
|
### `sampleMethod()`
|
|
@@ -62,7 +66,7 @@ public String sampleMethod()
|
|
|
62
66
|
Some return value
|
|
63
67
|
|
|
64
68
|
#### Throws
|
|
65
|
-
[SampleException](
|
|
69
|
+
[SampleException](SampleException.md): This is a sample exception
|
|
66
70
|
|
|
67
71
|
AnotherSampleException: This is another sample exception
|
|
68
72
|
|
|
@@ -115,7 +115,7 @@ global Url(Url context, String spec)
|
|
|
115
115
|
#### Parameters
|
|
116
116
|
| Name | Type | Description |
|
|
117
117
|
|------|------|-------------|
|
|
118
|
-
| context | [Url](
|
|
118
|
+
| context | [Url](Url.md) | The context in which to parse the specification. |
|
|
119
119
|
| spec | String | The string to parse as a URL. |
|
|
120
120
|
|
|
121
121
|
---
|
|
@@ -186,7 +186,7 @@ global static Url getCurrentRequestUrl()
|
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
#### Return Type
|
|
189
|
-
**[Url](
|
|
189
|
+
**[Url](Url.md)**
|
|
190
190
|
|
|
191
191
|
The URL of the entire request.
|
|
192
192
|
|
|
@@ -294,7 +294,7 @@ global static Url getOrgDomainUrl()
|
|
|
294
294
|
```
|
|
295
295
|
|
|
296
296
|
#### Return Type
|
|
297
|
-
**[Url](
|
|
297
|
+
**[Url](Url.md)**
|
|
298
298
|
|
|
299
299
|
getOrgDomainUrl() always returns the login URL for your org, regardless of context. Use that URL when making API calls to your org.
|
|
300
300
|
|
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
* @see SampleEnum
|
|
6
6
|
* @see ReferencedEnum
|
|
7
7
|
* @mermaid
|
|
8
|
+
* ```mermaid
|
|
8
9
|
* graph TD
|
|
9
10
|
* A[SampleInterface] -->|extends| B[ParentInterface]
|
|
10
11
|
* B -->|extends| C[GrandParentInterface]
|
|
11
12
|
* C -->|extends| D[GreatGrandParentInterface]
|
|
13
|
+
* ```
|
|
12
14
|
* @example
|
|
15
|
+
* ```apex
|
|
13
16
|
* SampleInterface sampleInterface = new SampleInterface();
|
|
14
17
|
* sampleInterface.sampleMethod();
|
|
18
|
+
* ```
|
|
15
19
|
*/
|
|
16
20
|
@NamespaceAccessible
|
|
17
21
|
public interface SampleInterface extends ParentInterface {
|
package/package.json
CHANGED
|
@@ -15,16 +15,21 @@ export class Apexdocs {
|
|
|
15
15
|
*/
|
|
16
16
|
static async generate(config: UserDefinedConfig): Promise<void> {
|
|
17
17
|
Logger.logSingle('Initializing...', false);
|
|
18
|
+
Logger.startSpinner();
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
try {
|
|
21
|
+
const fileBodies = ApexFileReader.processFiles(new DefaultFileSystem(), config.sourceDir, config.includeMetadata);
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
switch (config.targetGenerator) {
|
|
24
|
+
case 'markdown':
|
|
25
|
+
await markdown(fileBodies, config);
|
|
26
|
+
break;
|
|
27
|
+
case 'openapi':
|
|
28
|
+
openApi(fileBodies, config);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
} finally {
|
|
32
|
+
Logger.stopSpinner();
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
}
|
|
@@ -44,10 +44,10 @@ export const markdownOptions: { [key: string]: Options } = {
|
|
|
44
44
|
describe: "Whether to include the file's meta.xml information: Whether it is active and and the API version",
|
|
45
45
|
default: defaults.includeMetadata,
|
|
46
46
|
},
|
|
47
|
-
|
|
47
|
+
linkingStrategy: {
|
|
48
48
|
type: 'string',
|
|
49
|
-
describe:
|
|
50
|
-
|
|
51
|
-
default: defaults.
|
|
49
|
+
describe: 'The strategy to use when linking to other documentation pages.',
|
|
50
|
+
choices: ['relative', 'no-link', 'none'],
|
|
51
|
+
default: defaults.linkingStrategy,
|
|
52
52
|
},
|
|
53
53
|
};
|
|
@@ -268,9 +268,7 @@ describe('Generates interface documentation', () => {
|
|
|
268
268
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
269
269
|
expect(result).documentationBundleHasLength(2);
|
|
270
270
|
assertEither(result, (data) =>
|
|
271
|
-
expect(data).firstDocContains(
|
|
272
|
-
'This is a description with a [ClassRef](/miscellaneous/ClassRef.md) reference',
|
|
273
|
-
),
|
|
271
|
+
expect(data).firstDocContains('This is a description with a [ClassRef](ClassRef.md) reference'),
|
|
274
272
|
);
|
|
275
273
|
});
|
|
276
274
|
|
|
@@ -304,7 +302,7 @@ describe('Generates interface documentation', () => {
|
|
|
304
302
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
305
303
|
expect(result).documentationBundleHasLength(2);
|
|
306
304
|
assertEither(result, (data) => expect(data).firstDocContains('See'));
|
|
307
|
-
assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](
|
|
305
|
+
assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](ClassRef.md)'));
|
|
308
306
|
});
|
|
309
307
|
|
|
310
308
|
it('displays sees without links when the reference is not found', async () => {
|
|
@@ -211,7 +211,7 @@ describe('Generates enum documentation', () => {
|
|
|
211
211
|
expect(result).documentationBundleHasLength(2);
|
|
212
212
|
assertEither(result, (data) => expect(data).firstDocContains('Description'));
|
|
213
213
|
assertEither(result, (data) =>
|
|
214
|
-
expect(data).firstDocContains('This is a description with a [EnumRef](
|
|
214
|
+
expect(data).firstDocContains('This is a description with a [EnumRef](EnumRef.md) reference'),
|
|
215
215
|
);
|
|
216
216
|
});
|
|
217
217
|
|
|
@@ -245,7 +245,7 @@ describe('Generates enum documentation', () => {
|
|
|
245
245
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
246
246
|
expect(result).documentationBundleHasLength(2);
|
|
247
247
|
assertEither(result, (data) => expect(data).firstDocContains('See'));
|
|
248
|
-
assertEither(result, (data) => expect(data).firstDocContains('[EnumRef](
|
|
248
|
+
assertEither(result, (data) => expect(data).firstDocContains('[EnumRef](EnumRef.md)'));
|
|
249
249
|
});
|
|
250
250
|
|
|
251
251
|
it('displays sees without links when the reference is not found', async () => {
|
|
@@ -189,9 +189,7 @@ describe('Generates interface documentation', () => {
|
|
|
189
189
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
190
190
|
expect(result).documentationBundleHasLength(2);
|
|
191
191
|
assertEither(result, (data) =>
|
|
192
|
-
expect(data).firstDocContains(
|
|
193
|
-
'This is a description with a [InterfaceRef](/miscellaneous/InterfaceRef.md) reference',
|
|
194
|
-
),
|
|
192
|
+
expect(data).firstDocContains('This is a description with a [InterfaceRef](InterfaceRef.md) reference'),
|
|
195
193
|
);
|
|
196
194
|
});
|
|
197
195
|
|
|
@@ -225,7 +223,7 @@ describe('Generates interface documentation', () => {
|
|
|
225
223
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
226
224
|
expect(result).documentationBundleHasLength(2);
|
|
227
225
|
assertEither(result, (data) => expect(data).firstDocContains('See'));
|
|
228
|
-
assertEither(result, (data) => expect(data).firstDocContains('[InterfaceRef](
|
|
226
|
+
assertEither(result, (data) => expect(data).firstDocContains('[InterfaceRef](InterfaceRef.md)'));
|
|
229
227
|
});
|
|
230
228
|
|
|
231
229
|
it('displays sees without links when the reference is not found', async () => {
|
|
@@ -25,10 +25,10 @@ describe('Generates a Reference Guide', () => {
|
|
|
25
25
|
expect(result).documentationBundleHasLength(2);
|
|
26
26
|
|
|
27
27
|
assertEither(result, (data) =>
|
|
28
|
-
expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyEnum](
|
|
28
|
+
expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyEnum](miscellaneous/MyEnum.md)'),
|
|
29
29
|
);
|
|
30
30
|
assertEither(result, (data) =>
|
|
31
|
-
expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyClass](
|
|
31
|
+
expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyClass](miscellaneous/MyClass.md)'),
|
|
32
32
|
);
|
|
33
33
|
});
|
|
34
34
|
|
|
@@ -174,7 +174,7 @@ describe('Generates a Reference Guide', () => {
|
|
|
174
174
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
175
175
|
expect(result).documentationBundleHasLength(2);
|
|
176
176
|
assertEither(result, (data) =>
|
|
177
|
-
expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('with a [MyClass](
|
|
177
|
+
expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('with a [MyClass](group2/MyClass.md)'),
|
|
178
178
|
);
|
|
179
179
|
});
|
|
180
180
|
});
|
|
@@ -17,7 +17,7 @@ export function generateDocs(apexBundles: UnparsedSourceFile[], config?: Partial
|
|
|
17
17
|
defaultGroupName: 'Miscellaneous',
|
|
18
18
|
sortMembersAlphabetically: true,
|
|
19
19
|
referenceGuideTemplate: referenceGuideTemplate,
|
|
20
|
+
linkingStrategy: 'relative',
|
|
20
21
|
...config,
|
|
21
|
-
documentationRootDir: '',
|
|
22
22
|
});
|
|
23
23
|
}
|
|
@@ -15,7 +15,7 @@ const defaultMarkdownGeneratorConfig: MarkdownGeneratorConfig = {
|
|
|
15
15
|
defaultGroupName: 'Miscellaneous',
|
|
16
16
|
referenceGuideTemplate: '',
|
|
17
17
|
sortMembersAlphabetically: false,
|
|
18
|
-
|
|
18
|
+
linkingStrategy: 'relative',
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
describe('Conversion from InterfaceMirror to InterfaceSource understandable by the templating engine', () => {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { generateLink } from '../generate-link';
|
|
2
|
+
|
|
3
|
+
describe('Generates links', () => {
|
|
4
|
+
describe('relative', () => {
|
|
5
|
+
it('generates relative links from the base when found', () => {
|
|
6
|
+
const references = {
|
|
7
|
+
referenceName: {
|
|
8
|
+
referencePath: 'referencePath',
|
|
9
|
+
displayName: 'displayName',
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
const from = '__base__';
|
|
13
|
+
const referenceName = 'referenceName';
|
|
14
|
+
|
|
15
|
+
const result = generateLink('relative')(references, from, referenceName);
|
|
16
|
+
|
|
17
|
+
expect(result).toEqual({
|
|
18
|
+
__type: 'link',
|
|
19
|
+
title: 'displayName',
|
|
20
|
+
url: 'referencePath',
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns the name of the reference when not found', () => {
|
|
25
|
+
const references = {};
|
|
26
|
+
const from = '__base__';
|
|
27
|
+
const referenceName = 'referenceName';
|
|
28
|
+
|
|
29
|
+
const result = generateLink('relative')(references, from, referenceName);
|
|
30
|
+
|
|
31
|
+
expect(result).toEqual('referenceName');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns a relative path when linking from a file', () => {
|
|
35
|
+
const references = {
|
|
36
|
+
referenceName: {
|
|
37
|
+
referencePath: 'a/referencePath',
|
|
38
|
+
displayName: 'displayName',
|
|
39
|
+
},
|
|
40
|
+
from: {
|
|
41
|
+
referencePath: 'b/fromPath',
|
|
42
|
+
displayName: 'fromName',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const from = 'from';
|
|
46
|
+
const referenceName = 'referenceName';
|
|
47
|
+
|
|
48
|
+
const result = generateLink('relative')(references, from, referenceName);
|
|
49
|
+
|
|
50
|
+
expect(result).toEqual({
|
|
51
|
+
__type: 'link',
|
|
52
|
+
title: 'displayName',
|
|
53
|
+
url: '../a/referencePath',
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('returns the display name when the from reference is not found', () => {
|
|
58
|
+
const references = {
|
|
59
|
+
referenceName: {
|
|
60
|
+
referencePath: 'a/referencePath',
|
|
61
|
+
displayName: 'displayName',
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const from = 'from';
|
|
65
|
+
const referenceName = 'referenceName';
|
|
66
|
+
|
|
67
|
+
const result = generateLink('relative')(references, from, referenceName);
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual('displayName');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('no-link', () => {
|
|
74
|
+
it('returns the name of the reference when the reference is not found', () => {
|
|
75
|
+
const references = {};
|
|
76
|
+
const from = '__base__';
|
|
77
|
+
const referenceName = 'referenceName';
|
|
78
|
+
|
|
79
|
+
const result = generateLink('no-link')(references, from, referenceName);
|
|
80
|
+
|
|
81
|
+
expect(result).toEqual('referenceName');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('returns the display name of the reference when the reference is found', () => {
|
|
85
|
+
const references = {
|
|
86
|
+
referenceName: {
|
|
87
|
+
referencePath: 'referencePath',
|
|
88
|
+
displayName: 'displayName',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
const from = '__base__';
|
|
92
|
+
const referenceName = 'referenceName';
|
|
93
|
+
|
|
94
|
+
const result = generateLink('no-link')(references, from, referenceName);
|
|
95
|
+
|
|
96
|
+
expect(result).toEqual('displayName');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('none', () => {
|
|
101
|
+
it('returns the path as is when the reference is found', () => {
|
|
102
|
+
const references = {
|
|
103
|
+
referenceName: {
|
|
104
|
+
referencePath: 'referencePath',
|
|
105
|
+
displayName: 'displayName',
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const from = '__base__';
|
|
109
|
+
const referenceName = 'referenceName';
|
|
110
|
+
|
|
111
|
+
const result = generateLink('none')(references, from, referenceName);
|
|
112
|
+
|
|
113
|
+
expect(result).toEqual({
|
|
114
|
+
__type: 'link',
|
|
115
|
+
title: 'displayName',
|
|
116
|
+
url: 'referencePath',
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('returns the name of the reference when the reference is not found', () => {
|
|
121
|
+
const references = {};
|
|
122
|
+
const from = '__base__';
|
|
123
|
+
const referenceName = 'referenceName';
|
|
124
|
+
|
|
125
|
+
const result = generateLink('none')(references, from, referenceName);
|
|
126
|
+
|
|
127
|
+
expect(result).toEqual('referenceName');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { StringOrLink } from './types';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { LinkingStrategy } from '../../shared/types';
|
|
4
|
+
|
|
5
|
+
export type LinkingStrategyFn = (
|
|
6
|
+
references: Record<string, { referencePath: string; displayName: string } | undefined>,
|
|
7
|
+
from: string,
|
|
8
|
+
referenceName: string,
|
|
9
|
+
) => StringOrLink;
|
|
10
|
+
|
|
11
|
+
export const generateLink = (strategy: LinkingStrategy): LinkingStrategyFn => {
|
|
12
|
+
switch (strategy) {
|
|
13
|
+
case 'relative':
|
|
14
|
+
return generateRelativeLink;
|
|
15
|
+
case 'no-link':
|
|
16
|
+
return generateNoLink;
|
|
17
|
+
case 'none':
|
|
18
|
+
return returnReferenceAsIs;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const generateRelativeLink = (
|
|
23
|
+
references: Record<string, { referencePath: string; displayName: string } | undefined>,
|
|
24
|
+
from: string, // The name of the file for which the reference is being generated
|
|
25
|
+
referenceName: string,
|
|
26
|
+
): StringOrLink => {
|
|
27
|
+
function getRelativePath(fromPath: string, toPath: string) {
|
|
28
|
+
return path.relative(path.parse(path.join('/', fromPath)).dir, path.join('/', toPath));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const referenceTo = references[referenceName];
|
|
32
|
+
if (!referenceTo) {
|
|
33
|
+
return referenceName;
|
|
34
|
+
}
|
|
35
|
+
// When linking from the base path (e.g. the reference guide/index page), the reference path is the same as the output
|
|
36
|
+
// path.
|
|
37
|
+
if (referenceTo && from === '__base__') {
|
|
38
|
+
return {
|
|
39
|
+
__type: 'link',
|
|
40
|
+
title: referenceTo.displayName,
|
|
41
|
+
url: getRelativePath('', referenceTo.referencePath),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const referenceFrom = references[from];
|
|
46
|
+
|
|
47
|
+
if (!referenceFrom) {
|
|
48
|
+
return referenceTo.displayName;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
__type: 'link',
|
|
53
|
+
title: referenceTo.displayName,
|
|
54
|
+
url: getRelativePath(referenceFrom.referencePath, referenceTo.referencePath),
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const generateNoLink = (
|
|
59
|
+
references: Record<string, { referencePath: string; displayName: string } | undefined>,
|
|
60
|
+
_from: string,
|
|
61
|
+
referenceName: string,
|
|
62
|
+
): StringOrLink => {
|
|
63
|
+
const referenceTo = references[referenceName];
|
|
64
|
+
return referenceTo ? referenceTo.displayName : referenceName;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const returnReferenceAsIs = (
|
|
68
|
+
references: Record<string, { referencePath: string; displayName: string } | undefined>,
|
|
69
|
+
_from: string,
|
|
70
|
+
referenceName: string,
|
|
71
|
+
): StringOrLink => {
|
|
72
|
+
const referenceTo = references[referenceName];
|
|
73
|
+
if (!referenceTo) {
|
|
74
|
+
return referenceName;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
__type: 'link',
|
|
79
|
+
title: referenceTo.displayName,
|
|
80
|
+
url: referenceTo.referencePath,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { DocPageReference, ParsedFile } from '../../shared/types';
|
|
2
|
-
import { Link, ReferenceGuideReference, Renderable, RenderableBundle
|
|
2
|
+
import { Link, ReferenceGuideReference, Renderable, RenderableBundle } from './types';
|
|
3
3
|
import { typeToRenderable } from './apex-types';
|
|
4
4
|
import { adaptDescribable } from './documentables';
|
|
5
5
|
import { MarkdownGeneratorConfig } from '../generate-docs';
|
|
6
6
|
import { apply } from '#utils/fp';
|
|
7
7
|
import { Type } from '@cparra/apex-reflection';
|
|
8
|
-
import
|
|
8
|
+
import { generateLink } from './generate-link';
|
|
9
9
|
|
|
10
10
|
export function parsedFilesToRenderableBundle(
|
|
11
11
|
config: MarkdownGeneratorConfig,
|
|
12
12
|
parsedFiles: ParsedFile[],
|
|
13
13
|
references: Record<string, DocPageReference>,
|
|
14
14
|
): RenderableBundle {
|
|
15
|
-
const referenceFinder = apply(
|
|
15
|
+
const referenceFinder = apply(generateLink(config.linkingStrategy), references);
|
|
16
16
|
|
|
17
17
|
function toReferenceGuide(parsedFiles: ParsedFile[]): Record<string, ReferenceGuideReference[]> {
|
|
18
18
|
return parsedFiles.reduce<Record<string, ReferenceGuideReference[]>>(
|
|
@@ -55,43 +55,6 @@ function addToReferenceGuide(
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const linkGenerator = (
|
|
59
|
-
references: Record<string, DocPageReference>,
|
|
60
|
-
documentationRootDir: string,
|
|
61
|
-
from: string, // The name of the file for which the reference is being generated
|
|
62
|
-
referenceName: string,
|
|
63
|
-
): StringOrLink => {
|
|
64
|
-
const referenceTo: DocPageReference | undefined = references[referenceName];
|
|
65
|
-
// When linking from the base path (e.g. the reference guide/index page), the reference path is the same as the output
|
|
66
|
-
// path.
|
|
67
|
-
if (referenceTo && from === '__base__') {
|
|
68
|
-
return {
|
|
69
|
-
__type: 'link',
|
|
70
|
-
title: referenceTo.displayName,
|
|
71
|
-
url: path.join(documentationRootDir, referenceTo.referencePath),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const referenceFrom: DocPageReference | undefined = references[from];
|
|
76
|
-
|
|
77
|
-
if (!referenceFrom || !referenceTo) {
|
|
78
|
-
return referenceName;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Gets the directory of the file that is being linked from.
|
|
82
|
-
// This is used to calculate the relative path to the file
|
|
83
|
-
// being linked to.
|
|
84
|
-
const fromPath = path.parse(path.join('/', documentationRootDir, referenceFrom.referencePath)).dir;
|
|
85
|
-
const toPath = path.join('/', documentationRootDir, referenceTo.referencePath);
|
|
86
|
-
const relativePath = path.relative(fromPath, toPath);
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
__type: 'link',
|
|
90
|
-
title: referenceTo.displayName,
|
|
91
|
-
url: relativePath,
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
|
|
95
58
|
function getTypeGroup(type: Type, config: MarkdownGeneratorConfig): string {
|
|
96
59
|
const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group');
|
|
97
60
|
return groupAnnotation?.body ?? config.defaultGroupName;
|
|
@@ -12,6 +12,17 @@ export type ConfigurableHooks = {
|
|
|
12
12
|
transformReference: TransformReference;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
type LinkingStrategy =
|
|
16
|
+
// Links will be generated using relative paths.
|
|
17
|
+
| 'relative'
|
|
18
|
+
// No links will be generated.
|
|
19
|
+
// If the reference is found, the display name will be used.
|
|
20
|
+
// Otherwise, the name
|
|
21
|
+
// of the reference itself will be used.
|
|
22
|
+
| 'no-link'
|
|
23
|
+
// No logic will be applied, the reference path will be used as is.
|
|
24
|
+
| 'none';
|
|
25
|
+
|
|
15
26
|
export type UserDefinedMarkdownConfig = {
|
|
16
27
|
targetGenerator: 'markdown';
|
|
17
28
|
sourceDir: string;
|
|
@@ -21,7 +32,7 @@ export type UserDefinedMarkdownConfig = {
|
|
|
21
32
|
namespace?: string;
|
|
22
33
|
sortMembersAlphabetically: boolean;
|
|
23
34
|
includeMetadata: boolean;
|
|
24
|
-
|
|
35
|
+
linkingStrategy: LinkingStrategy;
|
|
25
36
|
} & Partial<ConfigurableHooks>;
|
|
26
37
|
|
|
27
38
|
export type UserDefinedOpenApiConfig = {
|
package/src/defaults.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -12,12 +12,7 @@ import { defaults } from './defaults';
|
|
|
12
12
|
type ConfigurableMarkdownConfig = Omit<
|
|
13
13
|
SetOptional<
|
|
14
14
|
UserDefinedMarkdownConfig,
|
|
15
|
-
| '
|
|
16
|
-
| 'scope'
|
|
17
|
-
| 'defaultGroupName'
|
|
18
|
-
| 'includeMetadata'
|
|
19
|
-
| 'sortMembersAlphabetically'
|
|
20
|
-
| 'documentationRootDir'
|
|
15
|
+
'targetDir' | 'scope' | 'defaultGroupName' | 'includeMetadata' | 'sortMembersAlphabetically' | 'linkingStrategy'
|
|
21
16
|
>,
|
|
22
17
|
'targetGenerator'
|
|
23
18
|
>;
|
package/src/util/logger.ts
CHANGED
|
@@ -8,6 +8,7 @@ export class Logger {
|
|
|
8
8
|
static currentFrame = 0;
|
|
9
9
|
|
|
10
10
|
static frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
11
|
+
static spinnerInterval: NodeJS.Timeout | null = null;
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Logs a message with optional arguments.
|
|
@@ -54,4 +55,21 @@ export class Logger {
|
|
|
54
55
|
public static clear() {
|
|
55
56
|
logUpdate.clear();
|
|
56
57
|
}
|
|
58
|
+
|
|
59
|
+
public static startSpinner() {
|
|
60
|
+
if (this.spinnerInterval) {
|
|
61
|
+
clearInterval(this.spinnerInterval);
|
|
62
|
+
}
|
|
63
|
+
this.spinnerInterval = setInterval(() => {
|
|
64
|
+
this.logSingle('Processing...', true, 'green', true);
|
|
65
|
+
}, 80);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public static stopSpinner() {
|
|
69
|
+
if (this.spinnerInterval) {
|
|
70
|
+
clearInterval(this.spinnerInterval);
|
|
71
|
+
this.spinnerInterval = null;
|
|
72
|
+
this.clear();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
57
75
|
}
|