@cparra/apexdocs 3.0.0-alpha.1 → 3.0.0-alpha.10
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 +511 -293
- package/dist/{defaults-jLXD2y8-.js → defaults-DGKfeZq-.js} +1 -1
- package/dist/index.d.ts +23 -7
- 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/apexdocs.config.ts +1 -1
- package/examples/vitepress/docs/index.md +10 -10
- 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 +4 -4
- package/package.json +2 -3
- package/src/application/Apexdocs.ts +53 -10
- package/src/application/__tests__/apex-file-reader.spec.ts +25 -25
- package/src/application/apex-file-reader.ts +32 -19
- package/src/application/file-system.ts +46 -10
- package/src/application/file-writer.ts +37 -15
- package/src/application/generators/markdown.ts +18 -31
- package/src/application/generators/openapi.ts +12 -8
- package/src/cli/commands/markdown.ts +4 -3
- package/src/core/markdown/__test__/generating-class-docs.spec.ts +3 -5
- package/src/core/markdown/__test__/generating-enum-docs.spec.ts +3 -3
- package/src/core/markdown/__test__/generating-interface-docs.spec.ts +5 -5
- 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/documentables.ts +0 -1
- package/src/core/markdown/adapters/generate-link.ts +82 -0
- package/src/core/markdown/adapters/reference-guide.ts +3 -1
- package/src/core/markdown/adapters/renderable-bundle.ts +5 -22
- package/src/core/markdown/adapters/renderable-to-page-data.ts +2 -2
- package/src/core/markdown/generate-docs.ts +9 -13
- package/src/core/markdown/reflection/reflect-source.ts +109 -31
- package/src/core/openapi/open-api-docs-processor.ts +1 -1
- package/src/core/openapi/openapi-type-file.ts +1 -1
- package/src/core/openapi/parser.ts +1 -15
- package/src/core/parse-apex-metadata.ts +21 -5
- package/src/core/shared/types.d.ts +22 -6
- package/src/defaults.ts +1 -1
- package/src/index.ts +1 -6
- package/src/util/logger.ts +8 -21
- package/dist/defaults-DUwru49Q.js +0 -12
- package/dist/defaults-SH0Rsi5E.js +0 -11
- package/src/core/markdown/reflection/error-handling.ts +0 -37
|
@@ -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
|
+
};
|
|
@@ -13,10 +13,12 @@ export function parsedFilesToReferenceGuide(
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function parsedFileToDocPageReference(config: MarkdownGeneratorConfig, parsedFile: ParsedFile): DocPageReference {
|
|
16
|
+
const path = `${slugify(getTypeGroup(parsedFile.type, config))}/${parsedFile.type.name}.md`;
|
|
16
17
|
return {
|
|
17
18
|
source: parsedFile.source,
|
|
18
19
|
displayName: parsedFile.type.name,
|
|
19
|
-
|
|
20
|
+
outputDocPath: path,
|
|
21
|
+
referencePath: path,
|
|
20
22
|
};
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -1,29 +1,29 @@
|
|
|
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[]>>(
|
|
19
|
-
addToReferenceGuide(referenceFinder, config, references),
|
|
19
|
+
addToReferenceGuide(apply(referenceFinder, '__base__'), config, references),
|
|
20
20
|
{},
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function toRenderables(parsedFiles: ParsedFile[]): Renderable[] {
|
|
25
25
|
return parsedFiles.reduce<Renderable[]>((acc, parsedFile) => {
|
|
26
|
-
const renderable = typeToRenderable(parsedFile, referenceFinder, config);
|
|
26
|
+
const renderable = typeToRenderable(parsedFile, apply(referenceFinder, parsedFile.source.name), config);
|
|
27
27
|
acc.push(renderable);
|
|
28
28
|
return acc;
|
|
29
29
|
}, []);
|
|
@@ -55,23 +55,6 @@ function addToReferenceGuide(
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const linkGenerator = (
|
|
59
|
-
references: Record<string, DocPageReference>,
|
|
60
|
-
documentationRootDir: string,
|
|
61
|
-
referenceName: string,
|
|
62
|
-
): StringOrLink => {
|
|
63
|
-
const reference: DocPageReference | undefined = references[referenceName];
|
|
64
|
-
|
|
65
|
-
return reference
|
|
66
|
-
? // Starting the path with a "/" will ensure the link will always be relative to the root of the site.
|
|
67
|
-
{
|
|
68
|
-
__type: 'link',
|
|
69
|
-
title: reference.displayName,
|
|
70
|
-
url: path.join('/', documentationRootDir, reference.pathFromRoot),
|
|
71
|
-
}
|
|
72
|
-
: referenceName;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
58
|
function getTypeGroup(type: Type, config: MarkdownGeneratorConfig): string {
|
|
76
59
|
const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group');
|
|
77
60
|
return groupAnnotation?.body ?? config.defaultGroupName;
|
|
@@ -14,7 +14,7 @@ export const convertToDocumentationBundle = (
|
|
|
14
14
|
referenceGuide: {
|
|
15
15
|
frontmatter: null,
|
|
16
16
|
content: referencesToReferenceGuideContent(referencesByGroup, referenceGuideTemplate),
|
|
17
|
-
|
|
17
|
+
outputDocPath: 'index.md',
|
|
18
18
|
},
|
|
19
19
|
docs: renderables.map((renderable: Renderable) =>
|
|
20
20
|
renderableToPageData(Object.values(referencesByGroup).flat(), renderable),
|
|
@@ -56,7 +56,7 @@ function renderableToPageData(referenceGuideReference: ReferenceGuideReference[]
|
|
|
56
56
|
name: renderable.name,
|
|
57
57
|
type: renderable.type,
|
|
58
58
|
},
|
|
59
|
-
|
|
59
|
+
outputDocPath: reference!.reference.outputDocPath,
|
|
60
60
|
frontmatter: null,
|
|
61
61
|
content: docContents,
|
|
62
62
|
group: renderable.doc.group ?? defaults.defaultGroupName,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { pipe } from 'fp-ts/function';
|
|
2
|
-
import * as E from 'fp-ts/Either';
|
|
3
2
|
import * as TE from 'fp-ts/TaskEither';
|
|
4
3
|
import yaml from 'js-yaml';
|
|
5
4
|
|
|
@@ -20,8 +19,7 @@ import {
|
|
|
20
19
|
ParsedFile,
|
|
21
20
|
} from '../shared/types';
|
|
22
21
|
import { parsedFilesToRenderableBundle } from './adapters/renderable-bundle';
|
|
23
|
-
import {
|
|
24
|
-
import { checkForReflectionErrors } from './reflection/error-handling';
|
|
22
|
+
import { reflectBundles } from './reflection/reflect-source';
|
|
25
23
|
import { addInheritanceChainToTypes } from './reflection/inheritance-chain-expanion';
|
|
26
24
|
import { addInheritedMembersToTypes } from './reflection/inherited-member-expansion';
|
|
27
25
|
import { convertToDocumentationBundle } from './adapters/renderable-to-page-data';
|
|
@@ -42,7 +40,7 @@ export type MarkdownGeneratorConfig = Pick<
|
|
|
42
40
|
| 'transformDocs'
|
|
43
41
|
| 'transformDocPage'
|
|
44
42
|
| 'transformReference'
|
|
45
|
-
| '
|
|
43
|
+
| 'linkingStrategy'
|
|
46
44
|
> & {
|
|
47
45
|
referenceGuideTemplate: string;
|
|
48
46
|
sortMembersAlphabetically: boolean;
|
|
@@ -63,15 +61,13 @@ export function generateDocs(apexBundles: UnparsedSourceFile[], config: Markdown
|
|
|
63
61
|
|
|
64
62
|
return pipe(
|
|
65
63
|
apexBundles,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
E.bind('references', ({ parsedFiles }) => E.right(convertToReferences(parsedFiles))),
|
|
74
|
-
TE.fromEither,
|
|
64
|
+
reflectBundles,
|
|
65
|
+
TE.map(filterOutOfScope),
|
|
66
|
+
TE.map(addInheritedMembersToTypes),
|
|
67
|
+
TE.map(addInheritanceChainToTypes),
|
|
68
|
+
TE.map(sortTypeMembers),
|
|
69
|
+
TE.bindTo('parsedFiles'),
|
|
70
|
+
TE.bind('references', ({ parsedFiles }) => TE.right(convertToReferences(parsedFiles))),
|
|
75
71
|
TE.flatMap(({ parsedFiles, references }) => transformReferenceHook(config)({ references, parsedFiles })),
|
|
76
72
|
TE.map(({ parsedFiles, references }) => convertToRenderableBundle(parsedFiles, references)),
|
|
77
73
|
TE.map(convertToDocumentationBundleForTemplate),
|
|
@@ -1,45 +1,123 @@
|
|
|
1
1
|
import { ParsedFile, UnparsedSourceFile } from '../../shared/types';
|
|
2
|
+
import * as TE from 'fp-ts/TaskEither';
|
|
2
3
|
import * as E from 'fp-ts/Either';
|
|
3
|
-
import
|
|
4
|
+
import * as T from 'fp-ts/Task';
|
|
5
|
+
import * as A from 'fp-ts/lib/Array';
|
|
6
|
+
import { Annotation, reflect as mirrorReflection, Type } from '@cparra/apex-reflection';
|
|
4
7
|
import { pipe } from 'fp-ts/function';
|
|
5
8
|
import * as O from 'fp-ts/Option';
|
|
6
9
|
import { parseApexMetadata } from '../../parse-apex-metadata';
|
|
7
|
-
import {
|
|
10
|
+
import { ParsingError } from '@cparra/apex-reflection';
|
|
11
|
+
import { apply } from '#utils/fp';
|
|
12
|
+
import { Semigroup } from 'fp-ts/Semigroup';
|
|
8
13
|
|
|
9
|
-
export
|
|
10
|
-
|
|
14
|
+
export class ReflectionErrors {
|
|
15
|
+
readonly _tag = 'ReflectionErrors';
|
|
16
|
+
|
|
17
|
+
constructor(public errors: ReflectionError[]) {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class ReflectionError {
|
|
21
|
+
constructor(
|
|
22
|
+
public file: string,
|
|
23
|
+
public message: string,
|
|
24
|
+
) {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function reflectAsync(rawSource: string): Promise<Type> {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const result = mirrorReflection(rawSource);
|
|
30
|
+
if (result.typeMirror) {
|
|
31
|
+
return resolve(result.typeMirror);
|
|
32
|
+
} else if (result.error) {
|
|
33
|
+
return reject(result.error);
|
|
34
|
+
} else {
|
|
35
|
+
return reject(new Error('Unknown error'));
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function reflectBundles(apexBundles: UnparsedSourceFile[]) {
|
|
41
|
+
const semiGroupReflectionError: Semigroup<ReflectionErrors> = {
|
|
42
|
+
concat: (x, y) => new ReflectionErrors([...x.errors, ...y.errors]),
|
|
43
|
+
};
|
|
44
|
+
const Ap = TE.getApplicativeTaskValidation(T.ApplyPar, semiGroupReflectionError);
|
|
45
|
+
|
|
46
|
+
return pipe(apexBundles, A.traverse(Ap)(reflectBundle));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function reflectBundle(apexBundle: UnparsedSourceFile): TE.TaskEither<ReflectionErrors, ParsedFile> {
|
|
50
|
+
const convertToParsedFile: (typeMirror: Type) => ParsedFile = apply(toParsedFile, apexBundle.filePath);
|
|
51
|
+
const withMetadata = apply(addMetadata, apexBundle.metadataContent);
|
|
52
|
+
|
|
53
|
+
return pipe(apexBundle, reflectAsTask, TE.map(convertToParsedFile), TE.flatMap(withMetadata));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function reflectAsTask(apexBundle: UnparsedSourceFile): TE.TaskEither<ReflectionErrors, Type> {
|
|
57
|
+
return TE.tryCatch(
|
|
58
|
+
() => reflectAsync(apexBundle.content),
|
|
59
|
+
(error) =>
|
|
60
|
+
new ReflectionErrors([new ReflectionError(apexBundle.filePath, (error as ParsingError | Error).message)]),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toParsedFile(filePath: string, typeMirror: Type): ParsedFile {
|
|
65
|
+
return {
|
|
66
|
+
source: {
|
|
67
|
+
filePath: filePath,
|
|
68
|
+
name: typeMirror.name,
|
|
69
|
+
type: typeMirror.type_name,
|
|
70
|
+
},
|
|
71
|
+
type: typeMirror,
|
|
72
|
+
};
|
|
11
73
|
}
|
|
12
74
|
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
75
|
+
function addMetadata(
|
|
76
|
+
rawMetadataContent: string | null,
|
|
77
|
+
parsedFile: ParsedFile,
|
|
78
|
+
): TE.TaskEither<ReflectionErrors, ParsedFile> {
|
|
79
|
+
return TE.fromEither(
|
|
80
|
+
pipe(
|
|
81
|
+
parsedFile.type,
|
|
82
|
+
(type) => addFileMetadataToTypeAnnotation(type, rawMetadataContent),
|
|
83
|
+
E.map((type) => ({ ...parsedFile, type })),
|
|
84
|
+
E.mapLeft((error) => errorToReflectionErrors(error, parsedFile.source.filePath)),
|
|
85
|
+
),
|
|
86
|
+
);
|
|
26
87
|
}
|
|
27
88
|
|
|
28
|
-
function
|
|
89
|
+
function errorToReflectionErrors(error: Error, filePath: string): ReflectionErrors {
|
|
90
|
+
return new ReflectionErrors([new ReflectionError(filePath, error.message)]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function addFileMetadataToTypeAnnotation(type: Type, metadata: string | null): E.Either<Error, Type> {
|
|
94
|
+
const concatAnnotationToType = apply(concatAnnotations, type);
|
|
95
|
+
|
|
29
96
|
return pipe(
|
|
30
97
|
O.fromNullable(metadata),
|
|
31
|
-
O.map(
|
|
32
|
-
|
|
33
|
-
metadataParams.forEach((value, key) => {
|
|
34
|
-
const declaration = `${key}: ${value}`;
|
|
35
|
-
type.annotations.push({
|
|
36
|
-
rawDeclaration: declaration,
|
|
37
|
-
name: declaration,
|
|
38
|
-
type: declaration,
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
return type;
|
|
42
|
-
}),
|
|
43
|
-
O.getOrElse(() => type),
|
|
98
|
+
O.map(concatAnnotationToType),
|
|
99
|
+
O.getOrElse(() => E.right(type)),
|
|
44
100
|
);
|
|
45
101
|
}
|
|
102
|
+
|
|
103
|
+
function concatAnnotations(type: Type, metadataInput: string): E.Either<Error, Type> {
|
|
104
|
+
return pipe(
|
|
105
|
+
metadataInput,
|
|
106
|
+
parseApexMetadata,
|
|
107
|
+
E.map((metadataMap) => ({
|
|
108
|
+
...type,
|
|
109
|
+
annotations: [...type.annotations, ...mapToAnnotations(metadataMap)],
|
|
110
|
+
})),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function mapToAnnotations(metadata: Map<string, string>): Annotation[] {
|
|
115
|
+
return Array.from(metadata.entries()).map(([key, value]) => {
|
|
116
|
+
const declaration = `${key}: ${value}`;
|
|
117
|
+
return {
|
|
118
|
+
name: declaration,
|
|
119
|
+
type: declaration,
|
|
120
|
+
rawDeclaration: declaration,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -25,7 +25,7 @@ export class OpenApiDocsProcessor {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
onProcess(type: Type): void {
|
|
28
|
-
Logger.logSingle(`Processing ${type.name}`,
|
|
28
|
+
Logger.logSingle(`Processing ${type.name}`, 'green');
|
|
29
29
|
|
|
30
30
|
const endpointPath = this.getEndpointPath(type);
|
|
31
31
|
if (!endpointPath) {
|
|
@@ -4,7 +4,7 @@ import { OpenApiPageData } from '../shared/types';
|
|
|
4
4
|
export function createOpenApiFile(fileName: string, openApiModel: OpenApi): OpenApiPageData {
|
|
5
5
|
const content = JSON.stringify({ ...openApiModel, namespace: undefined }, null, 2);
|
|
6
6
|
return {
|
|
7
|
-
|
|
7
|
+
outputDocPath: '',
|
|
8
8
|
content,
|
|
9
9
|
frontmatter: null,
|
|
10
10
|
group: null,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ClassMirror, InterfaceMirror, ReflectionResult, Type } from '@cparra/apex-reflection';
|
|
2
|
-
import { parseApexMetadata } from '../parse-apex-metadata';
|
|
3
2
|
import { Logger } from '#utils/logger';
|
|
4
3
|
import { UnparsedSourceFile } from '../shared/types';
|
|
5
4
|
|
|
@@ -16,20 +15,7 @@ export class RawBodyParser implements TypeParser {
|
|
|
16
15
|
const types = this.typeBundles
|
|
17
16
|
.map((currentBundle) => {
|
|
18
17
|
Logger.log(`Parsing file: ${currentBundle.filePath}`);
|
|
19
|
-
|
|
20
|
-
if (!!result.typeMirror && !!currentBundle.metadataContent) {
|
|
21
|
-
// If successful and there is a metadata file
|
|
22
|
-
const metadataParams = parseApexMetadata(currentBundle.metadataContent);
|
|
23
|
-
metadataParams.forEach((value, key) => {
|
|
24
|
-
const declaration = `${key}: ${value}`;
|
|
25
|
-
result.typeMirror?.annotations.push({
|
|
26
|
-
rawDeclaration: declaration,
|
|
27
|
-
name: declaration,
|
|
28
|
-
type: declaration,
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
18
|
+
return reflect(currentBundle);
|
|
33
19
|
})
|
|
34
20
|
.filter((reflectionResult) => {
|
|
35
21
|
return reflectionResult.typeMirror;
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { XMLParser } from 'fast-xml-parser';
|
|
2
|
+
import * as E from 'fp-ts/Either';
|
|
3
|
+
import { pipe } from 'fp-ts/function';
|
|
4
|
+
|
|
5
|
+
type ApexMetadata = {
|
|
6
|
+
ApexClass: ApexClassMetadata;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ApexClassMetadata = {
|
|
10
|
+
apiVersion: string;
|
|
11
|
+
status?: string;
|
|
12
|
+
};
|
|
2
13
|
|
|
3
14
|
export function parseApexMetadata(input: string) {
|
|
4
|
-
|
|
5
|
-
|
|
15
|
+
return pipe(input, parse, E.map(toMap));
|
|
16
|
+
}
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
function parse(input: string): E.Either<Error, ApexMetadata> {
|
|
19
|
+
return E.tryCatch(() => new XMLParser().parse(input), E.toError);
|
|
20
|
+
}
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
22
|
+
function toMap(metadata: ApexMetadata): Map<string, string> {
|
|
23
|
+
const map = new Map<string, string>();
|
|
24
|
+
map.set('apiVersion', String(metadata.ApexClass.apiVersion));
|
|
25
|
+
if (metadata.ApexClass.status) {
|
|
26
|
+
map.set('status', String(metadata.ApexClass.status));
|
|
11
27
|
}
|
|
12
28
|
|
|
13
29
|
return map;
|
|
@@ -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 = {
|
|
@@ -55,8 +66,13 @@ export type DocPageReference = {
|
|
|
55
66
|
// The name under which the type should be displayed in the documentation.
|
|
56
67
|
// By default, this will match the source.name, but it can be configured by the user.
|
|
57
68
|
displayName: string;
|
|
58
|
-
// The location
|
|
59
|
-
|
|
69
|
+
// The location where the file will be written.
|
|
70
|
+
outputDocPath: string;
|
|
71
|
+
// The path to the file relative to the documentation root directory. This is used when linking to the file.
|
|
72
|
+
// Usually the value will be the same as outputDocPath. However, some site generators may have a different way of
|
|
73
|
+
// organizing the files, so this allows for the flexibility of having a path from linking that is different from
|
|
74
|
+
// the path where the file is written.
|
|
75
|
+
referencePath: string;
|
|
60
76
|
};
|
|
61
77
|
|
|
62
78
|
type Frontmatter = string | Record<string, unknown> | null;
|
|
@@ -64,13 +80,13 @@ type Frontmatter = string | Record<string, unknown> | null;
|
|
|
64
80
|
export type ReferenceGuidePageData = {
|
|
65
81
|
frontmatter: Frontmatter;
|
|
66
82
|
content: string;
|
|
67
|
-
|
|
83
|
+
outputDocPath: string;
|
|
68
84
|
};
|
|
69
85
|
|
|
70
86
|
export type DocPageData = {
|
|
71
87
|
source: SourceFileMetadata;
|
|
72
88
|
group: string | null;
|
|
73
|
-
|
|
89
|
+
outputDocPath: string;
|
|
74
90
|
frontmatter: Frontmatter;
|
|
75
91
|
content: string;
|
|
76
92
|
};
|
|
@@ -100,7 +116,7 @@ export type PostHookDocumentationBundle = {
|
|
|
100
116
|
|
|
101
117
|
type ConfigurableDocPageReference = Omit<DocPageReference, 'source'>;
|
|
102
118
|
|
|
103
|
-
type ConfigurableDocPageData = Omit<DocPageData, 'source' | '
|
|
119
|
+
type ConfigurableDocPageData = Omit<DocPageData, 'source' | 'outputDocPath'>;
|
|
104
120
|
|
|
105
121
|
/**
|
|
106
122
|
* Allows changing where the files are written to.
|
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
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import logUpdate from 'log-update';
|
|
3
|
-
|
|
4
2
|
/**
|
|
5
3
|
* Logs messages to the console.
|
|
6
4
|
*/
|
|
7
5
|
export class Logger {
|
|
8
6
|
static currentFrame = 0;
|
|
9
7
|
|
|
10
|
-
static frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
11
|
-
|
|
12
8
|
/**
|
|
13
9
|
* Logs a message with optional arguments.
|
|
14
10
|
* @param message The message to log.
|
|
@@ -27,31 +23,22 @@ export class Logger {
|
|
|
27
23
|
* @param args Optional arguments.
|
|
28
24
|
*/
|
|
29
25
|
public static error(message: unknown, ...args: string[]) {
|
|
30
|
-
this.logSingle(message,
|
|
26
|
+
this.logSingle(message, 'red');
|
|
31
27
|
args.forEach(() => {
|
|
32
|
-
this.logSingle(message,
|
|
28
|
+
this.logSingle(message, 'red');
|
|
33
29
|
});
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
public static logSingle(text: unknown,
|
|
32
|
+
public static logSingle(text: unknown, color: 'green' | 'red' = 'green') {
|
|
37
33
|
if (this.currentFrame > 9) {
|
|
38
34
|
this.currentFrame = 0;
|
|
39
35
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
logMessage = `${chalk.green(new Date().toLocaleString() + ': ')}${text}\n`;
|
|
44
|
-
} else {
|
|
45
|
-
logMessage = `${chalk.red(new Date().toLocaleString() + ': ')}${text}\n`;
|
|
46
|
-
}
|
|
47
|
-
if (overrideConsole) {
|
|
48
|
-
logUpdate(`${spinner} ${logMessage}`);
|
|
49
|
-
} else {
|
|
50
|
-
process.stdout.write(`${spinner} ${logMessage}`);
|
|
51
|
-
}
|
|
36
|
+
|
|
37
|
+
const logMessage = `${this.getChalkFn(color)(new Date().toLocaleString() + ': ')}${text}\n`;
|
|
38
|
+
process.stdout.write(logMessage);
|
|
52
39
|
}
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
private static getChalkFn(color: 'green' | 'red') {
|
|
42
|
+
return color === 'green' ? chalk.green : chalk.red;
|
|
56
43
|
}
|
|
57
44
|
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as E from 'fp-ts/Either';
|
|
2
|
-
import { ParsedFile } from '../../shared/types';
|
|
3
|
-
import { pipe } from 'fp-ts/function';
|
|
4
|
-
|
|
5
|
-
export class ReflectionErrors {
|
|
6
|
-
readonly _tag = 'ReflectionErrors';
|
|
7
|
-
constructor(public errors: ReflectionError[]) {}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class ReflectionError {
|
|
11
|
-
constructor(
|
|
12
|
-
public file: string,
|
|
13
|
-
public message: string,
|
|
14
|
-
) {}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function checkForReflectionErrors(reflectionResult: E.Either<ReflectionError, ParsedFile>[]) {
|
|
18
|
-
function reduceReflectionResultIntoSingleEither(results: E.Either<ReflectionError, ParsedFile>[]): {
|
|
19
|
-
errors: ReflectionError[];
|
|
20
|
-
parsedFiles: ParsedFile[];
|
|
21
|
-
} {
|
|
22
|
-
return results.reduce<{ errors: ReflectionError[]; parsedFiles: ParsedFile[] }>(
|
|
23
|
-
(acc, result) => {
|
|
24
|
-
E.isLeft(result) ? acc.errors.push(result.left) : acc.parsedFiles.push(result.right);
|
|
25
|
-
return acc;
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
errors: [],
|
|
29
|
-
parsedFiles: [],
|
|
30
|
-
},
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return pipe(reflectionResult, reduceReflectionResultIntoSingleEither, ({ errors, parsedFiles }) =>
|
|
35
|
-
errors.length ? E.left(new ReflectionErrors(errors)) : E.right(parsedFiles),
|
|
36
|
-
);
|
|
37
|
-
}
|