@cparra/apexdocs 2.25.0-alpha.8 → 3.0.0-alpha.1

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.
Files changed (69) hide show
  1. package/dist/cli/generate.js +167 -180
  2. package/dist/defaults-jLXD2y8-.js +13 -0
  3. package/dist/index.d.ts +53 -14
  4. package/dist/index.js +1 -1
  5. package/examples/{plain-markdown → markdown}/docs/index.md +25 -33
  6. package/examples/{plain-markdown/docs/Miscellaneous/ns.BaseClass.md → markdown/docs/miscellaneous/BaseClass.md} +1 -1
  7. package/examples/{plain-markdown/docs/Miscellaneous/ns.MultiInheritanceClass.md → markdown/docs/miscellaneous/MultiInheritanceClass.md} +5 -6
  8. package/examples/{plain-markdown/docs/Miscellaneous/ns.SampleException.md → markdown/docs/miscellaneous/SampleException.md} +3 -4
  9. package/examples/{plain-markdown/docs/Miscellaneous/ns.SampleInterface.md → markdown/docs/miscellaneous/SampleInterface.md} +22 -29
  10. package/examples/{plain-markdown/docs/Miscellaneous/ns.Url.md → markdown/docs/miscellaneous/Url.md} +32 -43
  11. package/examples/markdown/docs/sample-enums/SampleEnum.md +36 -0
  12. package/examples/{plain-markdown/docs/SampleGroup/ns.SampleClass.md → markdown/docs/samplegroup/SampleClass.md} +8 -11
  13. package/examples/vitepress/apexdocs.config.ts +1 -3
  14. package/examples/vitepress/docs/.vitepress/sidebar.json +18 -18
  15. package/examples/vitepress/docs/index.md +10 -10
  16. package/examples/vitepress/docs/{Miscellaneous/apexdocs.BaseClass.md → miscellaneous/BaseClass.md} +1 -1
  17. package/examples/vitepress/docs/{Miscellaneous/apexdocs.MultiInheritanceClass.md → miscellaneous/MultiInheritanceClass.md} +2 -2
  18. package/examples/vitepress/docs/{Miscellaneous/apexdocs.SampleException.md → miscellaneous/SampleException.md} +1 -1
  19. package/examples/vitepress/docs/{Miscellaneous/apexdocs.SampleInterface.md → miscellaneous/SampleInterface.md} +6 -6
  20. package/examples/vitepress/docs/{Miscellaneous/apexdocs.Url.md → miscellaneous/Url.md} +3 -3
  21. package/examples/vitepress/docs/{Sample-Enums/apexdocs.SampleEnum.md → sample-enums/SampleEnum.md} +3 -3
  22. package/examples/vitepress/docs/{SampleGroup/apexdocs.SampleClass.md → samplegroup/SampleClass.md} +4 -4
  23. package/package.json +1 -1
  24. package/src/application/apex-file-reader.ts +3 -3
  25. package/src/application/file-writer.ts +5 -9
  26. package/src/application/generators/markdown.ts +9 -4
  27. package/src/application/generators/openapi.ts +4 -4
  28. package/src/cli/args.ts +1 -1
  29. package/src/cli/commands/markdown.ts +6 -12
  30. package/src/cli/generate.ts +3 -3
  31. package/src/core/markdown/__test__/generating-class-docs.spec.ts +5 -3
  32. package/src/core/markdown/__test__/generating-enum-docs.spec.ts +3 -3
  33. package/src/core/markdown/__test__/generating-interface-docs.spec.ts +5 -3
  34. package/src/core/markdown/__test__/generating-reference-guide.spec.ts +3 -7
  35. package/src/core/markdown/__test__/test-helpers.ts +4 -3
  36. package/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +41 -5
  37. package/src/core/markdown/adapters/apex-types.ts +3 -2
  38. package/src/core/markdown/adapters/reference-guide.ts +35 -0
  39. package/src/core/markdown/adapters/renderable-bundle.ts +52 -118
  40. package/src/core/markdown/adapters/renderable-to-page-data.ts +12 -15
  41. package/src/core/markdown/adapters/types.d.ts +4 -6
  42. package/src/core/markdown/generate-docs.ts +100 -42
  43. package/src/core/markdown/reflection/inheritance-chain-expanion.ts +1 -1
  44. package/src/core/markdown/reflection/reflect-source.ts +8 -4
  45. package/src/core/openapi/manifest-factory.ts +2 -2
  46. package/src/core/openapi/openapi-type-file.ts +1 -3
  47. package/src/core/openapi/parser.ts +4 -4
  48. package/src/core/shared/types.d.ts +53 -14
  49. package/src/defaults.ts +1 -0
  50. package/src/index.ts +8 -2
  51. package/examples/plain-markdown/docs/Sample-Enums/ns.SampleEnum.md +0 -38
  52. /package/examples/{plain-markdown → markdown}/.forceignore +0 -0
  53. /package/examples/{plain-markdown → markdown}/config/project-scratch-def.json +0 -0
  54. /package/examples/{plain-markdown/docs/Miscellaneous/ns.ParentInterface.md → markdown/docs/miscellaneous/ParentInterface.md} +0 -0
  55. /package/examples/{plain-markdown/docs/Miscellaneous/ns.ReferencedEnum.md → markdown/docs/miscellaneous/ReferencedEnum.md} +0 -0
  56. /package/examples/{plain-markdown → markdown}/force-app/classes/BaseClass.cls +0 -0
  57. /package/examples/{plain-markdown → markdown}/force-app/classes/MultiInheritanceClass.cls +0 -0
  58. /package/examples/{plain-markdown → markdown}/force-app/classes/ParentInterface.cls +0 -0
  59. /package/examples/{plain-markdown → markdown}/force-app/classes/ReferencedEnum.cls +0 -0
  60. /package/examples/{plain-markdown → markdown}/force-app/classes/SampleClass.cls +0 -0
  61. /package/examples/{plain-markdown → markdown}/force-app/classes/SampleEnum.cls +0 -0
  62. /package/examples/{plain-markdown → markdown}/force-app/classes/SampleException.cls +0 -0
  63. /package/examples/{plain-markdown → markdown}/force-app/classes/SampleInterface.cls +0 -0
  64. /package/examples/{plain-markdown → markdown}/force-app/classes/Url.cls +0 -0
  65. /package/examples/{plain-markdown → markdown}/package-lock.json +0 -0
  66. /package/examples/{plain-markdown → markdown}/package.json +0 -0
  67. /package/examples/{plain-markdown → markdown}/sfdx-project.json +0 -0
  68. /package/examples/vitepress/docs/{Miscellaneous/apexdocs.ParentInterface.md → miscellaneous/ParentInterface.md} +0 -0
  69. /package/examples/vitepress/docs/{Miscellaneous/apexdocs.ReferencedEnum.md → miscellaneous/ReferencedEnum.md} +0 -0
