- Migrated developer docs to Twenty website - Modified User Guide and Docs layout to include sections and subsections **Section Example:** <img width="549" alt="Screenshot 2024-05-30 at 15 44 42" src="https://github.com/twentyhq/twenty/assets/102751374/41bd4037-4b76-48e6-bc79-48d3d6be9ab8"> **Subsection Example:** <img width="557" alt="Screenshot 2024-05-30 at 15 44 55" src="https://github.com/twentyhq/twenty/assets/102751374/f14c65a9-ab0c-4530-b624-5b20fc00511a"> - Created different components (Tabs, Tables, Editors etc.) for the mdx files **Tabs & Editor** <img width="665" alt="Screenshot 2024-05-30 at 15 47 39" src="https://github.com/twentyhq/twenty/assets/102751374/5166b5c7-b6cf-417d-9f29-b1f674c1c531"> **Tables** <img width="698" alt="Screenshot 2024-05-30 at 15 57 39" src="https://github.com/twentyhq/twenty/assets/102751374/2bbfe937-ec19-4004-ab00-f7a56e96db4a"> <img width="661" alt="Screenshot 2024-05-30 at 16 03 32" src="https://github.com/twentyhq/twenty/assets/102751374/ae95b47c-dd92-44f9-b535-ccdc953f71ff"> - Created a crawler for Twenty Developers (now that it will be on the twenty website). Once this PR is merged and the website is re-deployed, we need to start crawling and make sure the index name is ‘twenty-developer’ - Added a dropdown menu in the header to access User Guide and Developers + added Developers to footer https://github.com/twentyhq/twenty/assets/102751374/1bd1fbbd-1e65-4461-b18b-84d4ddbb8ea1 - Made new layout responsive Please fill in the information for each mdx file so that it can appear on its card, as well as in the ‘In this article’ section. Example with ‘Getting Started’ in the User Guide: <img width="786" alt="Screenshot 2024-05-30 at 16 29 39" src="https://github.com/twentyhq/twenty/assets/102751374/2714b01d-a664-4ddc-9291-528632ee12ea"> Example with info and sectionInfo filled in for 'Getting Started': <img width="620" alt="Screenshot 2024-05-30 at 16 33 57" src="https://github.com/twentyhq/twenty/assets/102751374/bc69e880-da6a-4b7e-bace-1effea866c11"> Please keep in mind that the images that are being used for Developers are the same as those found in User Guide and may not match the article. --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
173 lines
4.8 KiB
TypeScript
173 lines
4.8 KiB
TypeScript
import { ReactElement } from 'react';
|
|
import fs from 'fs';
|
|
import { compileMDX } from 'next-mdx-remote/rsc';
|
|
import path from 'path';
|
|
import gfm from 'remark-gfm';
|
|
|
|
import ArticleEditContent from '@/app/_components/ui/layout/articles/ArticleEditContent';
|
|
import ArticleLink from '@/app/_components/ui/layout/articles/ArticleLink';
|
|
import ArticlePropsTable from '@/app/_components/ui/layout/articles/ArticlePropsTable';
|
|
import ArticleTab from '@/app/_components/ui/layout/articles/ArticleTab';
|
|
import ArticleTable from '@/app/_components/ui/layout/articles/ArticleTable';
|
|
import ArticleTabs from '@/app/_components/ui/layout/articles/ArticleTabs';
|
|
import ArticleWarning from '@/app/_components/ui/layout/articles/ArticleWarning';
|
|
import SandpackEditor from '@/app/_components/ui/layout/articles/SandpackEditor';
|
|
|
|
interface ItemInfo {
|
|
title: string;
|
|
position?: number;
|
|
path: string;
|
|
type: 'file' | 'directory';
|
|
icon?: string;
|
|
info?: string;
|
|
image?: string;
|
|
}
|
|
|
|
export interface FileContent {
|
|
content: ReactElement;
|
|
itemInfo: ItemInfo;
|
|
}
|
|
|
|
export interface Directory {
|
|
[key: string]: FileContent | Directory | ItemInfo;
|
|
itemInfo: ItemInfo;
|
|
}
|
|
|
|
async function getFiles(
|
|
filePath: string,
|
|
basePath: string,
|
|
position = 0,
|
|
): Promise<Directory> {
|
|
const entries = fs.readdirSync(filePath, { withFileTypes: true });
|
|
|
|
const urlpath = path.toString().split(basePath);
|
|
const pathName = urlpath.length > 1 ? urlpath[1] : path.basename(filePath);
|
|
|
|
const directory: Directory = {
|
|
itemInfo: {
|
|
title: path.basename(filePath),
|
|
position,
|
|
type: 'directory',
|
|
path: pathName,
|
|
},
|
|
};
|
|
|
|
for (const entry of entries) {
|
|
if (entry.isDirectory()) {
|
|
directory[entry.name] = await getFiles(
|
|
path.join(filePath, entry.name),
|
|
basePath,
|
|
position++,
|
|
);
|
|
} else if (entry.isFile() && path.extname(entry.name) === '.mdx') {
|
|
const { content, frontmatter } = await compileMDXFile(
|
|
path.join(filePath, entry.name),
|
|
);
|
|
directory[entry.name] = {
|
|
content,
|
|
itemInfo: {
|
|
...frontmatter,
|
|
type: 'file',
|
|
path: pathName + '/' + entry.name.replace(/\.mdx$/, ''),
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
return directory;
|
|
}
|
|
|
|
async function parseFrontMatterAndCategory(
|
|
directory: Directory,
|
|
dirPath: string,
|
|
): Promise<Directory> {
|
|
const parsedDirectory: Directory = {
|
|
itemInfo: directory.itemInfo,
|
|
};
|
|
|
|
for (const entry in directory) {
|
|
if (entry !== 'itemInfo' && directory[entry] instanceof Object) {
|
|
parsedDirectory[entry] = await parseFrontMatterAndCategory(
|
|
directory[entry] as Directory,
|
|
path.join(dirPath, entry),
|
|
);
|
|
}
|
|
}
|
|
|
|
const categoryPath = path.join(dirPath, '_category_.json');
|
|
|
|
if (fs.existsSync(categoryPath)) {
|
|
const categoryJson: ItemInfo = JSON.parse(
|
|
fs.readFileSync(categoryPath, 'utf8'),
|
|
);
|
|
parsedDirectory.itemInfo = categoryJson;
|
|
}
|
|
|
|
return parsedDirectory;
|
|
}
|
|
|
|
export async function compileMDXFile(filePath: string) {
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
const compiled = await compileMDX<{ title: string; position?: number }>({
|
|
source: fileContent,
|
|
components: {
|
|
ArticleWarning(properties) {
|
|
return <ArticleWarning {...properties} />;
|
|
},
|
|
ArticleEditContent(properties) {
|
|
return <ArticleEditContent {...properties} />;
|
|
},
|
|
ArticleLink(properties) {
|
|
return <ArticleLink {...properties} />;
|
|
},
|
|
ArticleTabs(properties) {
|
|
return <ArticleTabs {...properties} />;
|
|
},
|
|
ArticleTab(properties) {
|
|
return <ArticleTab {...properties} />;
|
|
},
|
|
ArticleTable(properties) {
|
|
return <ArticleTable {...properties} />;
|
|
},
|
|
ArticlePropsTable(properties) {
|
|
return <ArticlePropsTable {...properties} />;
|
|
},
|
|
SandpackEditor(properties) {
|
|
return <SandpackEditor {...properties} />;
|
|
},
|
|
},
|
|
options: {
|
|
parseFrontmatter: true,
|
|
mdxOptions: {
|
|
development: process.env.NODE_ENV === 'development',
|
|
remarkPlugins: [gfm],
|
|
},
|
|
},
|
|
});
|
|
|
|
return compiled;
|
|
}
|
|
|
|
export async function getPosts(basePath: string): Promise<Directory> {
|
|
const postsDirectory = path.join(process.cwd(), basePath);
|
|
const directory = await getFiles(postsDirectory, basePath);
|
|
return parseFrontMatterAndCategory(directory, postsDirectory);
|
|
}
|
|
|
|
export async function getPost(
|
|
slug: string,
|
|
basePath: string,
|
|
): Promise<FileContent | null> {
|
|
const postsDirectory = path.join(process.cwd(), basePath);
|
|
const filePath = path.join(postsDirectory, `${slug}.mdx`);
|
|
|
|
if (!fs.existsSync(filePath)) {
|
|
return null;
|
|
}
|
|
const { content, frontmatter } = await compileMDXFile(filePath);
|
|
return {
|
|
content,
|
|
itemInfo: { ...frontmatter, type: 'file', path: slug },
|
|
};
|
|
}
|