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", "react-hook-form": "^7.51.3",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"restfulmc-lib": "^1.1.3", "restfulmc-lib": "^1.1.3",
"shadcn-ui": "0.8.0",
"sharp": "^0.33.3", "sharp": "^0.33.3",
"sonner": "^1.4.41", "sonner": "^1.4.41",
"tailwind-merge": "^2.2.2", "tailwind-merge": "^2.2.2",

@ -3,6 +3,15 @@ import { getDocsContent } from "@/lib/mdxUtils";
import { PageProps } from "@/types/page"; import { PageProps } from "@/types/page";
import { notFound } from "next/navigation"; 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 * The page to display content
* from an MDX file on the docs. * from an MDX file on the docs.
@ -10,17 +19,47 @@ import { notFound } from "next/navigation";
* @param slug the slug of the mdx file * @param slug the slug of the mdx file
* @return the page jsx * @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( const content: DocsContentMetadata | undefined = getDocsContent().find(
(metadata: DocsContentMetadata): boolean => (metadata: DocsContentMetadata): boolean =>
metadata.slug === (slug ? slug[0] : "home") metadata.slug === (slug || "home")
); // Get the content to display based on the provided slug ); // Get the content to display based on the provided slug
// Return a 404 if the content is not found // Return a 404 if the content is not found
if (!content) { if (!content) {
notFound(); 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; export default ContentPage;

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

@ -1,25 +1,40 @@
import * as fs from "node:fs"; import * as fs from "node:fs";
import path from "node:path"; import path from "node:path";
import { Stats } from "node:fs";
/** /**
* The regex to match for metadata. * The regex to match for metadata.
*/ */
const METADATA_REGEX: RegExp = /---\s*([\s\S]*?)\s*---/; 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 * Get the content to
* display in the docs. * display in the docs.
*/ */
export const getDocsContent = (): DocsContentMetadata[] => export const getDocsContent = (): DocsContentMetadata[] => {
getMetadata<DocsContentMetadata>(path.join(process.cwd(), "docs")); const content: DocsContentMetadata[] = [];
for (let directory of getRecursiveDirectories(DOCS_DIR)) {
content.push(...getMetadata<DocsContentMetadata>(DOCS_DIR, directory));
}
return content;
};
/** /**
* Get the metadata of mdx * Get the metadata of mdx
* files in the given directory. * files in the given directory.
* *
* @param parent the parent directory to search
* @param directory the 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 const files: string[] = fs
.readdirSync(directory) .readdirSync(directory)
.filter((file: string): boolean => { .filter((file: string): boolean => {
@ -29,8 +44,12 @@ export const getMetadata = <T extends MDXMetadata>(directory: string): T[] => {
return files.map((file: string): T => { return files.map((file: string): T => {
const filePath: string = path.join(directory, file); // The path of the file const filePath: string = path.join(directory, file); // The path of the file
return { return {
slug: filePath
.replace(parent, "")
.replace(/\\/g, "/") // Normalize the path
.replace(/\.mdx?$/, "")
.substring(1),
...parseMetadata<T>(fs.readFileSync(filePath, "utf-8")), ...parseMetadata<T>(fs.readFileSync(filePath, "utf-8")),
slug: path.basename(file, path.extname(file)),
}; // Map each file to its metadata }; // 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. // slug is empty, and is defined later on.
return { ...metadata, content } as T; 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 { ReactElement } from "react";
import { Input } from "@/components/ui/input";
/** /**
* The sidebar for the docs page. * The sidebar for the docs page.
@ -6,8 +7,14 @@ import { ReactElement } from "react";
* @returns the sidebar jsx * @returns the sidebar jsx
*/ */
const Sidebar = (): ReactElement => ( const Sidebar = (): ReactElement => (
<div className="absolute left-20 h-72 p-5 bg-muted rounded-xl"> <div className="w-60 h-80 px-3 py-5 flex justify-center bg-muted border border-zinc-700/70 rounded-lg">
<h1 className="font-semibold uppercase">SIDEBAR</h1> {/* Search */}
<Input
type="search"
name="search"
placeholder="Quick search..."
disabled
/>
</div> </div>
); );
export default Sidebar; export default Sidebar;

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

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