diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-14 23:55:20 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-14 23:55:20 +0200 |
| commit | 8fe66090e7d4a3a0dcde02cfa828a5c3bee63122 (patch) | |
| tree | d7dfd95a38d8323f1e3771646c2d14dbfd50f18d /apps/web/src/app/changelog/utils.ts | |
| parent | 91e1232112421438ce73b9361896f9b58c84fb3e (diff) | |
| download | opencut-8fe66090e7d4a3a0dcde02cfa828a5c3bee63122.tar.gz | |
feat: add support for technical details in changelog entries
Diffstat (limited to 'apps/web/src/app/changelog/utils.ts')
| -rw-r--r-- | apps/web/src/app/changelog/utils.ts | 135 |
1 files changed, 82 insertions, 53 deletions
diff --git a/apps/web/src/app/changelog/utils.ts b/apps/web/src/app/changelog/utils.ts index 3f38ab6b..682d376a 100644 --- a/apps/web/src/app/changelog/utils.ts +++ b/apps/web/src/app/changelog/utils.ts @@ -1,53 +1,82 @@ -import { allChangelogs } from "content-collections";
-
-export type Change = { type: string; text: string };
-export type Release = (typeof allChangelogs)[number];
-
-const knownSectionOrder = ["new", "improved", "fixed", "breaking"];
-
-const knownSectionTitles: Record<string, string> = {
- new: "Features",
- improved: "Improvements",
- fixed: "Fixes",
- breaking: "Breaking Changes",
-};
-
-export function getSectionTitle({ type }: { type: string }): string {
- return (
- knownSectionTitles[type] ?? type.charAt(0).toUpperCase() + type.slice(1)
- );
-}
-
-export function groupAndOrderChanges({ changes }: { changes: Change[] }) {
- const grouped = changes.reduce<Record<string, Change[]>>((acc, change) => {
- if (!acc[change.type]) acc[change.type] = [];
- acc[change.type].push(change);
- return acc;
- }, {});
-
- const customTypes = Object.keys(grouped).filter(
- (type) => !knownSectionOrder.includes(type),
- );
- const orderedTypes = [
- ...knownSectionOrder.filter((type) => grouped[type]?.length > 0),
- ...customTypes,
- ];
-
- return { grouped, orderedTypes };
-}
-
-function isPublishedRelease({ published }: Release) {
- return published !== false;
-}
-
-export function getSortedReleases() {
- return allChangelogs
- .filter(isPublishedRelease)
- .sort((a, b) =>
- b.version.localeCompare(a.version, undefined, { numeric: true }),
- );
-}
-
-export function getReleaseByVersion({ version }: { version: string }) {
- return getSortedReleases().find((release) => release.version === version);
-}
+import { allChangelogs } from "content-collections"; + +export type Change = { type: string; text: string }; +export type Release = (typeof allChangelogs)[number]; + +type ChangeSectionConfig = { + title: string; + order: number; + collapsible?: boolean; +}; + +const knownSectionConfigs: Record<string, ChangeSectionConfig> = { + new: { title: "Features", order: 0 }, + improved: { title: "Improvements", order: 1 }, + fixed: { title: "Fixes", order: 2 }, + breaking: { title: "Breaking Changes", order: 3 }, + technical: { + title: "Technical details", + order: 4, + collapsible: true, + }, +}; + +function getSectionConfig({ type }: { type: string }): ChangeSectionConfig { + return ( + knownSectionConfigs[type] ?? { + title: type.charAt(0).toUpperCase() + type.slice(1), + order: Number.MAX_SAFE_INTEGER, + } + ); +} + +export function getSectionTitle({ type }: { type: string }): string { + return getSectionConfig({ type }).title; +} + +export function isSectionCollapsible({ type }: { type: string }): boolean { + return getSectionConfig({ type }).collapsible ?? false; +} + +export function groupAndOrderChanges({ changes }: { changes: Change[] }) { + const typeEncounterOrder = new Map<string, number>(); + const grouped = changes.reduce<Record<string, Change[]>>((acc, change) => { + if (!typeEncounterOrder.has(change.type)) { + typeEncounterOrder.set(change.type, typeEncounterOrder.size); + } + if (!acc[change.type]) acc[change.type] = []; + acc[change.type].push(change); + return acc; + }, {}); + + const orderedTypes = Object.keys(grouped).sort((left, right) => { + const orderDifference = + getSectionConfig({ type: left }).order - + getSectionConfig({ type: right }).order; + if (orderDifference !== 0) { + return orderDifference; + } + return ( + (typeEncounterOrder.get(left) ?? Number.MAX_SAFE_INTEGER) - + (typeEncounterOrder.get(right) ?? Number.MAX_SAFE_INTEGER) + ); + }); + + return { grouped, orderedTypes }; +} + +function isPublishedRelease({ published }: Release) { + return published !== false; +} + +export function getSortedReleases() { + return allChangelogs + .filter(isPublishedRelease) + .sort((a, b) => + b.version.localeCompare(a.version, undefined, { numeric: true }), + ); +} + +export function getReleaseByVersion({ version }: { version: string }) { + return getSortedReleases().find((release) => release.version === version); +} |
