WIP: updated HTMLReactParserOptions replace function, from complex if else to handler pattern

This commit is contained in:
Suyono 2024-02-22 14:06:43 +11:00
parent a607a4528e
commit a1cba242e9
5 changed files with 206 additions and 61 deletions

2
.gitignore vendored
View File

@ -5,6 +5,8 @@
/.pnp /.pnp
.pnp.js .pnp.js
/.local/
# testing # testing
/coverage /coverage

View File

@ -0,0 +1,109 @@
import {ReactElement} from "react";
import {domToReact, Element} from "html-react-parser";
import {nunito_sans, raleway} from "@/app/fonts";
import {Code} from "bright";
import {mark} from "@/bright-extension/mark";
export type nodeHandlerResult = {match :boolean, element? :ReactElement}
export type nodeHandler = (node :Element) => nodeHandlerResult
function h1(node: Element): nodeHandlerResult {
if (node.name === "h1") {
if (node.attribs.class === "title") {
return {
match: true,
element: (
<h1 className={`${raleway.className} mx-auto w-224 text-4xl`}>
{domToReact(node.children)}
</h1>
),
};
}
return {
match: true,
element: (
<h1 className={`${raleway.className} mx-auto w-224 text-3xl`}>
{domToReact(node.children)}
</h1>
),
};
}
return { match: false};
}
function h2(node: Element): nodeHandlerResult {
if (node.name === "h2") {
return {
match: true,
element: (
<h1 className={`${raleway.className} mx-auto w-224 text-2xl`}>
{domToReact(node.children)}
</h1>
)
};
}
return {match: false};
}
function h3(node: Element): nodeHandlerResult {
if (node.name === "h3") {
return {
match: true,
element: (
<h1 className={`${raleway.className} mx-auto w-224 text-xl`}>
{domToReact(node.children)}
</h1>
)
};
}
return {match: false};
}
function code(node: Element): nodeHandlerResult {
if (node.name === "code") {
let linenumber = false;
if ("class" in node.attribs) {
const classes = node.attribs.class.split(" ");
if (classes.includes("linenumber")) {
linenumber = true;
}
}
return {
match: true,
element: (
<div className={`w-224 mx-auto`}>
<Code
lang={`${node.attribs.lang}`}
lineNumbers={linenumber}
extensions={[mark]}
>
{domToReact(node.children)}
</Code>
</div>
)
};
}
return {match: false};
}
function p(node: Element): nodeHandlerResult {
if (node.name === "p") {
if (node.attribs.class === "paragraph") {
return {
match: true,
element: (
<h1 className={`${nunito_sans.className} mx-auto w-224`}>
{domToReact(node.children)}
</h1>
)
};
}
}
return {match: false};
}
export const handlers: nodeHandler[] = [h1, h2, h3, code, p];

View File

