diff --git a/examples/vitepress/apexdocs.config.ts b/examples/vitepress/apexdocs.config.ts index 15db7a60..d32405b7 100644 --- a/examples/vitepress/apexdocs.config.ts +++ b/examples/vitepress/apexdocs.config.ts @@ -30,6 +30,13 @@ export default { previousVersionDir: 'previous', currentVersionDir: 'force-app', scope: ['global', 'public', 'protected', 'private', 'namespaceaccessible'], + transformChangeLogPage: () => { + return { + frontmatter: { + title: 'Changelog', + }, + }; + }, }), markdown: defineMarkdownConfig({ sourceDir: 'force-app', diff --git a/examples/vitepress/docs/changelog.md b/examples/vitepress/docs/changelog.md index d957d61d..3cc6f539 100644 --- a/examples/vitepress/docs/changelog.md +++ b/examples/vitepress/docs/changelog.md @@ -1,3 +1,7 @@ +--- +title: Changelog +--- + # Changelog ## New Classes diff --git a/src/application/generators/changelog.ts b/src/application/generators/changelog.ts index 00899238..e332c6c8 100644 --- a/src/application/generators/changelog.ts +++ b/src/application/generators/changelog.ts @@ -1,8 +1,14 @@ import { pipe } from 'fp-ts/function'; -import { PageData, Skip, UnparsedSourceBundle, UserDefinedChangelogConfig } from '../../core/shared/types'; +import { + ChangeLogPageData, + PageData, + Skip, + UnparsedSourceBundle, + UserDefinedChangelogConfig, +} from '../../core/shared/types'; import * as TE from 'fp-ts/TaskEither'; import { writeFiles } from '../file-writer'; -import { ChangeLogPageData, generateChangeLog } from '../../core/changelog/generate-change-log'; +import { generateChangeLog } from '../../core/changelog/generate-change-log'; import { FileWritingError } from '../errors'; import { isSkip } from '../../core/shared/utils'; diff --git a/src/core/changelog/__test__/generating-change-log.spec.ts b/src/core/changelog/__test__/generating-change-log.spec.ts index 4b33b6b3..8dbbefa4 100644 --- a/src/core/changelog/__test__/generating-change-log.spec.ts +++ b/src/core/changelog/__test__/generating-change-log.spec.ts @@ -1,5 +1,10 @@ -import { UnparsedApexBundle, UnparsedCustomObjectBundle, UnparsedSourceBundle } from '../../shared/types'; -import { ChangeLogPageData, generateChangeLog } from '../generate-change-log'; +import { + ChangeLogPageData, + UnparsedApexBundle, + UnparsedCustomObjectBundle, + UnparsedSourceBundle, +} from '../../shared/types'; +import { generateChangeLog } from '../generate-change-log'; import { assertEither } from '../../test-helpers/assert-either'; import { isSkip } from '../../shared/utils'; import { unparsedFieldBundleFromRawString } from '../../test-helpers/test-data-builders'; @@ -466,4 +471,16 @@ describe('when generating a changelog', () => { assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('PhotoUrl__c')); }); }); + + describe('and a custom hook is provided to customize the frontmatter', () => { + it('includes the custom frontmatter', async () => { + const hook = () => ({ + frontmatter: '---\ntitle: Custom Title\n---', + }); + + const result = await generateChangeLog([], [], { ...config, transformChangeLogPage: hook })(); + + assertEither(result, (data) => expect((data as ChangeLogPageData).frontmatter).toContain('title: Custom Title')); + }); + }); }); diff --git a/src/core/changelog/generate-change-log.ts b/src/core/changelog/generate-change-log.ts index b5dc13ea..a19564d5 100644 --- a/src/core/changelog/generate-change-log.ts +++ b/src/core/changelog/generate-change-log.ts @@ -1,6 +1,8 @@ import { + ChangeLogPageData, ParsedFile, Skip, + TransformChangelogPage, UnparsedApexBundle, UnparsedSourceBundle, UserDefinedChangelogConfig, @@ -12,26 +14,24 @@ import { Changelog, hasChanges, processChangelog, VersionManifest } from './proc import { convertToRenderableChangelog, RenderableChangelog } from './renderable-changelog'; import { CompilationRequest, Template } from '../template'; import { changelogTemplate } from './templates/changelog-template'; -import { ReflectionErrors } from '../errors/errors'; +import { HookError, ReflectionErrors } from '../errors/errors'; import { apply } from '#utils/fp'; import { filterScope } from '../reflection/apex/filter-scope'; -import { isInSource, skip } from '../shared/utils'; +import { isInSource, isSkip, passThroughHook, skip, toFrontmatterString } from '../shared/utils'; import { reflectCustomFieldsAndObjects } from '../reflection/sobject/reflectCustomFieldsAndObjects'; import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { Type } from '@cparra/apex-reflection'; import { filterApexSourceFiles, filterCustomObjectsAndFields } from '#utils/source-bundle-utils'; import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; +import { hookableTemplate } from '../markdown/templates/hookable'; -export type ChangeLogPageData = { - content: string; - outputDocPath: string; -}; +type Config = Omit; export function generateChangeLog( oldBundles: UnparsedSourceBundle[], newBundles: UnparsedSourceBundle[], - config: Omit, -): TE.TaskEither { + config: Config, +): TE.TaskEither { const convertToPageData = apply(toPageData, config.fileName); function handleConversion({ changelog, newManifest }: { changelog: Changelog; newManifest: VersionManifest }) { @@ -51,6 +51,8 @@ export function generateChangeLog( newManifest, })), TE.map(handleConversion), + TE.flatMap(transformChangelogPageHook(config)), + TE.map(postHookCompile), ); } @@ -106,7 +108,45 @@ function compile(renderable: RenderableChangelog): string { function toPageData(fileName: string, content: string): ChangeLogPageData { return { + frontmatter: null, content, outputDocPath: `${fileName}.md`, }; } + +function transformChangelogPageHook(config: Config) { + return (page: ChangeLogPageData | Skip) => + TE.tryCatch( + () => transformChangelogPage(page, config.transformChangeLogPage), + (error) => new HookError(error), + ); +} + +async function transformChangelogPage( + page: ChangeLogPageData | Skip, + hook: TransformChangelogPage = passThroughHook, +): Promise { + if (isSkip(page)) { + return page; + } + return { + ...page, + ...(await hook(page)), + }; +} + +function postHookCompile(page: ChangeLogPageData | Skip): ChangeLogPageData | Skip { + if (isSkip(page)) { + return page; + } + return { + ...page, + content: Template.getInstance().compile({ + source: { + frontmatter: toFrontmatterString(page.frontmatter), + content: page.content, + }, + template: hookableTemplate, + }), + }; +} diff --git a/src/core/markdown/generate-docs.ts b/src/core/markdown/generate-docs.ts index a0464ac3..fbffc05a 100644 --- a/src/core/markdown/generate-docs.ts +++ b/src/core/markdown/generate-docs.ts @@ -1,12 +1,10 @@ import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither'; -import yaml from 'js-yaml'; import { apply } from '#utils/fp'; import { DocPageData, DocumentationBundle, - Frontmatter, PostHookDocumentationBundle, ReferenceGuidePageData, UnparsedApexBundle, @@ -28,7 +26,7 @@ import { filterScope } from '../reflection/apex/filter-scope'; import { Template } from '../template'; import { hookableTemplate } from './templates/hookable'; import { sortTypesAndMembers } from '../reflection/sort-types-and-members'; -import { isSkip } from '../shared/utils'; +import { isSkip, passThroughHook, toFrontmatterString } from '../shared/utils'; import { parsedFilesToReferenceGuide } from './adapters/reference-guide'; import { removeExcludedTags } from '../reflection/apex/remove-excluded-tags'; import { HookError } from '../errors/errors'; @@ -129,9 +127,6 @@ function transformDocumentationBundleHook(config: MarkdownGeneratorConfig) { } // Configurable hooks -function passThroughHook(value: T): T { - return value; -} const execTransformReferenceHook = async ( references: DocPageReference[], @@ -219,16 +214,3 @@ function postHookCompile(bundle: PostHookDocumentationBundle) { })), }; } - -function toFrontmatterString(frontmatter: Frontmatter): string { - if (typeof frontmatter === 'string') { - return frontmatter; - } - - if (!frontmatter) { - return ''; - } - - const yamlString = yaml.dump(frontmatter); - return `---\n${yamlString}---\n`; -} diff --git a/src/core/shared/types.d.ts b/src/core/shared/types.d.ts index 560a51cf..70c7c1fd 100644 --- a/src/core/shared/types.d.ts +++ b/src/core/shared/types.d.ts @@ -1,5 +1,4 @@ import { Type } from '@cparra/apex-reflection'; -import { ChangeLogPageData } from '../changelog/generate-change-log'; import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; @@ -34,7 +33,7 @@ export type UserDefinedMarkdownConfig = { excludeTags: string[]; exclude: string[]; } & CliConfigurableMarkdownConfig & - Partial; + Partial; export type UserDefinedOpenApiConfig = { targetGenerator: 'openapi'; @@ -56,7 +55,7 @@ export type UserDefinedChangelogConfig = { scope: string[]; exclude: string[]; skipIfNoChanges: boolean; -}; +} & Partial; export type UserDefinedConfig = UserDefinedMarkdownConfig | UserDefinedOpenApiConfig | UserDefinedChangelogConfig; @@ -144,6 +143,12 @@ export type DocPageData = { export type OpenApiPageData = Omit; +export type ChangeLogPageData = { + frontmatter: Frontmatter; + content: string; + outputDocPath: string; +}; + export type PageData = DocPageData | OpenApiPageData | ReferenceGuidePageData | ChangeLogPageData; export type DocumentationBundle = { @@ -166,9 +171,9 @@ export type PostHookDocumentationBundle = { // CONFIGURABLE HOOKS /** - * The configurable hooks that can be used to modify the output of the generator. + * The configurable hooks that can be used to modify the output of the Markdown generator. */ -export type ConfigurableHooks = { +export type MarkdownConfigurableHooks = { transformReferenceGuide: TransformReferenceGuide; transformDocs: TransformDocs; transformDocPage: TransformDocPage; @@ -206,3 +211,11 @@ export type TransformDocs = (docs: DocPageData[]) => DocPageData[] | Promise Partial | Promise>; + +export type ChangelogConfigurableHooks = { + transformChangeLogPage: TransformChangelogPage; +}; + +export type TransformChangelogPage = ( + page: ChangeLogPageData, +) => Partial | Promise>; diff --git a/src/core/shared/utils.ts b/src/core/shared/utils.ts index 096719fe..05cf60bf 100644 --- a/src/core/shared/utils.ts +++ b/src/core/shared/utils.ts @@ -1,8 +1,9 @@ -import { ExternalMetadata, Skip, SourceFileMetadata } from './types'; +import { ExternalMetadata, Frontmatter, Skip, SourceFileMetadata } from './types'; import { Type } from '@cparra/apex-reflection'; import { CustomObjectMetadata } from '../reflection/sobject/reflect-custom-object-sources'; import { MarkdownGeneratorConfig } from '../markdown/generate-docs'; import { CustomFieldMetadata } from '../reflection/sobject/reflect-custom-field-source'; +import yaml from 'js-yaml'; /** * Represents a file to be skipped. @@ -44,3 +45,20 @@ export function getTypeGroup(type: Type | CustomObjectMetadata, config: Markdown return getGroup(type, config); } } + +export function passThroughHook(value: T): T { + return value; +} + +export function toFrontmatterString(frontmatter: Frontmatter): string { + if (typeof frontmatter === 'string') { + return frontmatter; + } + + if (!frontmatter) { + return ''; + } + + const yamlString = yaml.dump(frontmatter); + return `---\n${yamlString}---\n`; +} diff --git a/src/index.ts b/src/index.ts index dc7c2aa1..ed38ae86 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,11 @@ import type { - ConfigurableHooks, + MarkdownConfigurableHooks, Skip, UserDefinedMarkdownConfig, ReferenceGuidePageData, DocPageData, DocPageReference, + ChangeLogPageData, ConfigurableDocPageData, TransformReferenceGuide, TransformDocs, @@ -13,6 +14,8 @@ import type { ConfigurableDocPageReference, UserDefinedOpenApiConfig, UserDefinedChangelogConfig, + ChangelogConfigurableHooks, + TransformChangelogPage, } from './core/shared/types'; import { skip } from './core/shared/utils'; import { changeLogDefaults, markdownDefaults, openApiDefaults } from './defaults'; @@ -72,12 +75,15 @@ export { TransformDocs, TransformDocPage, TransformReference, - ConfigurableHooks, + MarkdownConfigurableHooks, ReferenceGuidePageData, DocPageData, + ChangeLogPageData, DocPageReference, Skip, ConfigurableDocPageData, ConfigurableDocPageReference, process, + ChangelogConfigurableHooks, + TransformChangelogPage, };