@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
|
@@ -10,30 +10,43 @@ export class ApexFileReader {
|
|
|
10
10
|
/**
|
|
11
11
|
* Reads from .cls files and returns their raw body.
|
|
12
12
|
*/
|
|
13
|
-
static processFiles(
|
|
14
|
-
|
|
13
|
+
static async processFiles(
|
|
14
|
+
fileSystem: FileSystem,
|
|
15
|
+
rootPath: string,
|
|
16
|
+
includeMetadata: boolean,
|
|
17
|
+
): Promise<UnparsedSourceFile[]> {
|
|
18
|
+
const filePaths = await this.getFilePaths(fileSystem, rootPath);
|
|
19
|
+
const apexFilePaths = filePaths.filter((filePath) => this.isApexFile(filePath));
|
|
20
|
+
const filePromises = apexFilePaths.map((filePath) => this.processFile(fileSystem, filePath, includeMetadata));
|
|
21
|
+
return Promise.all(filePromises);
|
|
22
|
+
}
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
directoryContents.
|
|
24
|
+
private static async getFilePaths(fileSystem: FileSystem, rootPath: string): Promise<string[]> {
|
|
25
|
+
const directoryContents = await fileSystem.readDirectory(rootPath);
|
|
26
|
+
const paths: string[] = [];
|
|
27
|
+
for (const filePath of directoryContents) {
|
|
18
28
|
const currentPath = fileSystem.joinPath(rootPath, filePath);
|
|
19
|
-
if (fileSystem.isDirectory(currentPath)) {
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!this.isApexFile(filePath)) {
|
|
24
|
-
return;
|
|
29
|
+
if (await fileSystem.isDirectory(currentPath)) {
|
|
30
|
+
paths.push(...(await this.getFilePaths(fileSystem, currentPath)));
|
|
25
31
|
}
|
|
32
|
+
paths.push(currentPath);
|
|
33
|
+
}
|
|
34
|
+
return paths;
|
|
35
|
+
}
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
private static async processFile(
|
|
38
|
+
fileSystem: FileSystem,
|
|
39
|
+
filePath: string,
|
|
40
|
+
includeMetadata: boolean,
|
|
41
|
+
): Promise<UnparsedSourceFile> {
|
|
42
|
+
const rawTypeContent = await fileSystem.readFile(filePath);
|
|
43
|
+
const metadataPath = `${filePath}-meta.xml`;
|
|
44
|
+
let rawMetadataContent = null;
|
|
45
|
+
if (includeMetadata) {
|
|
46
|
+
rawMetadataContent = fileSystem.exists(metadataPath) ? await fileSystem.readFile(metadataPath) : null;
|
|
47
|
+
}
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
return bundles;
|
|
49
|
+
return { filePath, content: rawTypeContent, metadataContent: rawMetadataContent };
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
private static isApexFile(currentFile: string): boolean {
|
|
@@ -2,25 +2,61 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
4
|
export interface FileSystem {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
readFile: (path: string) => string
|
|
5
|
+
isDirectory: (path: string) => Promise<boolean>;
|
|
6
|
+
readDirectory: (sourceDirectory: string) => Promise<string[]>;
|
|
7
|
+
readFile: (path: string) => Promise<string>;
|
|
8
8
|
joinPath: (...paths: string[]) => string;
|
|
9
9
|
exists: (path: string) => boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function stat(path: string): Promise<fs.Stats> {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
fs.stat(path, (err, stats) => {
|
|
15
|
+
if (err) {
|
|
16
|
+
reject(err);
|
|
17
|
+
} else {
|
|
18
|
+
resolve(stats);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readdir(path: string): Promise<string[]> {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
fs.readdir(path, (err, files) => {
|
|
27
|
+
if (err) {
|
|
28
|
+
reject(err);
|
|
29
|
+
} else {
|
|
30
|
+
resolve(files);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readFile(path: string): Promise<string> {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
fs.readFile(path, (err, data) => {
|
|
39
|
+
if (err) {
|
|
40
|
+
reject(err);
|
|
41
|
+
} else {
|
|
42
|
+
resolve(data.toString());
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
12
48
|
export class DefaultFileSystem implements FileSystem {
|
|
13
|
-
isDirectory(pathToRead: string): boolean {
|
|
14
|
-
|
|
49
|
+
async isDirectory(pathToRead: string): Promise<boolean> {
|
|
50
|
+
const stats = await stat(pathToRead);
|
|
51
|
+
return stats.isDirectory();
|
|
15
52
|
}
|
|
16
53
|
|
|
17
|
-
readDirectory(sourceDirectory: string): string[] {
|
|
18
|
-
return
|
|
54
|
+
readDirectory(sourceDirectory: string): Promise<string[]> {
|
|
55
|
+
return readdir(sourceDirectory);
|
|
19
56
|
}
|
|
20
57
|
|
|
21
|
-
readFile(pathToRead: string): string {
|
|
22
|
-
|
|
23
|
-
return rawFile.toString();
|
|
58
|
+
readFile(pathToRead: string): Promise<string> {
|
|
59
|
+
return readFile(pathToRead);
|
|
24
60
|
}
|
|
25
61
|
|
|
26
62
|
joinPath(...paths: string[]): string {
|
|
@@ -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 { filePath, content } = this.getTargetLocation(file, outputDir);
|
|
9
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
10
|
-
fs.writeFileSync(filePath, 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,
|
|
@@ -8,30 +6,25 @@ import {
|
|
|
8
6
|
UnparsedSourceFile,
|
|
9
7
|
UserDefinedMarkdownConfig,
|
|
10
8
|
} from '../../core/shared/types';
|
|
11
|
-
import { ReflectionError } from '../../core/markdown/reflection/error-handling';
|
|
12
9
|
import { referenceGuideTemplate } from '../../core/markdown/templates/reference-guide';
|
|
13
10
|
import * as TE from 'fp-ts/TaskEither';
|
|
14
11
|
import { isSkip } from '../../core/shared/utils';
|
|
12
|
+
import { writeFiles } from '../file-writer';
|
|
13
|
+
|
|
14
|
+
class FileWritingError {
|
|
15
|
+
readonly _tag = 'FileWritingError';
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
public message: string,
|
|
19
|
+
public error: unknown,
|
|
20
|
+
) {}
|
|
21
|
+
}
|
|
15
22
|
|
|
16
23
|
export default function generate(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
|
|
17
24
|
return pipe(
|
|
18
25
|
generateDocumentationBundle(bundles, config),
|
|
19
|
-
TE.
|
|
20
|
-
|
|
21
|
-
if (error._tag === 'HookError') {
|
|
22
|
-
Logger.error('Error(s) occurred while processing hooks. Please review the following issues:');
|
|
23
|
-
Logger.error(error.error);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const errorMessages = [
|
|
28
|
-
'Error(s) occurred while parsing files. Please review the following issues:',
|
|
29
|
-
...error.errors.map(formatReflectionError),
|
|
30
|
-
].join('\n');
|
|
31
|
-
|
|
32
|
-
Logger.error(errorMessages);
|
|
33
|
-
}),
|
|
34
|
-
)();
|
|
26
|
+
TE.flatMap((files) => writeFilesToSystem(files, config.targetDir)),
|
|
27
|
+
);
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
|
|
@@ -42,17 +35,11 @@ function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: User
|
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
function writeFilesToSystem(files: PostHookDocumentationBundle, outputDir: string) {
|
|
45
|
-
|
|
46
|
-
[files.referenceGuide, ...files.docs]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Logger.logSingle(`${file.filePath} processed.`, false, 'green', false);
|
|
52
|
-
},
|
|
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
|
+
}),
|
|
53
44
|
);
|
|
54
45
|
}
|
|
55
|
-
|
|
56
|
-
function formatReflectionError(error: ReflectionError) {
|
|
57
|
-
return `Source file: ${error.file}\n${error.message}\n`;
|
|
58
|
-
}
|
|
@@ -2,13 +2,15 @@ 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';
|
|
12
14
|
|
|
13
15
|
export default function openApi(fileBodies: UnparsedSourceFile[], config: UserDefinedOpenApiConfig) {
|
|
14
16
|
const manifest = createManifest(new RawBodyParser(fileBodies), reflectionWithLogger);
|
|
@@ -18,11 +20,14 @@ export default function openApi(fileBodies: UnparsedSourceFile[], config: UserDe
|
|
|
18
20
|
Transpiler.generate(filteredTypes, processor);
|
|
19
21
|
const generatedFiles = processor.fileBuilder().files();
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
pipe(
|
|
24
|
+
writeFiles(generatedFiles, config.targetDir, (file: PageData) => {
|
|
25
|
+
Logger.logSingle(`${file.outputDocPath} processed.`, 'green');
|
|
26
|
+
}),
|
|
27
|
+
TE.mapError((error) => Logger.error(error)),
|
|
28
|
+
);
|
|
24
29
|
|
|
25
|
-
//
|
|
30
|
+
// Logs any errors that the types might have in their doc comment's error field
|
|
26
31
|
ErrorLogger.logErrors(filteredTypes);
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -48,9 +53,8 @@ function filterByScopes(manifest: Manifest) {
|
|
|
48
53
|
const filteredLogMessage = `Filtered ${
|
|
49
54
|
manifest.types.length - filteredTypes.length
|
|
50
55
|
} file(s), only keeping classes annotated as @RestResource.`;
|
|
51
|
-
Logger.clear();
|
|
52
56
|
|
|
53
|
-
Logger.logSingle(filteredLogMessage,
|
|
54
|
-
Logger.logSingle(`Creating documentation for ${filteredTypes.length} file(s)`,
|
|
57
|
+
Logger.logSingle(filteredLogMessage, 'green');
|
|
58
|
+
Logger.logSingle(`Creating documentation for ${filteredTypes.length} file(s)`, 'green');
|
|
55
59
|
return filteredTypes;
|
|
56
60
|
}
|
|
@@ -44,9 +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: 'The
|
|
50
|
-
|
|
49
|
+
describe: 'The strategy to use when linking to other documentation pages.',
|
|
50
|
+
choices: ['relative', 'no-link', 'none'],
|
|
51
|
+
default: defaults.linkingStrategy,
|
|
51
52
|
},
|
|
52
53
|
};
|
|
@@ -12,7 +12,7 @@ describe('Generates interface documentation', () => {
|
|
|
12
12
|
|
|
13
13
|
const result = await generateDocs([apexBundleFromRawString(input)])();
|
|
14
14
|
expect(result).documentationBundleHasLength(1);
|
|
15
|
-
assertEither(result, (data) => expect(data.docs[0].
|
|
15
|
+
assertEither(result, (data) => expect(data.docs[0].outputDocPath).toContain('MyClass'));
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
it('returns the type as class', async () => {
|
|
@@ -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 () => {
|
|
@@ -17,7 +17,7 @@ describe('Generates enum documentation', () => {
|
|
|
17
17
|
|
|
18
18
|
const result = await generateDocs([apexBundleFromRawString(input)])();
|
|
19
19
|
expect(result).documentationBundleHasLength(1);
|
|
20
|
-
assertEither(result, (data) => expect(data.docs[0].
|
|
20
|
+
assertEither(result, (data) => expect(data.docs[0].outputDocPath).toContain('MyEnum'));
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it('returns the type as enum', 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 () => {
|
|
@@ -453,7 +451,9 @@ describe('Generates interface documentation', () => {
|
|
|
453
451
|
const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
|
|
454
452
|
expect(result).documentationBundleHasLength(2);
|
|
455
453
|
assertEither(result, (data) =>
|
|
456
|
-
expect(data.docs.find((doc) => doc.
|
|
454
|
+
expect(data.docs.find((doc) => doc.outputDocPath.includes('AnotherInterface'))?.content).toContain(
|
|
455
|
+
'Inherited',
|
|
456
|
+
),
|
|
457
457
|
);
|
|
458
458
|
});
|
|
459
459
|
});
|
|
@@ -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
|
+
});
|
|
@@ -98,7 +98,6 @@ export function adaptDocumentable(
|
|
|
98
98
|
.map((currentAnnotation) => currentAnnotation.body) ?? []
|
|
99
99
|
);
|
|
100
100
|
}
|
|
101
|
-
|
|
102
101
|
return {
|
|
103
102
|
...adaptDescribable(documentable.docComment?.descriptionLines, linkGenerator),
|
|
104
103
|
annotations: documentable.annotations.map((annotation) => annotation.type.toUpperCase()),
|