@cparra/apexdocs 2.25.0-alpha.6 → 2.25.0-alpha.9

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 (53) hide show
  1. package/dist/cli/generate.js +192 -225
  2. package/dist/index.d.ts +51 -13
  3. package/examples/plain-markdown/docs/index.md +25 -33
  4. package/examples/plain-markdown/docs/{Miscellaneous/ns.BaseClass.md → miscellaneous/BaseClass.md} +1 -1
  5. package/examples/plain-markdown/docs/{Miscellaneous/ns.MultiInheritanceClass.md → miscellaneous/MultiInheritanceClass.md} +5 -6
  6. package/examples/plain-markdown/docs/{Miscellaneous/ns.SampleException.md → miscellaneous/SampleException.md} +3 -4
  7. package/examples/plain-markdown/docs/{Miscellaneous/ns.SampleInterface.md → miscellaneous/SampleInterface.md} +22 -29
  8. package/examples/plain-markdown/docs/{Miscellaneous/ns.Url.md → miscellaneous/Url.md} +32 -43
  9. package/examples/plain-markdown/docs/sample-enums/SampleEnum.md +36 -0
  10. package/examples/plain-markdown/docs/{SampleGroup/ns.SampleClass.md → samplegroup/SampleClass.md} +8 -11
  11. package/examples/vitepress/apexdocs.config.ts +1 -3
  12. package/examples/vitepress/docs/.vitepress/sidebar.json +18 -18
  13. package/examples/vitepress/docs/index.md +11 -82
  14. package/examples/vitepress/docs/{Miscellaneous/apexdocs.BaseClass.md → miscellaneous/BaseClass.md} +1 -1
  15. package/examples/vitepress/docs/{Miscellaneous/apexdocs.MultiInheritanceClass.md → miscellaneous/MultiInheritanceClass.md} +4 -6
  16. package/examples/vitepress/docs/{Miscellaneous/apexdocs.SampleException.md → miscellaneous/SampleException.md} +1 -1
  17. package/examples/vitepress/docs/{Miscellaneous/apexdocs.SampleInterface.md → miscellaneous/SampleInterface.md} +22 -28
  18. package/examples/vitepress/docs/{Miscellaneous/apexdocs.Url.md → miscellaneous/Url.md} +31 -32
  19. package/examples/vitepress/docs/sample-enums/SampleEnum.md +40 -0
  20. package/examples/vitepress/docs/{SampleGroup/apexdocs.SampleClass.md → samplegroup/SampleClass.md} +6 -10
  21. package/examples/vitepress/force-app/main/default/classes/Url.cls +7 -4
  22. package/package.json +1 -1
  23. package/src/application/apex-file-reader.ts +3 -3
  24. package/src/application/file-writer.ts +5 -9
  25. package/src/application/generators/markdown.ts +9 -4
  26. package/src/application/generators/openapi.ts +4 -4
  27. package/src/core/markdown/__test__/generating-class-docs.spec.ts +11 -3
  28. package/src/core/markdown/__test__/generating-enum-docs.spec.ts +7 -3
  29. package/src/core/markdown/__test__/generating-interface-docs.spec.ts +11 -3
  30. package/src/core/markdown/__test__/generating-reference-guide.spec.ts +3 -7
  31. package/src/core/markdown/__test__/test-helpers.ts +3 -3
  32. package/src/core/markdown/adapters/__tests__/interface-adapter.spec.ts +40 -5
  33. package/src/core/markdown/adapters/apex-types.ts +3 -2
  34. package/src/core/markdown/adapters/documentables.ts +49 -68
  35. package/src/core/markdown/adapters/reference-guide.ts +35 -0
  36. package/src/core/markdown/adapters/renderable-bundle.ts +43 -119
  37. package/src/core/markdown/adapters/renderable-to-page-data.ts +12 -15
  38. package/src/core/markdown/adapters/types.d.ts +5 -8
  39. package/src/core/markdown/generate-docs.ts +99 -42
  40. package/src/core/markdown/reflection/inheritance-chain-expanion.ts +1 -1
  41. package/src/core/markdown/reflection/reflect-source.ts +8 -4
  42. package/src/core/markdown/templates/documentable-partial-template.ts +1 -1
  43. package/src/core/openapi/manifest-factory.ts +2 -2
  44. package/src/core/openapi/openapi-type-file.ts +1 -3
  45. package/src/core/openapi/parser.ts +4 -4
  46. package/src/core/shared/types.d.ts +52 -14
  47. package/src/index.ts +2 -1
  48. package/examples/plain-markdown/docs/Sample-Enums/ns.SampleEnum.md +0 -38
  49. package/examples/vitepress/docs/Sample-Enums/apexdocs.SampleEnum.md +0 -41
  50. /package/examples/plain-markdown/docs/{Miscellaneous/ns.ParentInterface.md → miscellaneous/ParentInterface.md} +0 -0
  51. /package/examples/plain-markdown/docs/{Miscellaneous/ns.ReferencedEnum.md → miscellaneous/ReferencedEnum.md} +0 -0
  52. /package/examples/vitepress/docs/{Miscellaneous/apexdocs.ParentInterface.md → miscellaneous/ParentInterface.md} +0 -0
  53. /package/examples/vitepress/docs/{Miscellaneous/apexdocs.ReferencedEnum.md → miscellaneous/ReferencedEnum.md} +0 -0
