Files
twenty/packages/twenty-website/src/app/(public)/releases/utils/get-releases.tsx
Baptiste Devessier 2c465bd42e Integrate Keystatic to edit twenty.com content (#10709)
This PR introduces Keystatic to let us edit twenty.com's content with a
CMS. For now, we'll focus on creating release notes through Keystatic as
it uses quite simple Markdown. Other types of content will need some
refactoring to work with Keystatic.


https://github.com/user-attachments/assets/e9f85bbf-daff-4b41-bc97-d1baf63758b2

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2025-03-07 07:59:06 +01:00

71 lines
2.2 KiB
TypeScript

import fs from 'fs';
import matter from 'gray-matter';
import { compileMDX } from 'next-mdx-remote/rsc';
import { JSXElementConstructor, ReactElement } from 'react';
import gfm from 'remark-gfm';
import { ReleaseNote } from '@/app/(public)/releases/api/route';
import { compareSemanticVersions } from '@/shared-utils/compareSemanticVersions';
// WARNING: This API is used by twenty-front, not just by twenty-website
// Make sure you don't change it without updating twenty-front at the same time
export async function getReleases(baseUrl?: string): Promise<ReleaseNote[]> {
const files = fs.readdirSync('src/content/releases');
const releasenotes: ReleaseNote[] = [];
for (const fileName of files) {
if (!fileName.endsWith('.md') && !fileName.endsWith('.mdx')) {
continue;
}
const file = fs.readFileSync(`src/content/releases/${fileName}`, 'utf-8');
const { data: frontmatter, content } = matter(file);
let updatedContent;
if (baseUrl) {
updatedContent = content.replace(
/!\[(.*?)\]\((?!http)(.*?)\)/g,
(match: string, alt: string, src: string) => {
// Check if src is a relative path (not starting with http:// or https://)
if (!src.startsWith('/')) {
src = `${baseUrl}/${src}`;
} else {
src = `${baseUrl}${src}`;
}
return `![${alt}](${src})`;
},
);
}
releasenotes.push({
slug: fileName.slice(0, -4),
date: frontmatter.Date,
release: frontmatter.release,
content: updatedContent ?? content,
});
}
releasenotes.sort((a, b) => compareSemanticVersions(b.release, a.release));
return releasenotes;
}
export async function getMdxReleasesContent(
releases: ReleaseNote[],
): Promise<ReactElement<any, string | JSXElementConstructor<any>>[]> {
const mdxSourcesPromises = releases.map(async (release) => {
const mdxSource = await compileMDX<{ title: string; position?: number }>({
source: release.content,
options: {
parseFrontmatter: true,
mdxOptions: {
development: process.env.NODE_ENV === 'development',
remarkPlugins: [gfm],
},
},
});
return mdxSource.content;
});
return await Promise.all(mdxSourcesPromises);
}