@@ -1,8 +1,8 @@
1
- import { SourceFile } from '../../shared/types';
1
+ import { UnparsedSourceFile } from '../../shared/types';
2
2
  import { generateDocs as gen, MarkdownGeneratorConfig } from '../generate-docs';
3
3
  import { referenceGuideTemplate } from '../templates/reference-guide';
4
4
 
5
- export function apexBundleFromRawString(raw: string, rawMetadata?: string): SourceFile {
5
+ export function apexBundleFromRawString(raw: string, rawMetadata?: string): UnparsedSourceFile {
6
6
  return {
7
7
  filePath: 'test.cls',
8
8
  content: raw,
@@ -10,7 +10,7 @@ export function apexBundleFromRawString(raw: string, rawMetadata?: string): Sour
10
10
  };
11
11
  }
12
12
 
13
- export function generateDocs(apexBundles: SourceFile[], config?: Partial<MarkdownGeneratorConfig>) {
13
+ export function generateDocs(apexBundles: UnparsedSourceFile[], config?: Partial<MarkdownGeneratorConfig>) {
14
14
  return gen(apexBundles, {
15
15
  targetDir: 'target',
16
16
  scope: ['global', 'public'],
@@ -18,5 +18,6 @@ export function generateDocs(apexBundles: SourceFile[], config?: Partial<Markdow
18
18
  sortMembersAlphabetically: true,
19
19
  referenceGuideTemplate: referenceGuideTemplate,
20
20
  ...config,
21
+ documentationRootDir: '',
21
22
  });
22
23
  }
@@ -15,13 +15,21 @@ const defaultMarkdownGeneratorConfig: MarkdownGeneratorConfig = {
15
15
  defaultGroupName: 'Miscellaneous',
16
16
  referenceGuideTemplate: '',
17
17
  sortMembersAlphabetically: false,
18
+ documentationRootDir: '',
18
19
  };
19
20
 
20
21
  describe('Conversion from InterfaceMirror to InterfaceSource understandable by the templating engine', () => {
21
22
  it('converts the name', () => {
22
23
  const interfaceMirror = new InterfaceMirrorBuilder().withName('SampleInterface').build();
23
24
  const interfaceSource = typeToRenderable(
24
- { filePath: '', type: interfaceMirror },
25
+ {
26
+ source: {
27
+ filePath: '',
28
+ type: 'interface',
29
+ name: 'SampleInterface',
30
+ },
31
+ type: interfaceMirror,
32
+ },
25
33
  linkGenerator,
26
34
  defaultMarkdownGeneratorConfig,
27
35
  );
@@ -32,7 +40,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
32
40
  it('converts the access modifier', () => {
33
41
  const interfaceMirror = new InterfaceMirrorBuilder().build();
34
42
  const interfaceSource = typeToRenderable(
35
- { filePath: '', type: interfaceMirror },
43
+ {
44
+ source: {
45
+ filePath: '',
46
+ type: 'interface',
47
+ name: 'SampleInterface',
48
+ },
49
+ type: interfaceMirror,
50
+ },
36
51
  linkGenerator,
37
52
  defaultMarkdownGeneratorConfig,
38
53
  );
@@ -45,7 +60,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
45
60
  .addAnnotation(new AnnotationBuilder().withName('MyAnnotation').build())
46
61
  .build();
47
62
  const interfaceSource = typeToRenderable(
48
- { filePath: '', type: interfaceMirror },
63
+ {
64
+ source: {
65
+ filePath: '',
66
+ type: 'interface',
67
+ name: 'SampleInterface',
68
+ },
69
+ type: interfaceMirror,
70
+ },
49
71
  linkGenerator,
50
72
  defaultMarkdownGeneratorConfig,
51
73
  );
@@ -67,7 +89,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
67
89
  .build();
68
90
 
69
91
  const interfaceSource = typeToRenderable(
70
- { filePath: '', type: interfaceMirror },
92
+ {
93
+ source: {
94
+ filePath: '',
95
+ type: 'interface',
96
+ name: 'SampleInterface',
97
+ },
98
+ type: interfaceMirror,
99
+ },
71
100
  linkGenerator,
72
101
  defaultMarkdownGeneratorConfig,
73
102
  );
@@ -99,7 +128,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
99
128
  .build();
100
129
 
101
130
  const interfaceSource = typeToRenderable(
102
- { filePath: '', type: interfaceMirror },
131
+ {
132
+ source: {
133
+ filePath: '',
134
+ type: 'interface',
135
+ name: 'SampleInterface',
136
+ },
137
+ type: interfaceMirror,
138
+ },
103
139
  linkGenerator,
104
140
  defaultMarkdownGeneratorConfig,
105
141
  );
@@ -15,6 +15,7 @@ import { adaptDescribable, adaptDocumentable } from './documentables';
15
15
  import { adaptConstructor, adaptMethod } from './methods-and-constructors';
16
16
  import { adaptFieldOrProperty } from './fields-and-properties';
17
17
  import { MarkdownGeneratorConfig } from '../generate-docs';
18
+ import { SourceFileMetadata } from '../../shared/types';
18
19
 
19
20
  type GetReturnRenderable<T extends Type> = T extends InterfaceMirror
20
21
  ? RenderableInterface
@@ -23,7 +24,7 @@ type GetReturnRenderable<T extends Type> = T extends InterfaceMirror
23
24
  : RenderableEnum;
24
25
 
25
26
  export function typeToRenderable<T extends Type>(
26
- parsedFile: { filePath: string; type: T },
27
+ parsedFile: { source: SourceFileMetadata; type: T },
27
28
  linkGenerator: GetRenderableContentByTypeName,
28
29
  config: MarkdownGeneratorConfig,
29
30
  ): GetReturnRenderable<T> & { filePath: string; namespace?: string } {
@@ -41,7 +42,7 @@ export function typeToRenderable<T extends Type>(
41
42
 
42
43
  return {
43
44
  ...(getRenderable() as GetReturnRenderable<T>),
44
- filePath: parsedFile.filePath,
45
+ filePath: parsedFile.source.filePath,
45
46
  namespace: config.namespace,
46
47
  };
47
48
  }
@@ -0,0 +1,35 @@
1
+ import { MarkdownGeneratorConfig } from '../generate-docs';
2
+ import { DocPageReference, ParsedFile } from '../../shared/types';
3
+ import { Type } from '@cparra/apex-reflection';
4
+
5
+ export function parsedFilesToReferenceGuide(
6
+ config: MarkdownGeneratorConfig,
7
+ parsedFiles: ParsedFile[],
8
+ ): Record<string, DocPageReference> {
9
+ return parsedFiles.reduce<Record<string, DocPageReference>>((acc, parsedFile) => {
10
+ acc[parsedFile.type.name] = parsedFileToDocPageReference(config, parsedFile);
11
+ return acc;
12
+ }, {});
13
+ }
14
+
15
+ function parsedFileToDocPageReference(config: MarkdownGeneratorConfig, parsedFile: ParsedFile): DocPageReference {
16
+ return {
17
+ source: parsedFile.source,
18
+ displayName: parsedFile.type.name,
19
+ pathFromRoot: `${slugify(getTypeGroup(parsedFile.type, config))}/${parsedFile.type.name}.md`,
20
+ };
21
+ }
22
+
23
+ function getTypeGroup(type: Type, config: MarkdownGeneratorConfig): string {
24
+ const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group');
25
+ return groupAnnotation?.body ?? config.defaultGroupName;
26
+ }
27
+
28
+ function slugify(text: string): string {
29
+ return text
30
+ .toLowerCase()
31
+ .replace(/[^a-z0-9\s-]/g, '') // Remove non-alphanumeric characters except spaces and hyphens
32
+ .trim()
33
+ .replace(/\s+/g, '-') // Replace spaces with hyphens
34
+ .replace(/-+/g, '-'); // Replace multiple hyphens with a single hyphen
35
+ }
@@ -1,144 +1,78 @@
1
- import { ParsedFile } from '../../shared/types';
2
- import { Link, RenderableBundle, StringOrLink } from './types';
1
+ import { DocPageReference, ParsedFile } from '../../shared/types';
2
+ import { Link, ReferenceGuideReference, Renderable, RenderableBundle, StringOrLink } from './types';
3
3
  import { typeToRenderable } from './apex-types';
4
4
  import { adaptDescribable } from './documentables';
5
- import { Type } from '@cparra/apex-reflection';
6
5
  import { MarkdownGeneratorConfig } from '../generate-docs';
6
+ import { apply } from '#utils/fp';
7
+ import { Type } from '@cparra/apex-reflection';
8
+ import * as path from 'path';
7
9
 
8
10
  export function parsedFilesToRenderableBundle(
9
11
  config: MarkdownGeneratorConfig,
10
12
  parsedFiles: ParsedFile[],
13
+ references: Record<string, DocPageReference>,
11
14
  ): RenderableBundle {
12
- return parsedFiles.reduce<RenderableBundle>(
13
- (acc, parsedFile) => {
14
- const renderable = typeToRenderable(
15
- parsedFile,
16
- (referenceName) => {
17
- return linkFromTypeNameGenerator(
18
- parsedFile.type,
19
- parsedFiles.map((file) => file.type),
20
- referenceName,
21
- config,
22
- );
23
- },
24
- config,
25
- );
26
- acc.renderables.push(renderable);
15
+ const referenceFinder = apply(linkGenerator, references, config.documentationRootDir);
27
16
 
28
- const descriptionLines = parsedFile.type.docComment?.descriptionLines;
29
- const reference = {
30
- typeName: parsedFile.type.name,
31
- directory: getDirectoryFromRoot(config, parsedFile.type),
32
- title: getLinkFromRoot(config, parsedFile.type),
33
- description: adaptDescribable(descriptionLines, (referenceName) =>
34
- getPossibleLinkFromRoot(
35
- config,
36
- referenceName,
37
- findType(
38
- parsedFiles.map((file) => file.type),
39
- referenceName,
40
- ),
41
- ),
42
- ).description,
43
- };
44
-
45
- const group = getTypeGroup(parsedFile.type, config);
46
- if (!acc.references[group]) {
47
- acc.references[group] = [];
48
- }
49
- acc.references[group].push(reference);
17
+ function toReferenceGuide(parsedFiles: ParsedFile[]): Record<string, ReferenceGuideReference[]> {
18
+ return parsedFiles.reduce<Record<string, ReferenceGuideReference[]>>(
19
+ addToReferenceGuide(referenceFinder, config, references),
20
+ {},
21
+ );
22
+ }
50
23
 
24
+ function toRenderables(parsedFiles: ParsedFile[]): Renderable[] {
25
+ return parsedFiles.reduce<Renderable[]>((acc, parsedFile) => {
26
+ const renderable = typeToRenderable(parsedFile, referenceFinder, config);
27
+ acc.push(renderable);
51
28
  return acc;
52
- },
53
- {
54
- references: {},
55
- renderables: [],
56
- },
57
- );
58
- }
59
-
60
- function linkFromTypeNameGenerator(
61
- typeBeingDocumented: Type,
62
- repository: Type[],
63
- referenceName: string,
64
- config: MarkdownGeneratorConfig,
65
- ): StringOrLink {
66
- const type = findType(repository, referenceName);
67
- if (!type) {
68
- // If the type is not found, we return the type name as a string.
69
- return referenceName;
29
+ }, []);
70
30
  }
71
31
 
72
- const [fullClassName, fileLink] = getFileLinkTuple(typeBeingDocumented, type, config);
73
32
  return {
74
- __type: 'link',
75
- title: fullClassName,
76
- url: fileLink,
33
+ referencesByGroup: toReferenceGuide(parsedFiles),
34
+ renderables: toRenderables(parsedFiles),
77
35
  };
78
36
  }
79
37
 
80
- function getPossibleLinkFromRoot(config: MarkdownGeneratorConfig, fallback: string, type?: Type): StringOrLink {
81
- if (!type) {
82
- return fallback;
83
- }
84
- const namespacePrefix = config.namespace ? `${config.namespace}.` : '';
85
- const title = `${namespacePrefix}${type.name}`;
86
- return {
87
- __type: 'link',
88
- title: title,
89
- url: `${getDirectoryFromRoot(config, type)}/${title}.md`,
90
- };
91
- }
92
-
93
- function getDirectoryFromRoot(config: MarkdownGeneratorConfig, type?: Type): string {
94
- if (!type) {
95
- return '';
96
- }
97
- return `./${getSanitizedGroup(type, config)}`;
98
- }
38
+ function addToReferenceGuide(
39
+ findLinkFromHome: (referenceName: string) => string | Link,
40
+ config: MarkdownGeneratorConfig,
41
+ references: Record<string, DocPageReference>,
42
+ ) {
43
+ return (acc: Record<string, ReferenceGuideReference[]>, parsedFile: ParsedFile) => {
44
+ const group: string = getTypeGroup(parsedFile.type, config);
45
+ if (!acc[group]) {
46
+ acc[group] = [];
47
+ }
48
+ acc[group].push({
49
+ reference: references[parsedFile.type.name],
50
+ title: findLinkFromHome(parsedFile.type.name) as Link,
51
+ description: adaptDescribable(parsedFile.type.docComment?.descriptionLines, findLinkFromHome).description ?? null,
52
+ });
99
53
 
100
- function findType(repository: Type[], referenceName: string) {
101
- return repository.find((currentType: Type) => currentType.name.toLowerCase() === referenceName.toLowerCase());
54
+ return acc;
55
+ };
102
56
  }
103
57
 
104
- function getFileLinkTuple(
105
- typeBeingDocumented: Type,
106
- referencedType: Type,
107
- config: MarkdownGeneratorConfig,
108
- ): [string, string] {
109
- const namespacePrefix = config.namespace ? `${config.namespace}.` : '';
110
- const directoryRoot = `${getDirectoryRoot(typeBeingDocumented, referencedType, config)}`;
111
- // TODO: Instead of adding a "." to the name when there is a namespace, maybe we want to create a folder for everything
112
- // within that namespace and put the files in there.
113
- const fullClassName = `${namespacePrefix}${referencedType.name}`;
114
- return [fullClassName, `${directoryRoot}${fullClassName}.md`];
115
- }
58
+ const linkGenerator = (
59
+ references: Record<string, DocPageReference>,
60
+ documentationRootDir: string,
61
+ referenceName: string,
62
+ ): StringOrLink => {
63
+ const reference: DocPageReference | undefined = references[referenceName];
116
64
 
117
- function getDirectoryRoot(typeBeingDocumented: Type, referencedType: Type, config: MarkdownGeneratorConfig) {
118
- if (getTypeGroup(typeBeingDocumented, config) === getTypeGroup(referencedType, config)) {
119
- // If the types the same groups then we simply link directly to that file
120
- return './';
121
- } else {
122
- // If the types have different groups, then we have to go up a directory
123
- return `../${getSanitizedGroup(referencedType, config)}/`;
124
- }
125
- }
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
+ };
126
74
 
127
75
  function getTypeGroup(type: Type, config: MarkdownGeneratorConfig): string {
128
76
  const groupAnnotation = type.docComment?.annotations.find((annotation) => annotation.name.toLowerCase() === 'group');
129
77
  return groupAnnotation?.body ?? config.defaultGroupName;
130
78
  }
131
-
132
- function getSanitizedGroup(classModel: Type, config: MarkdownGeneratorConfig) {
133
- return getTypeGroup(classModel, config).replace(/ /g, '-').replace('.', '');
134
- }
135
-
136
- function getLinkFromRoot(config: MarkdownGeneratorConfig, type: Type): Link {
137
- const namespacePrefix = config.namespace ? `${config.namespace}.` : '';
138
- const title = `${namespacePrefix}${type.name}`;
139
- return {
140
- __type: 'link',
141
- title: title,
142
- url: `${getDirectoryFromRoot(config, type)}/${title}.md`,
143
- };
144
- }
@@ -5,19 +5,20 @@ import { CompilationRequest, Template } from '../templates/template';
5
5
  import { enumMarkdownTemplate } from '../templates/enum-template';
6
6
  import { interfaceMarkdownTemplate } from '../templates/interface-template';
7
7
  import { classMarkdownTemplate } from '../templates/class-template';
8
+ import { defaults } from '../../../defaults';
8
9
 
9
10
  export const convertToDocumentationBundle = (
10
11
  referenceGuideTemplate: string,
11
- { references, renderables }: RenderableBundle,
12
+ { referencesByGroup, renderables }: RenderableBundle,
12
13
  ): DocumentationBundle => ({
13
14
  referenceGuide: {
14
- directory: '',
15
15
  frontmatter: null,
16
- content: referencesToReferenceGuideContent(references, referenceGuideTemplate),
17
- fileExtension: 'md',
18
- fileName: 'index',
16
+ content: referencesToReferenceGuideContent(referencesByGroup, referenceGuideTemplate),
17
+ filePath: 'index.md',
19
18
  },
20
- docs: renderables.map((renderable: Renderable) => renderableToPageData(Object.values(references).flat(), renderable)),
19
+ docs: renderables.map((renderable: Renderable) =>
20
+ renderableToPageData(Object.values(referencesByGroup).flat(), renderable),
21
+ ),
21
22
  });
22
23
 
23
24
  function referencesToReferenceGuideContent(
@@ -45,11 +46,9 @@ function referencesToReferenceGuideContent(
45
46
 
46
47
  function renderableToPageData(referenceGuideReference: ReferenceGuideReference[], renderable: Renderable): DocPageData {
47
48
  function buildDocOutput(renderable: Renderable, docContents: string): DocPageData {
48
- const reference = referenceGuideReference.find(
49
- (ref) => ref.typeName.toLowerCase() === renderable.name.toLowerCase(),
50
- );
51
-
52
- const namespacePrefix = renderable.namespace ? `${renderable.namespace}.` : '';
49
+ const reference: ReferenceGuideReference = referenceGuideReference.find(
50
+ (ref) => ref.reference.source.name.toLowerCase() === renderable.name.toLowerCase(),
51
+ )!;
53
52
 
54
53
  return {
55
54
  source: {
@@ -57,12 +56,10 @@ function renderableToPageData(referenceGuideReference: ReferenceGuideReference[]
57
56
  name: renderable.name,
58
57
  type: renderable.type,
59
58
  },
60
- fileName: `${namespacePrefix}${renderable.name}`,
61
- fileExtension: 'md',
62
- directory: `${reference?.directory}`,
59
+ filePath: reference!.reference.pathFromRoot,
63
60
  frontmatter: null,
64
61
  content: docContents,
65
- group: renderable.doc.group ?? 'Miscellaneous',
62
+ group: renderable.doc.group ?? defaults.defaultGroupName,
66
63
  };
67
64
  }
68
65
 
@@ -7,6 +7,7 @@ import {
7
7
  MethodMirror,
8
8
  PropertyMirror,
9
9
  } from '@cparra/apex-reflection';
10
+ import { DocPageReference } from '../../shared/types';
10
11
 
11
12
  export type Describable = string[] | undefined;
12
13
 
@@ -24,17 +25,14 @@ export type MethodMirrorWithInheritance = MethodMirror & InheritanceSupport;
24
25
  // Renderable types
25
26
 
26
27
  export type ReferenceGuideReference = {
27
- typeName: string;
28
- directory: string;
28
+ reference: DocPageReference;
29
29
  title: Link;
30
- description: RenderableContent[] | undefined;
30
+ description: RenderableContent[] | null;
31
31
  };
32
32
 
33
33
  export type RenderableBundle = {
34
34
  // References are grouped by their defined @group annotation
35
- references: {
36
- [key: string]: ReferenceGuideReference[];
37
- };
35
+ referencesByGroup: Record<string, ReferenceGuideReference[]>;
38
36
  renderables: Renderable[];
39
37
  };
40
38
 
@@ -10,11 +10,14 @@ import {
10
10
  Frontmatter,
11
11
  PostHookDocumentationBundle,
12
12
  ReferenceGuidePageData,
13
- SourceFile,
13
+ UnparsedSourceFile,
14
14
  TransformDocPage,
15
15
  TransformDocs,
16
16
  TransformReferenceGuide,
17
17
  UserDefinedMarkdownConfig,
18
+ DocPageReference,
19
+ TransformReference,
20
+ ParsedFile,
18
21
  } from '../shared/types';
19
22
  import { parsedFilesToRenderableBundle } from './adapters/renderable-bundle';
20
23
  import { reflectSourceCode } from './reflection/reflect-source';
@@ -27,6 +30,7 @@ import { Template } from './templates/template';
27
30
  import { hookableTemplate } from './templates/hookable';
28
31
  import { sortMembers } from './reflection/sort-members';
29
32
  import { isSkip } from '../shared/utils';
33
+ import { parsedFilesToReferenceGuide } from './adapters/reference-guide';
30
34
 
31
35
  export type MarkdownGeneratorConfig = Pick<
32
36
  UserDefinedMarkdownConfig,
@@ -37,6 +41,8 @@ export type MarkdownGeneratorConfig = Pick<
37
41
  | 'transformReferenceGuide'
38
42
  | 'transformDocs'
39
43
  | 'transformDocPage'
44
+ | 'transformReference'
45
+ | 'documentationRootDir'
40
46
  > & {
41
47
  referenceGuideTemplate: string;
42
48
  sortMembersAlphabetically: boolean;
@@ -48,8 +54,9 @@ export class HookError {
48
54
  constructor(public error: unknown) {}
49
55
  }
50
56
 
51
- export function generateDocs(apexBundles: SourceFile[], config: MarkdownGeneratorConfig) {
57
+ export function generateDocs(apexBundles: UnparsedSourceFile[], config: MarkdownGeneratorConfig) {
52
58
  const filterOutOfScope = apply(filterScope, config.scope);
59
+ const convertToReferences = apply(parsedFilesToReferenceGuide, config);
53
60
  const convertToRenderableBundle = apply(parsedFilesToRenderableBundle, config);
54
61
  const convertToDocumentationBundleForTemplate = apply(convertToDocumentationBundle, config.referenceGuideTemplate);
55
62
  const sortTypeMembers = apply(sortMembers, config.sortMembersAlphabetically);
@@ -62,53 +69,45 @@ export function generateDocs(apexBundles: SourceFile[], config: MarkdownGenerato
62
69
  E.map(addInheritedMembersToTypes),
63
70
  E.map(addInheritanceChainToTypes),
64
71
  E.map(sortTypeMembers),
65
- E.map(convertToRenderableBundle),
66
- E.map(convertToDocumentationBundleForTemplate),
72
+ E.bindTo('parsedFiles'),
73
+ E.bind('references', ({ parsedFiles }) => E.right(convertToReferences(parsedFiles))),
67
74
  TE.fromEither,
68
- TE.flatMap((bundle) =>
69
- TE.tryCatch(
70
- () => documentationBundleHook(bundle, config),
71
- (error) => new HookError(error),
72
- ),
73
- ),
74
- TE.map((bundle: PostHookDocumentationBundle) => ({
75
- referenceGuide: isSkip(bundle.referenceGuide)
76
- ? bundle.referenceGuide
77
- : {
78
- ...bundle.referenceGuide,
79
- content: Template.getInstance().compile({
80
- source: {
81
- frontmatter: toFrontmatterString(bundle.referenceGuide.frontmatter),
82
- content: bundle.referenceGuide.content,
83
- },
84
- template: hookableTemplate,
85
- }),
86
- },
87
- docs: bundle.docs.map((doc) => ({
88
- ...doc,
89
- content: Template.getInstance().compile({
90
- source: {
91
- frontmatter: toFrontmatterString(doc.frontmatter),
92
- content: doc.content,
93
- },
94
- template: hookableTemplate,
95
- }),
96
- })),
97
- })),
75
+ TE.flatMap(({ parsedFiles, references }) => transformReferenceHook(config)({ references, parsedFiles })),
76
+ TE.map(({ parsedFiles, references }) => convertToRenderableBundle(parsedFiles, references)),
77
+ TE.map(convertToDocumentationBundleForTemplate),
78
+ TE.flatMap(transformDocumentationBundleHook(config)),
79
+ TE.map(postHookCompile),
98
80
  );
99
81
  }
100
82
 
101
- function toFrontmatterString(frontmatter: Frontmatter): string {
102
- if (typeof frontmatter === 'string') {
103
- return frontmatter;
83
+ function transformReferenceHook(config: MarkdownGeneratorConfig) {
84
+ async function _execute(
85
+ references: Record<string, DocPageReference>,
86
+ parsedFiles: ParsedFile[],
87
+ transformReference?: TransformReference | undefined,
88
+ ): Promise<{
89
+ references: Record<string, DocPageReference>;
90
+ parsedFiles: ParsedFile[];
91
+ }> {
92
+ return {
93
+ references: await execTransformReferenceHook(Object.values(references), transformReference),
94
+ parsedFiles,
95
+ };
104
96
  }
105
97
 
106
- if (!frontmatter) {
107
- return '';
108
- }
98
+ return ({ references, parsedFiles }: { references: Record<string, DocPageReference>; parsedFiles: ParsedFile[] }) =>
99
+ TE.tryCatch(
100
+ () => _execute(references, parsedFiles, config.transformReference),
101
+ (error) => new HookError(error),
102
+ );
103
+ }
109
104
 
110
- const yamlString = yaml.dump(frontmatter);
111
- return `---\n${yamlString}---\n`;
105
+ function transformDocumentationBundleHook(config: MarkdownGeneratorConfig) {
106
+ return (bundle: DocumentationBundle) =>
107
+ TE.tryCatch(
108
+ () => documentationBundleHook(bundle, config),
109
+ (error) => new HookError(error),
110
+ );
112
111
  }
113
112
 
114
113
  // Configurable hooks
@@ -116,6 +115,25 @@ function passThroughHook<T>(value: T): T {
116
115
  return value;
117
116
  }
118
117
 
118
+ const execTransformReferenceHook = async (
119
+ references: DocPageReference[],
120
+ hook: TransformReference = passThroughHook,
121
+ ): Promise<Record<string, DocPageReference>> => {
122
+ const hooked = references.map<Promise<DocPageReference>>(async (reference) => {
123
+ const hookedResult = await hook(reference);
124
+ return {
125
+ ...reference,
126
+ ...hookedResult,
127
+ };
128
+ });
129
+ const awaited = await Promise.all(hooked);
130
+
131
+ return awaited.reduce<Record<string, DocPageReference>>((acc, reference) => {
132
+ acc[reference.source.name] = reference;
133
+ return acc;
134
+ }, {});
135
+ };
136
+
119
137
  const documentationBundleHook = async (
120
138
  bundle: DocumentationBundle,
121
139
  config: MarkdownGeneratorConfig,
@@ -156,3 +174,43 @@ const transformDocPage = async (doc: DocPageData, hook: TransformDocPage = passT
156
174
  ...(await hook(doc)),
157
175
  };
158
176
  };
177
+
178
+ function postHookCompile(bundle: PostHookDocumentationBundle) {
179
+ return {
180
+ referenceGuide: isSkip(bundle.referenceGuide)
181
+ ? bundle.referenceGuide
182
+ : {
183
+ ...bundle.referenceGuide,
184
+ content: Template.getInstance().compile({
185
+ source: {
186
+ frontmatter: toFrontmatterString(bundle.referenceGuide.frontmatter),
187
+ content: bundle.referenceGuide.content,
188
+ },
189
+ template: hookableTemplate,
190
+ }),
191
+ },
192
+ docs: bundle.docs.map((doc) => ({
193
+ ...doc,
194
+ content: Template.getInstance().compile({
195
+ source: {
196
+ frontmatter: toFrontmatterString(doc.frontmatter),
197
+ content: doc.content,
198
+ },
199
+ template: hookableTemplate,
200
+ }),
201
+ })),
202
+ };
203
+ }
204
+
205
+ function toFrontmatterString(frontmatter: Frontmatter): string {
206
+ if (typeof frontmatter === 'string') {
207
+ return frontmatter;
208
+ }
209
+
210
+ if (!frontmatter) {
211
+ return '';
212
+ }
213
+
214
+ const yamlString = yaml.dump(frontmatter);
215
+ return `---\n${yamlString}---\n`;
216
+ }
@@ -3,7 +3,7 @@ import { createInheritanceChain } from './inheritance-chain';
3
3
  import { ParsedFile } from '../../shared/types';
4
4
  import { parsedFilesToTypes } from '../utils';
5
5
 
6
- export const addInheritanceChainToTypes = (parsedFiles: ParsedFile[]) =>
6
+ export const addInheritanceChainToTypes = (parsedFiles: ParsedFile[]): ParsedFile[] =>
7
7
  parsedFiles.map((parsedFile) => ({
8
8
  ...parsedFile,
9
9
  type: addInheritanceChain(parsedFile.type, parsedFilesToTypes(parsedFiles)),