Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/application/Apexdocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ async function processChangeLog(config: UserDefinedChangelogConfig) {
return pipe(
TE.tryCatch(loadFiles, (e) => new FileReadingError('An error occurred while reading files.', e)),
TE.flatMap(([previous, current]) => changelog(previous, current, config)),
TE.map(() => '✔️ Changelog generated successfully!'),
TE.mapLeft(toErrors),
);
}
Expand Down
17 changes: 12 additions & 5 deletions src/application/generators/changelog.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import { pipe } from 'fp-ts/function';
import { PageData, UnparsedSourceFile, UserDefinedChangelogConfig } from '../../core/shared/types';
import { PageData, Skip, UnparsedSourceFile, 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 { FileWritingError } from '../errors';
import { isSkip } from '../../core/shared/utils';

export default function generate(
oldBundles: UnparsedSourceFile[],
newBundles: UnparsedSourceFile[],
config: UserDefinedChangelogConfig,
) {
return pipe(
generateChangeLog(oldBundles, newBundles, config),
TE.flatMap((files) => writeFilesToSystem(files, config.targetDir)),
);
function handleFile(file: ChangeLogPageData | Skip) {
if (isSkip(file)) {
return TE.right('✔️ Done! Skipped writing files to the system.');
}

return writeFilesToSystem(file, config.targetDir);
}

return pipe(generateChangeLog(oldBundles, newBundles, config), TE.flatMap(handleFile));
}

function writeFilesToSystem(pageData: ChangeLogPageData, outputDir: string) {
return pipe(
[pageData],
(files) => writeFiles(files as PageData[], outputDir),
TE.map(() => '✔️ Changelog generated successfully!'),
TE.mapLeft((error) => {
return new FileWritingError('An error occurred while writing files to the system.', error);
}),
Expand Down
5 changes: 5 additions & 0 deletions src/cli/commands/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ export const changeLogOptions: { [key: string]: Options } = {
'Values should be separated by a space, e.g --scope global public namespaceaccessible. ' +
'Annotations are supported and should be passed lowercased and without the @ symbol, e.g. namespaceaccessible auraenabled.',
},
skipIfNoChanges: {
type: 'boolean',
default: changeLogDefaults.skipIfNoChanges,
describe: 'Skip the changelog generation if there are no changes between the previous and current version.',
},
};
54 changes: 36 additions & 18 deletions src/core/changelog/__test__/generating-change-log.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UnparsedSourceFile } from '../../shared/types';
import { generateChangeLog } from '../generate-change-log';
import { ChangeLogPageData, generateChangeLog } from '../generate-change-log';
import { assertEither } from '../../test-helpers/assert-either';
import { isSkip } from '../../shared/utils';

const config = {
fileName: 'changelog',
Expand All @@ -9,13 +10,26 @@ const config = {
currentVersionDir: '',
previousVersionDir: '',
exclude: [],
skipIfNoChanges: false,
};

describe('when generating a changelog', () => {
it('should not skip when skipIfNoChanges, even if there are no changes', async () => {
const result = await generateChangeLog([], [], { ...config })();

assertEither(result, (data) => expect(isSkip(data)).toBe(false));
});

it('should skip when there are no changes', async () => {
const result = await generateChangeLog([], [], { ...config, skipIfNoChanges: true })();

assertEither(result, (data) => expect(isSkip(data)).toBe(true));
});

it('should return a file path', async () => {
const result = await generateChangeLog([], [], config)();

assertEither(result, (data) => expect(data.outputDocPath).toContain('changelog.md'));
assertEither(result, (data) => expect((data as ChangeLogPageData).outputDocPath).toContain('changelog.md'));
});

describe('that does not include new classes', () => {
Expand All @@ -25,7 +39,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).not.toContain('## New Classes'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).not.toContain('## New Classes'));
});
});

Expand All @@ -40,7 +54,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('## New Classes'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('## New Classes'));
});

it('should include the new class name', async () => {
Expand All @@ -53,7 +67,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('### Test'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('### Test'));
});

it('should include the new class description', async () => {
Expand All @@ -71,7 +85,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('This is a test class.'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('This is a test class.'));
});
});

Expand All @@ -86,7 +100,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('## New Interfaces'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('## New Interfaces'));
});

it('should include the new interface name', async () => {
Expand All @@ -99,7 +113,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('### Test'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('### Test'));
});

it('should include the new interface description', async () => {
Expand All @@ -117,7 +131,9 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('This is a test interface.'));
assertEither(result, (data) =>
expect((data as ChangeLogPageData).content).toContain('This is a test interface.'),
);
});
});

Expand All @@ -130,7 +146,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('## New Enums'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('## New Enums'));
});

it('should include the new enum name', async () => {
Expand All @@ -141,7 +157,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('### Test'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('### Test'));
});

it('should include the new enum description', async () => {
Expand All @@ -157,7 +173,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('This is a test enum.'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('This is a test enum.'));
});
});