@@ -5,21 +5,17 @@ import { PageData } from '../core/shared/types';
5
5
  export class FileWriter {
6
6
  static write(files: PageData[], outputDir: string, onWriteCallback: (file: PageData) => void) {
7
7
  files.forEach((file) => {
8
- const resolvedFile = this.getTargetLocation(file, outputDir);
9
- if (!fs.existsSync(resolvedFile.directory)) {
10
- fs.mkdirSync(resolvedFile.directory, { recursive: true });
11
- }
12
-
13
- const filePath = path.join(resolvedFile.directory, `${resolvedFile.fileName}.${resolvedFile.fileExtension}`);
14
- fs.writeFileSync(filePath, resolvedFile.content, 'utf8');
15
- onWriteCallback(resolvedFile);
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);
16
12
  });
17
13
  }
18
14
 
19
15
  private static getTargetLocation(file: PageData, outputDir: string): PageData {
20
16
  return {
21
17
  ...file,
22
- directory: path.join(outputDir, file.directory),
18
+ filePath: path.join(outputDir, file.filePath),
23
19
  };
24
20
  }
25
21
  }
@@ -2,13 +2,18 @@ import { generateDocs } from '../../core/markdown/generate-docs';
2
2
  import { FileWriter } from '../file-writer';
3
3
  import { Logger } from '#utils/logger';
4
4
  import { pipe } from 'fp-ts/function';
5
- import { PageData, PostHookDocumentationBundle, SourceFile, UserDefinedMarkdownConfig } from '../../core/shared/types';
5
+ import {
6
+ PageData,
7
+ PostHookDocumentationBundle,
8
+ UnparsedSourceFile,
9
+ UserDefinedMarkdownConfig,
10
+ } from '../../core/shared/types';
6
11
  import { ReflectionError } from '../../core/markdown/reflection/error-handling';
7
12
  import { referenceGuideTemplate } from '../../core/markdown/templates/reference-guide';
8
13
  import * as TE from 'fp-ts/TaskEither';
9
14
  import { isSkip } from '../../core/shared/utils';
10
15
 
