Skip to content

Commit

Permalink
feat: about page + custom attr markdown parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ridhozhr10 committed May 27, 2024
1 parent 0b17f38 commit c14b1b5
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 15 deletions.
15 changes: 15 additions & 0 deletions _contents/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: "About :: Ridho Azhar"
description: "Software Developer based on Depok city, Indonesia"
---

![ridho](/img/me.jpg){style="float:right;max-width:200px;margin:25px 10px;"}

Hi my name is Ridho. I am a versatile software developer, with a specific focus on web development. Passionate about informatics, I am dedicated to crafting seamless user experiences and delivering my work with effective communication.
Grounded in the principles of honesty and simplicity, I approach any problem from an adaptive to a technical perspective with commitment to swift and efficient resolution.

Hi my name is Ridho. I am a versatile software developer, with a specific focus on web development. Passionate about informatics, I am dedicated to crafting seamless user experiences and delivering my work with effective communication.
Grounded in the principles of honesty and simplicity, I approach any problem from an adaptive to a technical perspective with commitment to swift and efficient resolution.

Hi my name is Ridho. I am a versatile software developer, with a specific focus on web development. Passionate about informatics, I am dedicated to crafting seamless user experiences and delivering my work with effective communication.
Grounded in the principles of honesty and simplicity, I approach any problem from an adaptive to a technical perspective with commitment to swift and efficient resolution.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
"remark-toc": "^9.0.0",
"remark-unwrap-images": "^4.0.0",
"unified": "^11.0.4"
},
"devDependencies": {
Expand Down
Binary file added public/img/me.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 2 additions & 6 deletions src/app/_components/BlogPost/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"use client";

import Link from "next/link";
import "./markdown.css";
import "./style.scss";
import "highlight.js/styles/monokai.css";
import {
BiArrowBack,
BiCalendar,
Expand All @@ -26,6 +24,7 @@ import { useRef, useState } from "react";
import { SinglePagination } from "@/lib/api";
import Comment from "./Comment";
import dayjs from "dayjs";
import Markdown from "../Markdown";

export type BlogPostProps = Post & {
content: string;
Expand Down Expand Up @@ -105,10 +104,7 @@ export default function BlogPost({
)}
</figure>
)}
<div
className="markdown-body"
dangerouslySetInnerHTML={{ __html: content }}
/>
<Markdown content={content} />
</article>
<div className="post-info">
{post.tags.length > 0 && (
Expand Down
15 changes: 15 additions & 0 deletions src/app/_components/Markdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import "highlight.js/styles/monokai.css";
import "./markdown.css";

type Props = {
content: string;
};

export default function Markdown(props: Props) {
return (
<div
className="markdown-body"
dangerouslySetInnerHTML={{ __html: props.content }}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
}

.markdown-body figure {
margin: 1em 40px;
margin: 25px 0;
}

.markdown-body hr {
Expand Down
2 changes: 1 addition & 1 deletion src/app/_components/layout/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BiMenu, BiMenuAltRight } from "react-icons/bi";
import style from "./style.module.scss";

const menuList = [
// { label: "About", href: "/about" },
{ label: "About", href: "/about" },
// { label: "CV", href: "/cv" },
// { label: "Projects", href: "/projects" },
{ label: "Posts", href: "/posts" },
Expand Down
28 changes: 22 additions & 6 deletions src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import BaseLayout from "../_components/layout/BaseLayout";
import { Metadata } from "next";
import { getAboutContent } from "@/lib/api";
import BaseLayout from "@/app/_components/layout/BaseLayout";
import mdToHtml from "@/lib/markdown";
import Markdown from "@/app/_components/Markdown";

export const metadata = {
title: "About :: Ridho Azhar",
const { title, description, content } = getAboutContent();

export const metadata: Metadata = {
title,
description,
openGraph: {
title,
description,
},
};

export default function About() {
export default async function About() {
const contentParsed = await mdToHtml(content);

return (
<BaseLayout logoText="whoami">
<main>
<h1 className="text-5xl">Coming Soon</h1>
<main className="post">
<article>
<h1 className="text-5xl font-bold my-6">About</h1>
<Markdown content={contentParsed} />
</article>
</main>
</BaseLayout>
);
Expand Down
5 changes: 5 additions & 0 deletions src/interfaces/about.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type About = {
title: string;
description: string;
content: string;
};
14 changes: 13 additions & 1 deletion src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Post } from "@/interfaces/post";
import { lstatSync, readFileSync, readdirSync } from "fs";
import { dirname, join, sep } from "path";
import { join, sep } from "path";
import matter from "gray-matter";
import { About } from "@/interfaces/about";

const postsDir = join(process.cwd(), "_contents", "posts");
const aboutFile = join(process.cwd(), "_contents", "about.md");

export function getPostSlugs(): string[] {
const readDir = (path: string): string[] | undefined => {
Expand Down Expand Up @@ -171,3 +173,13 @@ export function getAllTags(): string[] {

return res;
}

export function getAboutContent(): About {
const fileContents = readFileSync(aboutFile);
const { content, data } = matter(fileContents);
return {
content,
description: data.description,
title: data.title,
};
}
4 changes: 4 additions & 0 deletions src/lib/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import rehypeStringify from "rehype-stringify";
import remarkGfm from "remark-gfm";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import remarkUnwrapImages from "remark-unwrap-images";
import remarkToc from "remark-toc";
import remarkAttrs from "./remark-attrs";

export default async function mdToHtml(markdown: string): Promise<string> {
const result = await unified()
.use(remarkParse)
.use(remarkToc, {
maxDepth: 3,
})
.use(remarkAttrs)
.use(remarkGfm)
.use(remarkUnwrapImages)
.use(remarkRehype)
.use(rehypeSlug)
.use(rehypeStringify)
Expand Down
48 changes: 48 additions & 0 deletions src/lib/remark-attrs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// ref:
// - https://github.com/arobase-che/remark-attr/issues/22#issuecomment-1013672211
// - https://gist.github.com/cah4a/9b75c531540e2891599453863dd24881

import parseAttributes from "./util";

type Iteratee = (node: any, index: number | null, parent: any) => any;
const map = (tree: any, iteratee: Iteratee) => {
const preorder: Iteratee = (node, index, parent) => {
const newNode = iteratee(node, index, parent);

if (Array.isArray(newNode.children)) {
newNode.children = newNode.children.map((child: any, index: number) => {
return preorder(child, index, node);
});
}

return newNode;
};

return preorder(tree, null, null);
};

export default function remarkAttrs(): any {
return (tree: any) => {
return map(tree, (node, index, parent) => {
if (node.type == "text" && node.value[0] == "{" && parent && index) {
const endof = node.value.indexOf("}");
const prev = parent.children[index - 1];
const attrs =
endof && prev && parseAttributes(node.value.slice(1, endof));

if (attrs) {
prev.data = {
...prev.data,
hProperties: attrs,
};
return {
...node,
value: node.value.slice(endof + 1),
};
}
}

return node;
});
};
}
110 changes: 110 additions & 0 deletions src/lib/remark-attrs/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
class Cursor {
pos = 0;

constructor(private readonly input: string) {}

isEof() {
return this.pos >= this.input.length;
}

get char() {
return this.input[this.pos];
}

match(char: string | RegExp): boolean {
const current = this.char;

if (char instanceof RegExp) {
return !!current && char.test(current);
} else {
return current === char;
}
}

consumeTrivia() {
while (this.accept(/\s|\n|\r/)) {
//
}
}

advance() {
const char = this.char;
this.pos++;
return char;
}

accept(char: string | RegExp): boolean {
if (this.match(char)) {
this.pos++;
return true;
}

return false;
}
}

function parseKey(cursor: Cursor): string {
cursor.consumeTrivia();

if (!cursor.match(/\w/)) {
return "";
}

let key = cursor.advance();

while (cursor.match(/[\w-]/)) {
key += cursor.advance();
}

cursor.consumeTrivia();

return key;
}

function parseValue(cursor: Cursor): string {
let value = "";
let eos = " ";

if (cursor.accept('"')) {
eos = '"';
} else if (cursor.accept("'")) {
eos = "'";
}

while (!cursor.isEof() && !cursor.accept(eos)) {
value += cursor.advance();
}

cursor.consumeTrivia();

return value.trim();
}

/// simple recursive decent parser for strings like:
/// 1) something=true
/// 2) target="_blank"
/// 2) hello='there'
/// 3) easy peasy data-value=5
export default function parseAttributes(
input: string
): Record<string, string> | null {
const result: Record<string, string> = {};
const cursor = new Cursor(input);

cursor.consumeTrivia();

while (!cursor.isEof()) {
const key = parseKey(cursor);

if (!key && !cursor.isEof()) {
// something goes wrong. Parsing failed
return null;
}

if (key) {
result[key] = cursor.accept("=") ? parseValue(cursor) : "";
}
}

return result;
}

0 comments on commit c14b1b5

Please sign in to comment.