Expand All @@ -172,7 +188,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, { ...config, scope: ['global'] })();

assertEither(result, (data) => expect(data.content).not.toContain('## New Classes'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).not.toContain('## New Classes'));
});
});

Expand All @@ -187,7 +203,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('## Removed Types'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('## Removed Types'));
});

it('should include the removed type name', async () => {
Expand All @@ -200,7 +216,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('- Test'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('- Test'));
});
});

Expand All @@ -219,7 +235,9 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('## New or Modified Members in Existing Types'));
assertEither(result, (data) =>
expect((data as ChangeLogPageData).content).toContain('## New or Modified Members in Existing Types'),
);
});

it('should include the new or modified type name', async () => {
Expand All @@ -236,7 +254,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('### Test'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('### Test'));
});

it('should include the new or modified member name', async () => {
Expand All @@ -253,7 +271,7 @@ describe('when generating a changelog', () => {

const result = await generateChangeLog(oldBundle, newBundle, config)();

assertEither(result, (data) => expect(data.content).toContain('myMethod'));
assertEither(result, (data) => expect((data as ChangeLogPageData).content).toContain('myMethod'));
});
});
});
20 changes: 13 additions & 7 deletions src/core/changelog/generate-change-log.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ParsedFile, UnparsedSourceFile, UserDefinedChangelogConfig } from '../shared/types';
import { ParsedFile, Skip, UnparsedSourceFile, UserDefinedChangelogConfig } from '../shared/types';
import { pipe } from 'fp-ts/function';
import * as TE from 'fp-ts/TaskEither';
import { reflectBundles } from '../reflection/reflect-source';
import { processChangelog, VersionManifest } from './process-changelog';
import { Changelog, hasChanges, processChangelog, VersionManifest } from './process-changelog';
import { convertToRenderableChangelog, RenderableChangelog } from './renderable-changelog';
import { CompilationRequest, Template } from '../template';
import { changelogTemplate } from './templates/changelog-template';
import { ReflectionErrors } from '../errors/errors';
import { apply } from '#utils/fp';
import { filterScope } from '../reflection/filter-scope';
import { skip } from '../../index';

export type ChangeLogPageData = {
content: string;
Expand All @@ -19,7 +20,7 @@ export function generateChangeLog(
oldBundles: UnparsedSourceFile[],
newBundles: UnparsedSourceFile[],
config: Omit<UserDefinedChangelogConfig, 'targetGenerator'>,
): TE.TaskEither<ReflectionErrors, ChangeLogPageData> {
): TE.TaskEither<ReflectionErrors, ChangeLogPageData | Skip> {
const filterOutOfScope = apply(filterScope, config.scope);

function reflect(sourceFiles: UnparsedSourceFile[]) {
Expand All @@ -28,18 +29,23 @@ export function generateChangeLog(

const convertToPageData = apply(toPageData, config.fileName);

function handleConversion({ changelog, newManifest }: { changelog: Changelog; newManifest: VersionManifest }) {
if (config.skipIfNoChanges && !hasChanges(changelog)) {
return skip();
}
return pipe(convertToRenderableChangelog(changelog, newManifest.types), compile, convertToPageData);
}

return pipe(
reflect(oldBundles),
TE.bindTo('oldVersion'),
TE.bind('newVersion', () => reflect(newBundles)),
TE.map(toManifests),
TE.map(({ oldManifest, newManifest }) => ({
changeLog: processChangelog(oldManifest, newManifest),
changelog: processChangelog(oldManifest, newManifest),
newManifest,
})),
TE.map(({ changeLog, newManifest }) => convertToRenderableChangelog(changeLog, newManifest.types)),
TE.map(compile),
TE.map(convertToPageData),
TE.map(handleConversion),
);
}

Expand Down
6 changes: 6 additions & 0 deletions src/core/changelog/process-changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export type Changelog = {
newOrModifiedMembers: NewOrModifiedMember[];
};

export function hasChanges(changelog: Changelog): boolean {
return (
changelog.newTypes.length > 0 || changelog.removedTypes.length > 0 || changelog.newOrModifiedMembers.length > 0
);
}

export function processChangelog(oldVersion: VersionManifest, newVersion: VersionManifest): Changelog {
return {
newTypes: getNewTypes(oldVersion, newVersion),
Expand Down
1 change: 1 addition & 0 deletions src/core/shared/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type UserDefinedChangelogConfig = {
fileName: string;
scope: string[];
exclude: string[];
skipIfNoChanges: boolean;
};

export type UserDefinedConfig = UserDefinedMarkdownConfig | UserDefinedOpenApiConfig | UserDefinedChangelogConfig;
Expand Down
1 change: 1 addition & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export const changeLogDefaults = {
fileName: 'changelog',
scope: ['global'],
exclude: [],
skipIfNoChanges: true,
};
Loading