Stoic Sounds 元はExtroseが運営する個人サイト名

プロフィール

顔写真(V)

Extrose
2002年頃から電脳海にいる。 制作ペースは激減したものの今でも現役の作曲者(自称)であり、機会があればBMSも作る。 が、最近はVを被ってゲーム実況に勤しんでいる。 興味があるものになんでも手を出すのでかなりの趣味を抱えている。

柊 雷夜
ユーチューブ地方で見かけるVのすがた (↑)。 たまに VRChat にも出る。 VRoid Studio 製。

リリース

個人活動

読み物

[JavaScript/Node.js] marked のコードブロックを Prism でプリレンダする

戻る > エンジニア - Web開発
関連:[JavaScript] Markdown形式をそのまま Webページとして表示する
関連:[JavaScript] marked.js のコードブロックに色付けする
関連:[JavaScript/Node.js] marked のコードブロックを Prism でプリレンダする

概要

markedとPrism をサイトで使用すると、画面のちらつきとか、負荷とかが気になってくる
後パーミッションとか設定が面倒

ならいっそ HTML化(プリレンダリング)するかということで思い立つも、ChatGPTを頼っても時間がかかって野で備忘

実装

結構大規模な記述になる

第1段階 - Node.jsで marked を使う

基本はサイトでの利用と同じ

npm install marked

npm install で marked をインストール

// marked のインポート (ESModule)
import { marked } from 'marked';

// 適当なマークダウン
const markdownText = "# h1content\n\nblahblahblah\n";

// パース
const markdownHtml = marked.parse(markdownText);

パースを実装するとこう

第2段階 - Prism を適用する

npm install prismjs

npm install で追加インストール
先の記述に加え、下記を適当に追加する

import Prism from 'prismjs';
import loadLanguages from 'prismjs/components/index.js';

// Prism 対象言語読み込み : ビルダーのため全種対応
loadLanguages();

// marked の設定をカスタマイズ
marked.use({

  extensions: [
    {
      name: 'code',
      level: 'block',
      start(src) {
        const match = src.match(/^```/gm);
        return match ? match.index : undefined;
      },
      tokenizer(src) {
        const rule = /^```(\w+)?\n([\s\S]*?)```/;
        const match = rule.exec(src);
        if (match) {
          return {
            type: 'code',
            raw: match[0],
            lang: match[1],
            text: match[2]
          };
        }
        return;
      },
      renderer(token) {
        const lang = token.lang || '';
        const code = token.text;

        if (Prism.languages[lang]) {
          const html = Prism.highlight(code, Prism.languages[lang], lang);
          return `<pre class="language-${lang}"><code class="language-${lang}">${html}</code></pre>`;
        }
        return `<pre><code>${code}</code></pre>`;
      }
    }
  ]
});

Prismを適用するため、コードブロックを再定義する必要があるそうで、結構大変なことになった
start、tokenizer がmarkdownのパースの条件
renderer がPrismの適用

renderer の token には、lang と text が渡される
これはtokenizerの設定に従う

例えば、以下のmarkdown があったとする

```javascript
console.log('output');
```

この場合、lang は「javascript」、text は「 console.log('output'); 」になる
後は if文で対応した言語か判定し、対応していればhightlight化する

対応できない方法

「setOptions」にて設定する方法、「marked をインスタンス化して、初期化パラメータにてレンダラーを設定する方法」も見つかる(ChatGPTから提案される)が
最新の marked では非対応である

marked.setOptions({
  highlight: (code, lang) => {
    if (Prism.languages[lang]) {
      return Prism.highlight(code, Prism.languages[lang], lang);
    }
    return code;
  }
});

setOptions の例

  const renderer = {
    code(code, infostring) {
      const lang = infostring || '';
      if (Prism.languages[lang]) {
        const html = Prism.highlight(code, Prism.languages[lang], lang);
        return `<pre class="language-${lang}"><code class="language-${lang}">${html}</code></pre>`;
      }
      return `<pre><code>${code}</code></pre>`;
    }
  };

  markedInstance.use({ renderer });
  

use にレンダラーを渡す例
infostring は undefined となり、言語を渡してこない