boring stuff

GridsomeにGatsbyのRemark Pluginを移植

GridsomeJavascriptlinkCard

emmdee@colus.img

このサイトはVueの静的サイトジェネレーターGridsomeを使って制作している。Gridsomeには既にたくさんの有用なプラグインがあるが、Gridsome自体がまだ新しいこともあって、必要なものが何でも揃っているわけではない。今回、はてなブログで見られるようなLink Cardを簡単に作れるプラグインを探してみたが、見つからなかった。
LinkCardについて検索している中で、Reactの静的サイトジェネレーターであるGatsby用にLinkCardのプラグインが公開されているのを見つけた。

早速ソースを眺めてみたが、基本的にRemarkのプラグインなので、そのままGridsomeでも使えそうにも思えたので、試しにフォークして使ってみた。 さすがにそのままでは動作しなかったが、入出力のほんの一部分のみGridsome用に書き換えたところ、とりあえず動作するようになった。

現状でGatsby用の元のプラグインに比べ、下記の2点が劣化している。

  1. CSSの設定を行っていないため、自力で設定する必要がある
  2. Gridsomeでのキャッシュの使い方がわからなかったので、キャッシュを使えておらず、更新の際に同じサイトへのリンクでもリンクがある度にサイトに情報を取得しに行ってしまい非効率である

上記2点については気が向いたら修正するかもしれないが、個人的には現状で満足しているため、先延ばしになりそうな……。
注意点として、puppeteerを使ってサイトの情報を取得する仕様のため、プラグインをインストールすると、同時にchromiumがインストールされる。chromiumは200MB程度の容量があるので一般的なパッケージに比べインストールにある程度時間がかかる。

Remarkのプラグインについては同じようにGatsbyから簡単に移植できるものも多いと思われるので、参考までに今回の主な変更点を下記に書いておく。

gatsby-remark-link-card/index.js
module.exports = async ({ cache, markdownAST }, pluginOption) => {
  const options = { ...defaultOption, ...pluginOption }
  const { delimiter } = options
  const browser = await puppeteer.launch()
  const targets = []

  visit(markdownAST, 'paragraph', paragraphNode => {
gridsome-plugin-remark-linkcard/index.js
module.exports = pluginOption => {
  const options = { ...defaultOption, ...pluginOption }
  const { delimiter } = options
  return async tree => {
    const browser = await puppeteer.launch()
    const targets = []
    visit(tree, 'paragraph', paragraphNode => {

GatsbyとGridsomeでのMarkdownkのASTの扱い方のお作法的な部分を書き換えているだけである。
上記以外の変更点は、

  1. キャッュ関連を数行コメントアウトした
  2. 最期のreturn markdownASTをコメントアウトした。Gridsomeでは修正後のtreeをreturnする必要がないようだ。
  3. githubなどいくつかのページへのリンクで動作しなかったものを修正した。$("link[rel='shortcut icon']")に該当するタグがなかったことが原因だった。

Gatsbyのプラグインのソースを眺めていて面白いと思ったのは、多分visitの処理の中では非同期処理ができないからだと思われるが、visitの処理の最後に配列targetsにasyncの関数をpushしておき、visitの外でawait Promise.all(targets.map(t => t()))としてpushした関数を実行している点だ。
初見時はvisit内の処理が関数を実行した結果をpushしているものだと勘違いしていたのもあって、visit外のtargets.map(t => t())が何をしているのか謎だったのだが、しばらく考えて、なるほど!と納得、こんな書き方ができるのかと勉強になった。

gatsby-remark-link-card/index.js
  visit(markdownAST, 'paragraph', paragraphNode => {
    
    ...途中省略...
    
    targets.push(async () => {      let html = await cache.get(urlString)

      if (!html) {
        const data = await getPageData(browser, url)
        html = getHTML(data)
        await cache.set(urlString, html)
      }

      node.type = `html`
      node.value = html
      node.children = undefined
    })
  })

  try {
    await Promise.all(targets.map(t => t()))  } catch (e) {
  } finally {
    await browser.close()

    return markdownAST
  }

まとめ

GatsbyはGridsomeに比べるとかなりプラグインが充実しているので、Gridsomeに必要なプラグインが見つからない場合、Gatsbyのものを移植することを考えてみるのもアリだと思った。他人のコードを読むのは勉強にもなるし。