@ -1,69 +1,21 @@
import { getPost } from "@/backend/post"; import { getPost } from "@/backend/post";
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import { JSDOM } from "jsdom"; import { JSDOM } from "jsdom";
import { nunito_sans, raleway } from "@/app/fonts";
import parse, { import parse, {
domToReact,
Element, Element,
// domToReact,
HTMLReactParserOptions, HTMLReactParserOptions,
} from "html-react-parser"; } from "html-react-parser";
import { DummyPostSlug, DummyPostString } from "@/components/dummyPost"; import { DummyPostSlug, DummyPostString } from "@/components/dummyPost";
import { Code } from "bright"; import { handlers } from "@/app/post/[slug]/nodeHandler";
import { mark } from "@/bright-extension/mark";
const options: HTMLReactParserOptions = { const options: HTMLReactParserOptions = {
replace: (domNode) => { replace: (domNode) => {
if (domNode instanceof Element && domNode.attribs) { for (let handler of handlers) {
// console.log(domNode.attribs) if (domNode instanceof Element && domNode.attribs) {
if (domNode.name === "h1") { let result = handler(domNode)
if (domNode.attribs.class === "title") { if (result.match && typeof result.element !== 'undefined') {
return ( return result.element
<h1 className={`${raleway.className} mx-auto w-224 text-4xl`}>
{domToReact(domNode.children)}
</h1>
);
} else {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-3xl`}>
{domToReact(domNode.children)}
</h1>
);
}
} else if (domNode.name === "h2") {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-2xl`}>
{domToReact(domNode.children)}
</h1>
);
} else if (domNode.name === "h3") {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-xl`}>
{domToReact(domNode.children)}
</h1>
);
} else if (domNode.name === "code") {
let linenumber = false
if ('class' in domNode.attribs) {
const classes = domNode.attribs.class.split(" ");
if (classes.includes('linenumber')) {
linenumber = true;
}
}
return (
<div className={`w-224 mx-auto`}>
<Code lang={`${domNode.attribs.lang}`} lineNumbers={linenumber} extensions={[mark]}>
{domToReact(domNode.children)}
</Code>
</div>
);
} else if (domNode.name === "p") {
// console.log(domNode.children)
if (domNode.attribs.class === "paragraph") {
return (
<h1 className={`${nunito_sans.className} mx-auto w-224`}>
{domToReact(domNode.children)}
</h1>
);
} }
} }
} }
@ -71,7 +23,7 @@ const options: HTMLReactParserOptions = {
}; };
export default async function Post({ params }: { params: { slug: string } }) { export default async function Post({ params }: { params: { slug: string } }) {
let content = ""; let content;
const dummySlug = await DummyPostSlug(); const dummySlug = await DummyPostSlug();
if (dummySlug === params.slug) { if (dummySlug === params.slug) {

View File

@ -1,11 +1,25 @@
import mysql, { PoolOptions } from "mysql2"; import mysql, { PoolOptions } from "mysql2";
import * as fs from 'fs';
import * as appEnv from "./env";
if (typeof process.env.MYSQL_SSL_CA === 'undefined') {
throw new Error("missing MYSQL_SSL_CA")
}
if (typeof process.env.MYSQL_SSL_KEY === 'undefined') {
throw new Error("missing MYSQL_SSL_KEY")
}
if (typeof process.env.MYSQL_SSL_CERT === 'undefined') {
throw new Error("missing MYSQL_SSL_CERT")
}
const access: PoolOptions = { const access: PoolOptions = {
host: process.env.MYSQL_HOST, host: appEnv.getMysqlHost(),
port: 'MYSQL_PORT' in process.env && typeof process.env.MYSQL_PORT === 'string' ? parseInt(process.env.MYSQL_PORT) : 3306, port: appEnv.getMysqlPort(),
user: process.env.MYSQL_USER, user: appEnv.getMysqlUser(),
password: process.env.MYSQL_PASSWORD, password: appEnv.getMysqlPassword(),
database: 'MYSQL_DATABASE' in process.env ? process.env.MYSQL_DATABASE : 'blog', database: appEnv.getMysqlDatabase(),
waitForConnections: true, waitForConnections: true,
connectionLimit: 10, connectionLimit: 10,
maxIdle: 10, maxIdle: 10,
@ -13,6 +27,11 @@ const access: PoolOptions = {
queueLimit: 0, queueLimit: 0,
enableKeepAlive: true, enableKeepAlive: true,
keepAliveInitialDelay: 0, keepAliveInitialDelay: 0,
ssl: {
ca: fs.readFileSync(appEnv.getMysqlSslCaFile()),
key: fs.readFileSync(appEnv.getMysqlSslKeyFile()),
cert: fs.readFileSync(appEnv.getMysqlSslCertFile())
}
} }
export const pool = mysql.createPool(access) export const pool = mysql.createPool(access)

63
backend/env.ts Normal file
View File

@ -0,0 +1,63 @@
export function getMysqlHost(): string {
if (typeof process.env.MYSQL_HOST === 'undefined') {
throw new Error("missing env MYSQL_HOST")
}
return process.env.MYSQL_HOST
}
export function getMysqlPort(): number {
if (typeof process.env.MYSQL_PORT === 'undefined') {
throw new Error("missing env MYSQL_PORT")
}
return parseInt(process.env.MYSQL_PORT)
}
export function getMysqlUser(): string {
if (typeof process.env.MYSQL_USER === 'undefined') {
throw new Error("missing env MYSQL_USER")
}
return process.env.MYSQL_USER
}
export function getMysqlPassword(): string {
if (typeof process.env.MYSQL_PASSWORD === 'undefined') {
throw new Error("missing env MYSQL_PASSWORD")
}
return process.env.MYSQL_PASSWORD
}
export function getMysqlDatabase(): string {
if (typeof process.env.MYSQL_DATABASE === 'undefined') {
throw new Error("missing env MYSQL_DATABASE")
}
return process.env.MYSQL_DATABASE
}
export function getMysqlSslCaFile(): string {
if (typeof process.env.MYSQL_SSL_CA === 'undefined') {
throw new Error("missing env MYSQL_SSL_CA")
}
return process.env.MYSQL_SSL_CA
}
export function getMysqlSslCertFile(): string {
if (typeof process.env.MYSQL_SSL_CERT === 'undefined') {
throw new Error("missing env MYSQL_SSL_CERT")
}
return process.env.MYSQL_SSL_CERT
}
export function getMysqlSslKeyFile(): string {
if (typeof process.env.MYSQL_SSL_KEY === 'undefined') {
throw new Error("missing env MYSQL_SSL_KEY")
}
return process.env.MYSQL_SSL_KEY
}