Ryota Kondo

Ryota Kondo

2023/06/05

microCMS|新リッチエディタでシンタックスハイライトとファイル名表示をする方法

2023/05/24に正式リリースされたmicroCMSの新リッチエディタは、コードブロックについても機能が追加されており、ブロックごとにプログラミング言語とファイル名を入力できるようになっています。

今回はこの機能追加に合わせた下の2つの方法について説明します。

  • 入力されたプログラミング言語でシンタックスハイライトする方法
  • 入力されたファイル名をフロントエンドで表示する方法

概要

処理の分岐

まず、microCMS管理画面でのコードブロックは下の様になっています。

コードブロック

プログラミング言語とファイル名を入力するかしないかは自由となっていますので、それぞれに対応した処理を行う必要があります。今回説明する方法では下の様に処理を分けるようにしました。

プログラミング言語の処理

入力あり

入力したプログラミング言語でシンタックスハイライトを実施

入力なし

自動で言語を判定し、シンタックスハイライトを実施

ファイル名の処理

入力あり

ファイル名をコードブロックに表示

入力なし

何も表示しない

処理の判定

APIから返却されるコードブロックのレスポンスは、入力の有無により下の様に変わります。このレスポンスの変更内容を利用して、実際にどの処理を実行するか判定するようにします。

プログラミング言語・ファイル名の入力がある場合のレスポンス

<div data-filename="{入力したファイル名}">
  <pre>
    <code class="language-{入力したプログラミング言語}">{コードブロックに入力した内容}</code>
  </pre>
</div>

プログラミング言語・ファイル名の入力がない場合のレスポンス

<pre>
  <code>{コードブロックに入力した内容}</code>
</pre>

プログラミング言語を入力した場合、codeタグにlanguage-{入力したプログラミング言語}classが追加されます。一方、ファイル名を入力した場合、data-filename属性を持つdivタグが追加されます。

概要は以上です。次は具体的な実施方法について説明します。

実施方法

例としてフロントエンドフレームワークにNext.jsを使った場合で説明します。

ライブラリのインストール

レスポンスの解析・変更を行うために、HTMLパーサーライブラリのcheerioを使用します。また、シンタックスハイライターとしてhighlight.jsを使用します。

下のコマンドを実行して、これらのライブラリをインストールしてください。

npm install cheerio highlight.js

表示ページの実装

コードブロックを表示するページ(例としてpages/blog/[id].tsx)に対して、下の2点の処理を追加してください。

  • 追加する処理①・・・必要なライブラリのインポート
  • 追加する処理②・・・APIから取得したリッチエディタのHTMLの解析と編集
pages/blog/[id].tsx
import { client } from "../../libs/client";
import { Blog } from "../../types/blog";

// ==================== 追加する処理① ここから ====================

import hljs, { HighlightResult } from "highlight.js";
import { load } from "cheerio";

// ==================== 追加する処理① ここまで ====================

type Props = {
  blog: Blog;
};

export default function BlogId({ blog }: Props) {
  return (
    <main>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.content}`,
        }}
      />
    </main>
  );
}

// 静的生成のためのパスを指定
export const getStaticPaths = async () => {
  const data = await client.get({ endpoint: "blogs" });

  const paths = data.contents.map((content: any) => `/blog/${content.id}`);
  return { paths, fallback: false };
};

// データをテンプレートに受け渡す部分の処理
export const getStaticProps = async (context: any) => {
  const id = context.params.id;
  const data = await client.get({ endpoint: "blogs", contentId: id });


  // ==================== 追加する処理② ここから ====================

  // APIから取得したリッチエディタのHTMLからcheerioオブジェクトを生成
  const $ = load(data.content);

  // コードブロックのファイル名が入力されている場合の処理
  $("div[data-filename]").each((_, elm) => {
    // data-filename属性の値を持つspanを
    // <div data-filename="{入力したファイル名}">の最初の子要素として追加
    $(elm).prepend(`<span>${$(elm).attr("data-filename")}</span>`);
  });

  // コードブロックのシンタックスハイライトを行う
  $("pre code").each((_, elm) => {
    const language = $(elm).attr("class") || "";
    let result: HighlightResult;

    if (language == "") {
      // 言語が入力なしの場合、自動判定
      result = hljs.highlightAuto($(elm).text());
    } else {
      // 言語が入力ありの場合、入力された言語で判定
      result = hljs.highlight($(elm).text(), {
        language: language.replace("language-", ""),
      });
    }
    $(elm).html(result.value);
    $(elm).addClass("hljs");
  });

  // 編集したHTMLを再設定
  data.content = $.html();
  
  // ==================== 追加する処理② ここまで ====================


  return {
    props: {
      blog: data,
    },
  };
};

CSSの実装

シンタックスハイライトをするために、highlight.jsのデモサイトから好きなテーマを選び、cssをimportしてください。

// 例 A11Y Darkの場合
import "highlight.js/styles/a11y-dark.css";

次にファイル名を表示するためにCSSの実装を行います。これまでに実施した「表示ページの実装」を行うことで、下の様にコードブロックのレスポンスに「入力したファイル名」を持つspan要素が追加されます。このspan要素をもとにファイル名を表示するよう、CSSを実装してください。

<div data-filename="{入力したファイル名}">
  <span>{入力したファイル名}</span> // 「表示ページの実装」により追加される
  <pre>
    <code class="language-{入力したプログラミング言語}">{コードブロックに入力した内容}</code>
  </pre>
</div>

参考として、下のイメージのようにタブ風の表示をする場合はこのようなCSSになります。

サンプルタブ風表示
sample.css
/* コードブロック */
pre {
  overflow-x: auto;
  margin-top: 16px;
  margin-bottom: 0px;
  border-radius: 5px;
}

/* コードブロック(ファイル名が入力ありの場合) */
div[data-filename] {
  margin-top: 16px;
}

div[data-filename] > span {
  display: inline-block;
  background-color: #dfdfdf;
  text-align: center;
  padding: 3px 15px;
  border-radius: 5px 5px 0px 0px;
}

div[data-filename] > pre {
  margin-top: 0px;
  border-top-left-radius: 0px;
}

以上で実装は完了です。動作に問題がないか確認を行ってください。

おわりに

新リッチエディタでシンタックスハイライトとファイル名表示をする方法について説明しました。

旧リッチエディタではカスタムフィールドを組み合わせることで、この記事のようなプログラミング言語を指定したシンタックスハイライトや、ファイル名の表示に対応することはできましが、その分microCMS管理画面がごちゃごちゃしたり、実装が複雑化したりすることがありました。新リッチエディタではリッチエディタだけで対応できるので、そのあたりがすっきりしてとても使いやすくなった印象を受けました。

この記事がサイトを作成する際の参考となれば幸いです。

参考

この記事は以下の情報を参考にしました。

関連タグの記事

Ryota Kondo
Ryota Kondo

システムエンジニア・プログラマー|このブログサイトの運営もしており、思いついたことをまとめて記事を書いています💡