Make markdown work recursively
Some checks failed
Deploy Frontend / docker (17, 3.8.5) (push) Failing after 2m53s

This commit is contained in:
Braydon 2024-04-20 18:07:13 -04:00
parent a665d5d0ba
commit 5e67ce1f34
9 changed files with 104 additions and 15 deletions

Binary file not shown.

@ -39,6 +39,7 @@
"react-hook-form": "^7.51.3",
"remark-gfm": "^4.0.0",
"restfulmc-lib": "^1.1.3",
"shadcn-ui": "0.8.0",
"sharp": "^0.33.3",
"sonner": "^1.4.41",
"tailwind-merge": "^2.2.2",

@ -3,6 +3,15 @@ import { getDocsContent } from "@/lib/mdxUtils";
import { PageProps } from "@/types/page";
import { notFound } from "next/navigation";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { capitalize } from "@/lib/stringUtils";
/**
* The page to display content
* from an MDX file on the docs.
@ -10,17 +19,47 @@ import { notFound } from "next/navigation";
* @param slug the slug of the mdx file
* @return the page jsx
*/
const ContentPage = ({ params: { slug } }: PageProps): ReactElement => {
const ContentPage = ({ params }: PageProps): ReactElement => {
const slug: string = ((params.slug as string[]) || undefined)?.join("/"); // The slug of the content
const content: DocsContentMetadata | undefined = getDocsContent().find(
(metadata: DocsContentMetadata): boolean =>
metadata.slug === (slug ? slug[0] : "home")
metadata.slug === (slug || "home")
); // Get the content to display based on the provided slug
// Return a 404 if the content is not found
if (!content) {
notFound();
}
const splitSlug: string[] = content.slug?.split("/") || [];
return <main>{content.title}</main>;
return (
<main>
{/* Breadcrumb */}
<Breadcrumb>
<BreadcrumbList className="text-minecraft-green-4">
{["docs", ...splitSlug].map(
(part: string, index: number): ReactElement => (
<div className="flex items-center" key={index}>
<BreadcrumbItem>
<BreadcrumbLink
href={
(index > 0 ? "/docs" : "") +
`/${part}`
}
>
{capitalize(part)}
</BreadcrumbLink>
</BreadcrumbItem>
{index < splitSlug.length && (
<BreadcrumbSeparator className="pl-1.5" />
)}
</div>
)
)}
</BreadcrumbList>
</Breadcrumb>
</main>
);
};
export default ContentPage;

@ -12,9 +12,11 @@ const DocumentationLayout = ({
}: Readonly<{
children: ReactNode;
}>): ReactElement => (
<section className="h-screen flex flex-col justify-center items-center">
<Sidebar />
{children}
<section className="h-screen flex justify-center items-center">
<div className="flex gap-10">
<Sidebar />
{children}
</div>
</section>
);
export default DocumentationLayout;

@ -1,25 +1,40 @@
import * as fs from "node:fs";
import path from "node:path";
import { Stats } from "node:fs";
/**
* The regex to match for metadata.
*/
const METADATA_REGEX: RegExp = /---\s*([\s\S]*?)\s*---/;
/**
* The directory docs are stored in.
*/
const DOCS_DIR: string = path.join(process.cwd(), "docs");
/**
* Get the content to
* display in the docs.
*/
export const getDocsContent = (): DocsContentMetadata[] =>
getMetadata<DocsContentMetadata>(path.join(process.cwd(), "docs"));
export const getDocsContent = (): DocsContentMetadata[] => {
const content: DocsContentMetadata[] = [];
for (let directory of getRecursiveDirectories(DOCS_DIR)) {
content.push(...getMetadata<DocsContentMetadata>(DOCS_DIR, directory));
}
return content;
};
/**
* Get the metadata of mdx
* files in the given directory.
*
* @param parent the parent directory to search
* @param directory the directory to search
*/
export const getMetadata = <T extends MDXMetadata>(directory: string): T[] => {
const getMetadata = <T extends MDXMetadata>(
parent: string,
directory: string
): T[] => {
const files: string[] = fs
.readdirSync(directory)
.filter((file: string): boolean => {
@ -29,8 +44,12 @@ export const getMetadata = <T extends MDXMetadata>(directory: string): T[] => {
return files.map((file: string): T => {
const filePath: string = path.join(directory, file); // The path of the file
return {
slug: filePath
.replace(parent, "")
.replace(/\\/g, "/") // Normalize the path
.replace(/\.mdx?$/, "")
.substring(1),
...parseMetadata<T>(fs.readFileSync(filePath, "utf-8")),
slug: path.basename(file, path.extname(file)),
}; // Map each file to its metadata
});
};
@ -65,3 +84,24 @@ const parseMetadata = <T extends MDXMetadata>(content: string): T => {
// slug is empty, and is defined later on.
return { ...metadata, content } as T;
};
/**
* Get directories recursively
* in the given directory.
*
* @param directory the directory to search
* @return the directories
*/
const getRecursiveDirectories = (directory: string): string[] => {
const directories: string[] = [directory]; // The directories to return
for (let sub of fs.readdirSync(directory)) {
const subDirPath: string = path.join(directory, sub); // The sub dir path
const stats: Stats = fs.statSync(subDirPath); // Get file stats
if (stats.isDirectory()) {
directories.push(...getRecursiveDirectories(subDirPath)); // Recursively get directories
}
}
return directories;
};

@ -1,4 +1,5 @@
import { ReactElement } from "react";
import { Input } from "@/components/ui/input";
/**
* The sidebar for the docs page.
@ -6,8 +7,14 @@ import { ReactElement } from "react";
* @returns the sidebar jsx
*/
const Sidebar = (): ReactElement => (
<div className="absolute left-20 h-72 p-5 bg-muted rounded-xl">
<h1 className="font-semibold uppercase">SIDEBAR</h1>
<div className="w-60 h-80 px-3 py-5 flex justify-center bg-muted border border-zinc-700/70 rounded-lg">
{/* Search */}
<Input
type="search"
name="search"
placeholder="Quick search..."
disabled
/>
</div>
);
export default Sidebar;

@ -25,7 +25,7 @@ type MDXMetadata = {
/**
* The slug of the file, defined once read.
*/
slug: string;
slug?: string | undefined;
/**
* The metadata of the file.

@ -2,6 +2,6 @@
* Props for a page.
*/
export type PageProps = {
params: { slug: string };
searchParams: { [key: string]: string | string[] | undefined };
params: { slug: string[] };
searchParams: { [key: string]: string | string[] | undefined };
};