11
- export default function generate(bundles: SourceFile[], config: UserDefinedMarkdownConfig) {
16
+ export default function generate(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
12
17
  return pipe(
13
18
  generateDocumentationBundle(bundles, config),
14
19
  TE.map((files) => writeFilesToSystem(files, config.targetDir)),
@@ -29,7 +34,7 @@ export default function generate(bundles: SourceFile[], config: UserDefinedMarkd
29
34
  )();
30
35
  }
31
36
 
32
- function generateDocumentationBundle(bundles: SourceFile[], config: UserDefinedMarkdownConfig) {
37
+ function generateDocumentationBundle(bundles: UnparsedSourceFile[], config: UserDefinedMarkdownConfig) {
33
38
  return generateDocs(bundles, {
34
39
  ...config,
35
40
  referenceGuideTemplate: referenceGuideTemplate,
@@ -43,7 +48,7 @@ function writeFilesToSystem(files: PostHookDocumentationBundle, outputDir: strin
43
48
  .filter((file) => !isSkip(file)) as PageData[],
44
49
  outputDir,
45
50
  (file: PageData) => {
46
- Logger.logSingle(`${file.fileName} processed.`, false, 'green', false);
51
+ Logger.logSingle(`${file.filePath} processed.`, false, 'green', false);
47
52
  },
48
53
  );
49
54
  }
@@ -7,10 +7,10 @@ import { Logger } from '#utils/logger';
7
7
  import ErrorLogger from '#utils/error-logger';
8
8
  import { reflect, ReflectionResult } from '@cparra/apex-reflection';
9
9
  import Manifest from '../../core/manifest';
10
- import { PageData, SourceFile, UserDefinedOpenApiConfig } from '../../core/shared/types';
10
+ import { PageData, UnparsedSourceFile, UserDefinedOpenApiConfig } from '../../core/shared/types';
11
11
  import { OpenApiDocsProcessor } from '../../core/openapi/open-api-docs-processor';
12
12
 
13
- export default function openApi(fileBodies: SourceFile[], config: UserDefinedOpenApiConfig) {
13
+ export default function openApi(fileBodies: UnparsedSourceFile[], config: UserDefinedOpenApiConfig) {
14
14
  const manifest = createManifest(new RawBodyParser(fileBodies), reflectionWithLogger);
15
15
  TypesRepository.getInstance().populateAll(manifest.types);
16
16
  const filteredTypes = filterByScopes(manifest);
@@ -19,14 +19,14 @@ export default function openApi(fileBodies: SourceFile[], config: UserDefinedOpe
19
19
  const generatedFiles = processor.fileBuilder().files();
20
20
 
21
21
  FileWriter.write(generatedFiles, config.targetDir, (file: PageData) => {
22
- Logger.logSingle(`${file.fileName} processed.`, false, 'green', false);
22
+ Logger.logSingle(`${file.filePath} processed.`, false, 'green', false);
23
23
  });
24
24
 
25
25
  // Error logging
26
26
  ErrorLogger.logErrors(filteredTypes);
27
27
  }
28
28
 
29
- function reflectionWithLogger(apexBundle: SourceFile): ReflectionResult {
29
+ function reflectionWithLogger(apexBundle: UnparsedSourceFile): ReflectionResult {
30
30
  const result = reflect(apexBundle.content);
31
31
  if (result.error) {
32
32
  Logger.error(`${apexBundle.filePath} - Parsing error ${result.error?.message}`);
@@ -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].fileName).toBe('MyClass'));
15
+ assertEither(result, (data) => expect(data.docs[0].filePath).toContain('MyClass'));
16
16
  });
17
17
 
18
18
  it('returns the type as class', async () => {
@@ -268,7 +268,9 @@ 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('This is a description with a [ClassRef](./ClassRef.md) reference'),
271
+ expect(data).firstDocContains(
272
+ 'This is a description with a [ClassRef](/miscellaneous/ClassRef.md) reference',
273
+ ),
272
274
  );
273
275
  });
274
276
 
@@ -302,7 +304,7 @@ describe('Generates interface documentation', () => {
302
304
  const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
303
305
  expect(result).documentationBundleHasLength(2);
304
306
  assertEither(result, (data) => expect(data).firstDocContains('See'));
305
- assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](./ClassRef.md)'));
307
+ assertEither(result, (data) => expect(data).firstDocContains('[ClassRef](/miscellaneous/ClassRef.md)'));
306
308
  });
307
309
 
308
310
  it('displays sees without links when the reference is not found', async () => {
@@ -340,11 +342,13 @@ describe('Generates interface documentation', () => {
340
342
  const input = `
341
343
  /**
342
344
  * @mermaid
345
+ * \`\`\`mermaid
343
346
  * graph TD
344
347
  * A[Square Rect] -- Link text --> B((Circle))
345
348
  * A --> C(Round Rect)
346
349
  * B --> D{Rhombus}
347
350
  * C --> D
351
+ * \`\`\`
348
352
  */
349
353
  public class MyClass {}
350
354
  `;
@@ -359,11 +363,13 @@ describe('Generates interface documentation', () => {
359
363
  const input = `
360
364
  /**
361
365
  * @example
366
+ * \`\`\`apex
362
367
  * public class MyClass {
363
368
  * public void myMethod() {
364
369
  * System.debug('Hello, World!');
365
370
  * }
366
371
  * }
372
+ * \`\`\`
367
373
  */
368
374
  public class MyClass {}`;
369
375
 
@@ -669,11 +675,13 @@ describe('Generates interface documentation', () => {
669
675
  public class MyClass {
670
676
  /**
671
677
  * @mermaid
678
+ * \`\`\`mermaid
672
679
  * graph TD
673
680
  * A[Square Rect] -- Link text --> B((Circle))
674
681
  * A --> C(Round Rect)
675
682
  * B --> D{Rhombus}
676
683
  * C --> D
684
+ * \`\`\`
677
685
  */
678
686
  public void myMethod() {}
679
687
  }
@@ -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].fileName).toBe('MyEnum'));
20
+ assertEither(result, (data) => expect(data.docs[0].filePath).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](./EnumRef.md) reference'),
214
+ expect(data).firstDocContains('This is a description with a [EnumRef](/miscellaneous/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](./EnumRef.md)'));
248
+ assertEither(result, (data) => expect(data).firstDocContains('[EnumRef](/miscellaneous/EnumRef.md)'));
249
249
  });
250
250
 
251
251
  it('displays sees without links when the reference is not found', async () => {
@@ -287,11 +287,13 @@ describe('Generates enum documentation', () => {
287
287
  const input = `
288
288
  /**
289
289
  * @mermaid
290
+ * \`\`\`mermaid
290
291
  * graph TD
291
292
  * A[Square Rect] -- Link text --> B((Circle))
292
293
  * A --> C(Round Rect)
293
294
  * B --> D{Rhombus}
294
295
  * C --> D
296
+ * \`\`\`
295
297
  */
296
298
  public enum MyEnum {
297
299
  VALUE1,
@@ -309,11 +311,13 @@ describe('Generates enum documentation', () => {
309
311
  const input = `
310
312
  /**
311
313
  * @example
314
+ * \`\`\`apex
312
315
  * public class MyClass {
313
316
  * public void myMethod() {
314
317
  * System.debug('Hello, World!');
315
318
  * }
316
319
  * }
320
+ * \`\`\`
317
321
  */
318
322
  public enum MyEnum {
319
323
  VALUE1,
@@ -189,7 +189,9 @@ 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('This is a description with a [InterfaceRef](./InterfaceRef.md) reference'),
192
+ expect(data).firstDocContains(
193
+ 'This is a description with a [InterfaceRef](/miscellaneous/InterfaceRef.md) reference',
194
+ ),
193
195
  );
194
196
  });
195
197
 
@@ -223,7 +225,7 @@ describe('Generates interface documentation', () => {
223
225
  const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
224
226
  expect(result).documentationBundleHasLength(2);
225
227
  assertEither(result, (data) => expect(data).firstDocContains('See'));
226
- assertEither(result, (data) => expect(data).firstDocContains('[InterfaceRef](./InterfaceRef.md)'));
228
+ assertEither(result, (data) => expect(data).firstDocContains('[InterfaceRef](/miscellaneous/InterfaceRef.md)'));
227
229
  });
228
230
 
229
231
  it('displays sees without links when the reference is not found', async () => {
@@ -261,11 +263,13 @@ describe('Generates interface documentation', () => {
261
263
  const input = `
262
264
  /**
263
265
  * @mermaid
266
+ * \`\`\`mermaid
264
267
  * graph TD
265
268
  * A[Square Rect] -- Link text --> B((Circle))
266
269
  * A --> C(Round Rect)
267
270
  * B --> D{Rhombus}
268
271
  * C --> D
272
+ * \`\`\`
269
273
  */
270
274
  public interface MyInterface {}
271
275
  `;
@@ -280,11 +284,13 @@ describe('Generates interface documentation', () => {
280
284
  const input = `
281
285
  /**
282
286
  * @example
287
+ * \`\`\`apex
283
288
  * public class MyClass {
284
289
  * public void myMethod() {
285
290
  * System.debug('Hello, World!');
286
291
  * }
287
292
  * }
293
+ * \`\`\`
288
294
  */
289
295
  public interface MyInterface {}`;
290
296
 
@@ -343,11 +349,13 @@ describe('Generates interface documentation', () => {
343
349
  public interface MyInterface {
344
350
  /**
345
351
  * @mermaid
352
+ * \`\`\`mermaid
346
353
  * graph TD
347
354
  * A[Square Rect] -- Link text --> B((Circle))
348
355
  * A --> C(Round Rect)
349
356
  * B --> D{Rhombus}
350
357
  * C --> D
358
+ * \`\`\`
351
359
  */
352
360
  void myMethod();
353
361
  }
@@ -445,7 +453,7 @@ describe('Generates interface documentation', () => {
445
453
  const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
446
454
  expect(result).documentationBundleHasLength(2);
447
455
  assertEither(result, (data) =>
448
- expect(data.docs.find((doc) => doc.fileName === 'AnotherInterface')?.content).toContain('Inherited'),
456
+ expect(data.docs.find((doc) => doc.filePath.includes('AnotherInterface'))?.content).toContain('Inherited'),
449
457
  );
450
458
  });
451
459
  });
@@ -25,12 +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](./Miscellaneous/MyEnum.md)'),
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(
32
- '[MyClass](./Miscellaneous/MyClass.md)',
33
- ),
31
+ expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('[MyClass](/miscellaneous/MyClass.md)'),
34
32
  );
35
33
  });
36
34
 
@@ -176,9 +174,7 @@ describe('Generates a Reference Guide', () => {
176
174
  const result = await generateDocs([apexBundleFromRawString(input1), apexBundleFromRawString(input2)])();
177
175
  expect(result).documentationBundleHasLength(2);
178
176
  assertEither(result, (data) =>
179
- expect((data.referenceGuide as ReferenceGuidePageData).content).toContain(
180
- 'with a [MyClass](./Group2/MyClass.md)',
181
- ),
177
+ expect((data.referenceGuide as ReferenceGuidePageData).content).toContain('with a [MyClass](/group2/MyClass.md)'),
182
178
  );
183
179
  });
184
180
  });
@@ -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'],
@@ -21,7 +21,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
21
21
  it('converts the name', () => {
22
22
  const interfaceMirror = new InterfaceMirrorBuilder().withName('SampleInterface').build();
23
23
  const interfaceSource = typeToRenderable(
24
- { filePath: '', type: interfaceMirror },
24
+ {
25
+ source: {
26
+ filePath: '',
27
+ type: 'interface',
28
+ name: 'SampleInterface',
29
+ },
30
+ type: interfaceMirror,
31
+ },
25
32
  linkGenerator,
26
33
  defaultMarkdownGeneratorConfig,
27
34
  );
@@ -32,7 +39,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
32
39
  it('converts the access modifier', () => {
33
40
  const interfaceMirror = new InterfaceMirrorBuilder().build();
34
41
  const interfaceSource = typeToRenderable(
35
- { filePath: '', type: interfaceMirror },
42
+ {
43
+ source: {
44
+ filePath: '',
45
+ type: 'interface',
46
+ name: 'SampleInterface',
47
+ },
48
+ type: interfaceMirror,
49
+ },
36
50
  linkGenerator,
37
51
  defaultMarkdownGeneratorConfig,
38
52
  );
@@ -45,7 +59,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
45
59
  .addAnnotation(new AnnotationBuilder().withName('MyAnnotation').build())
46
60
  .build();
47
61
  const interfaceSource = typeToRenderable(
48
- { filePath: '', type: interfaceMirror },
62
+ {
63
+ source: {
64
+ filePath: '',
65
+ type: 'interface',
66
+ name: 'SampleInterface',
67
+ },
68
+ type: interfaceMirror,
69
+ },
49
70
  linkGenerator,
50
71
  defaultMarkdownGeneratorConfig,
51
72
  );
@@ -67,7 +88,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
67
88
  .build();
68
89
 
69
90
  const interfaceSource = typeToRenderable(
70
- { filePath: '', type: interfaceMirror },
91
+ {
92
+ source: {
93
+ filePath: '',
94
+ type: 'interface',
95
+ name: 'SampleInterface',
96
+ },
97
+ type: interfaceMirror,
98
+ },
71
99
  linkGenerator,
72
100
  defaultMarkdownGeneratorConfig,
73
101
  );
@@ -99,7 +127,14 @@ describe('Conversion from InterfaceMirror to InterfaceSource understandable by t
99
127
  .build();
100
128
 
101
129
  const interfaceSource = typeToRenderable(
102
- { filePath: '', type: interfaceMirror },
130
+ {
131
+ source: {
132
+ filePath: '',
133
+ type: 'interface',
134
+ name: 'SampleInterface',
135
+ },
136
+ type: interfaceMirror,
137
+ },
103
138
  linkGenerator,
104
139
  defaultMarkdownGeneratorConfig,
105
140
  );
@@ -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
  }
@@ -1,4 +1,4 @@
1
- import { CustomTag, RenderableDocumentation, RenderableContent, CodeBlock } from './types';
1
+ import { CustomTag, RenderableDocumentation, RenderableContent } from './types';
2
2
  import { Describable, Documentable, GetRenderableContentByTypeName } from './types';
3
3
  import { replaceInlineReferences } from './inline';
4
4
  import { isEmptyLine } from './type-utils';
@@ -9,59 +9,62 @@ export function adaptDescribable(
9
9
  ): {
10
10
  description?: RenderableContent[];
11
11
  } {
12
- function describableToRenderableContent(describable: Describable): RenderableContent[] | undefined {
13
- if (!describable) {
14
- return;
15
- }
12
+ return {
13
+ description: describableToRenderableContent(describable, linkGenerator),
14
+ };
15
+ }
16
16
 
17
- let content: RenderableContent[] = [];
18
- for (let i = 0; i < describable.length; i++) {
19
- const line = describable[i];
20
- // The language might or might not be present after ```
21
- const codeBlockMatch = line.match(/^```([a-zA-Z]*)$/);
22
- if (codeBlockMatch) {
23
- // Check if the language is present, if not, fallback to "apex"
24
- const language = codeBlockMatch[1] || 'apex';
25
- const codeBlockLines: string[] = [];
26
- i++;
27
- while (i < describable.length) {
28
- const currentLine = describable[i];
29
- if (currentLine.trim() === '```') {
30
- break;
31
- }
32
- codeBlockLines.push(currentLine);
33
- i++;
17
+ export function describableToRenderableContent(
18
+ describable: Describable,
19
+ linkGenerator: GetRenderableContentByTypeName,
20
+ ): RenderableContent[] | undefined {
21
+ if (!describable) {
22
+ return;
23
+ }
24
+
25
+ let content: RenderableContent[] = [];
26
+ for (let i = 0; i < describable.length; i++) {
27
+ const line = describable[i];
28
+ // The language might or might not be present after ```
29
+ const codeBlockMatch = line.match(/^```([a-zA-Z]*)$/);
30
+ if (codeBlockMatch) {
31
+ // Check if the language is present, if not, fallback to "apex"
32
+ const language = codeBlockMatch[1] || 'apex';
33
+ const codeBlockLines: string[] = [];
34
+ i++;
35
+ while (i < describable.length) {
36
+ const currentLine = describable[i];
37
+ if (currentLine.trim() === '```') {
38
+ break;
34
39
  }
35
- content = [
36
- ...content,
37
- {
38
- __type: 'code-block',
39
- language,
40
- content: codeBlockLines,
41
- },
42
- { __type: 'empty-line' },
43
- ];
44
- continue;
40
+ codeBlockLines.push(currentLine);
41
+ i++;
45
42
  }
46
-
47
43
  content = [
48
44
  ...content,
49
- ...replaceInlineReferences(line, linkGenerator),
50
45
  {
51
- __type: 'empty-line',
46
+ __type: 'code-block',
47
+ language,
48
+ content: codeBlockLines,
52
49
  },
50
+ { __type: 'empty-line' },
53
51
  ];
52
+ continue;
54
53
  }
55
- return (
56
- content
57
- // If the last element is an empty line, remove it
58
- .filter((line, index, lines) => !(isEmptyLine(line) && index === lines.length - 1))
59
- );
60
- }
61
54
 
62
- return {
63
- description: describableToRenderableContent(describable),
64
- };
55
+ content = [
56
+ ...content,
57
+ ...replaceInlineReferences(line, linkGenerator),
58
+ {
59
+ __type: 'empty-line',
60
+ },
61
+ ];
62
+ }
63
+ return (
64
+ content
65
+ // If the last element is an empty line, remove it
66
+ .filter((line, index, lines) => !(isEmptyLine(line) && index === lines.length - 1))
67
+ );
65
68
  }
66
69
 
67
70
  export function adaptDocumentable(
@@ -70,7 +73,7 @@ export function adaptDocumentable(
70
73
  subHeadingLevel: number,
71
74
  ): RenderableDocumentation {
72
75
  function extractCustomTags(type: Documentable): CustomTag[] {
73
- const baseTags = ['description', 'group', 'author', 'date', 'see', 'example', 'mermaid', 'throws', 'exception'];
76
+ const baseTags = ['description', 'group', 'author', 'date', 'see', 'example', 'throws', 'exception'];
74
77
 
75
78
  return (
76
79
  type.docComment?.annotations
@@ -82,12 +85,6 @@ export function adaptDocumentable(
82
85
  );
83
86
  }
84
87
 
85
- function extractAnnotationBodyLines(type: Documentable, annotationName: string): string[] | undefined {
86
- return type.docComment?.annotations.find(
87
- (currentAnnotation) => currentAnnotation.name.toLowerCase() === annotationName,
88
- )?.bodyLines;
89
- }
90
-
91
88
  function extractAnnotationBody(type: Documentable, annotationName: string): string | undefined {
92
89
  return type.docComment?.annotations.find(
93
90
  (currentAnnotation) => currentAnnotation.name.toLowerCase() === annotationName,
@@ -102,30 +99,14 @@ export function adaptDocumentable(
102
99
  );
103
100
  }
104
101
 
105
- function bodyLinesToCodeBlock(language: string, bodyLines: string[] | undefined): CodeBlock | undefined {
106
- if (!bodyLines) {
107
- return;
108
- }
109
- return {
110
- __type: 'code-block',
111
- language,
112
- content: bodyLines,
113
- };
114
- }
115
-
116
102
  return {
117
103
  ...adaptDescribable(documentable.docComment?.descriptionLines, linkGenerator),
118
104
  annotations: documentable.annotations.map((annotation) => annotation.type.toUpperCase()),
119
105
  customTags: extractCustomTags(documentable),
120
- mermaid: {
121
- headingLevel: subHeadingLevel,
122
- heading: 'Diagram',
123
- value: bodyLinesToCodeBlock('mermaid', extractAnnotationBodyLines(documentable, 'mermaid')),
124
- },
125
106
  example: {
126
107
  headingLevel: subHeadingLevel,
127
108
  heading: 'Example',
128
- value: bodyLinesToCodeBlock('apex', documentable.docComment?.exampleAnnotation?.bodyLines),
109
+ value: describableToRenderableContent(documentable.docComment?.exampleAnnotation?.bodyLines, linkGenerator),
129
110
  },
130
111
  group: extractAnnotationBody(documentable, 'group'),
131
112
  author: extractAnnotationBody(documentable, 'author'),
@@ -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
+ }