markdown-itでリンクを開く際にtarget="_blank" rel="noopener"を自動付与する

js製Markdown parserのmarkdown-itを利用する際にaタグをカスタマイズしたかったので対応方法をメモしておく。

公式ドキュメントを参考にrenderer.rulesを書き換える。

import MarkdownIt from "markdown-it";

class Markdown {
  constructor() {
    this.md = new MarkdownIt();

    // https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
    const defaultRender =
      this.md.renderer.rules.link_open ||
      function(tokens, idx, options, env, self) {
        return self.renderToken(tokens, idx, options);
      };
    
    this.md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
      const targetIndex = tokens[idx].attrIndex("target");
      if (targetIndex < 0) {
        tokens[idx].attrPush(["target", "_blank"]);
      } else {
        tokens[idx].attrs[targetIndex][1] = "_blank";
      }

      const relIndex = tokens[idx].attrIndex("rel");
      if (relIndex < 0) {
        tokens[idx].attrPush(["rel", "noopener"]);
      } else {
        tokens[idx].attrs[relIndex][1] = "noopener";
      }

      return defaultRender(tokens, idx, options, env, self);
    };
  }

  render(text) {
    return this.md.render(text);
  }
}

const md = new Markdown();
export default md

renderer.rules.link_openのルールを書き換え、リンクを開く際はtarget="_blank" rel="noopener"を自動付与するようにした。

rel="noopener"をつけないとリンク先のページで親のwindowの操作ができてしまうためセキュリティ上の脆弱性がある。

一般的に、外部リンクを新しいウィンドウまたはタブで開く場合は、必ず rel="noopener" を追加してください。

サイトで rel="noopener" を使用して外部アンカーを開く  |  Tools for Web Developers  |  Google Developers

Google Developersにもtarget="_blank" を使う際はrel="noopener"も必須と書かれている。