refactor(code_highlight): update Code so it use syntax highlight api instead of prism on client side
+ 89
- 27
@@ -1,10 +1,10 @@
 {
-  "_generatedAtUnix": 1664716323681,
+  "_generatedAtUnix": 1664718648900,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "islands": {
     "Code": {
-      "hash": "1fb3b1015338a81ad565d1abb9b689372e7959e7",
+      "hash": "cc253764a3747f0a55ba11d339164711744f832e",
       "pathSource": "./app/islands/Code.tsx",
       "pathBundle": "./public/.islands/Code.bundle.js",
       "pathSourceMap": "./public/.islands/Code.bundle.js.map"

app/islands/Code.tsx
@@ -4,6 +4,7 @@ import { ReactIsland } from "@ethicdevs/react-monolith";
 import React, { useCallback, useEffect, useMemo, useState } from "react";
 import Prism from "prismjs";
 import styled, { css } from "styled-components";
+// import { fetch } from "cross-fetch";
 // app
 import type { AppThemeScheme, WithThemeSchemeProp } from "../types";
 import { NamedColors } from "../utils/style";

...
@@ -50,43 +51,104 @@ const Code: ReactIsland<CodeProps & WithThemeSchemeProp> = ({
   const codeBlockLineStartsAt = useMemo(() => getLineStartsAt(code), [code]);
   const lineStartAt = codeBlockLineStartsAt.before;
 
-  const computeSyntaxHighlighting = useCallback(() => {
-    const innerHtml = {
-      __html:
-        typeof window === "undefined" &&
-        Prism != null &&
-        typeof Prism !== "undefined" &&
-        "languages" in Prism &&
-        Prism.languages != null &&
-        typeof Prism.languages === "object" &&
-        language in Prism.languages &&
-        Prism.languages[language] != null
-          ? Prism.highlight(code, Prism.languages[language], language)
-          : escapeHtmlCode(code),
+  const computeSyntaxHighlightingSSR = useCallback(() => {
+    let innerHtml = {
+      __html: escapeHtmlCode(code),
     };
 
-    const linesCount = innerHtml.__html.split("\n").length;
-
-    innerHtml.__html += `\n<span aria-hidden="true" class="line-numbers-rows">`;
-    for (let i = linesCount; i > 0; i--) {
-      const lineNumber = lineStartAt + (linesCount - i + 1);
-      innerHtml.__html += `<a id="l-${lineNumber}" href="#l-${lineNumber}" data-line-number="${lineNumber}"></a>`;
+    if (
+      "languages" in Prism &&
+      Prism.languages != null &&
+      typeof Prism.languages === "object" &&
+      language in Prism.languages &&
+      Prism.languages[language] != null
+    ) {
+      innerHtml.__html = Prism.highlight(
+        code,
+        Prism.languages[language],
+        language
+      );
     }
-    innerHtml.__html += `</span>`;
 
     return innerHtml;
-  }, [code, language, lineStartAt]);
+  }, [code, language]);
+
+  const appendLineNumbers = useCallback(
+    (inHtml: {
+      __html: string;
+    }): {
+      __html: string;
+    } => {
+      const copyInnerHtml = { __html: inHtml.__html };
+      const linesCount = copyInnerHtml.__html.split("\n").length;
+      copyInnerHtml.__html += `\n<span aria-hidden="true" class="line-numbers-rows">`;
+      for (let i = linesCount; i > 0; i--) {
+        const lineNumber = lineStartAt + (linesCount - i + 1);
+        copyInnerHtml.__html += `<a id="l-${lineNumber}" href="#l-${lineNumber}" data-line-number="${lineNumber}"></a>`;
+      }
+      copyInnerHtml.__html += `</span>`;
+      return copyInnerHtml;
+    },
+    [lineStartAt]
+  );
 
   const [innerHtml, setInnerHtml] = useState<{ __html: string }>(
-    computeSyntaxHighlighting()
+    appendLineNumbers(computeSyntaxHighlightingSSR())
   );
 
+  const getHighlightedCodeInnerHtml = useCallback(async (): Promise<{
+    __html: string;
+  }> => {
+    let inHtml;
+    if (typeof window === "undefined") {
+      inHtml = computeSyntaxHighlightingSSR();
+    } else {
+      const { protocol, hostname, port } = new URL(window.location.href);
+      const res = await fetch(
+        `${protocol}//${hostname}:${port}/api/syntax/highlight/html`,
+        {
+          method: "POST",
+          headers: {
+            Accept: "application/json",
+            "Content-Type": "application/json",
+          },
+          body: JSON.stringify({
+            code,
+            language,
+            theme_scheme: themeScheme,
+          }),
+        }
+      );
+      const jsonRes = await res.json();
+      inHtml = {
+        __html: jsonRes.html,
+      };
+    }
+    inHtml = appendLineNumbers(inHtml);
+    return inHtml;
+  }, [
+    code,
+    language,
+    themeScheme,
+    appendLineNumbers,
+    computeSyntaxHighlightingSSR,
+  ]);
+
+  const getHighlightedCodeAsync = useCallback(async () => {
+    try {
+      const innerHtmlGenerated = await getHighlightedCodeInnerHtml();
+      setInnerHtml(innerHtmlGenerated);
+    } catch (err) {
+      // console.error((err as Error).message);
+    }
+  }, [getHighlightedCodeInnerHtml, setInnerHtml]);
+
   const onClientSideRouterLoadComplete = useCallback(() => {
-    setInnerHtml(computeSyntaxHighlighting());
-  }, [computeSyntaxHighlighting, setInnerHtml]);
+    getHighlightedCodeAsync();
+  }, [getHighlightedCodeAsync]);
 
   useEffect(() => {
-    setInnerHtml(computeSyntaxHighlighting());
+    onClientSideRouterLoadComplete();
     document.addEventListener(
       ClientSideRouterEvents.NAVIGATED,
       onClientSideRouterLoadComplete