Markdown
Twitter
クソアプリ
アドベントカレンダー2018

はじめに

クソアプリ Advent Calendar 2018 の3日目です。

概要

Twitterでツイートしようと思った時にふと「Markdownで書きたい」と思ったことはないでしょうか? ここに訪れている方であれば誰もがあるはずです。僕も時々そう思っていたので今回TwitterでMarkdownを使えるようにしてみました。

とりあえず完成したもの

例えば下記のようなツイートを行うと…

Twitterでmarkdownを入力している画面

こんな感じで表示されます。

ツイートのmarkdownがきちんと変換されている画像

リンクや画像も、コードまでも思いのままです。さあ、皆さん思う存分Twitter上でMarkdownライフをエンジョイしてください!

…と、すみません、技術解説や使い方の説明が抜けていました。

技術解説

何はともあれツイート文に書かれているMarkdownを変換する必要がありますので、変換用ライブラリと、コードハイライトのライブラリを導入します。あとCSSも一緒に入れておきます。

yarn add marked highlight.js github-markdown-css

markedを初期化します。コード部分はhighlight.jsを使うように設定します。

const marked = require("marked");
import * as hljs from "highlight.js";
require("highlight.js/styles/atom-one-dark.css");
const renderer = new marked.Renderer();
renderer.code = (code, language) => {
  return (
    "<pre" +
    '><code class="hljs">' +
    hljs.highlightAuto(code).value +
    "</code></pre>"
  );
};
marked.setOptions({
  renderer: renderer
});

各ツイートはjs-tweet-text-containerというクラスの要素に含まれているため、これでループしていきます。

  document.querySelectorAll(".js-tweet-text-container").forEach(element => {
    let body = element.innerHTML;
    if (body.match(/<!--marked-->/)) {
      return;
    }
    const pStartRegex = /<p[^>]+>/;
    const pStart = body.match(pStartRegex);
    body = body.replace(pStartRegex, "");
    body = body.replace(/<\/p>/, "");

    body = stripUrlTags(body);
    body = stripDollarHashLinks(body);

    body = body.trim();
    const markdown = marked(body).replace(/<p> +/, pStart[0]);
    const divStart = pStart[0]
      .replace(/<p/, "<div")
      .replace(/class="/, 'class="markdown-body ');
    element.innerHTML = `<!--marked-->${divStart}${markdown}</div>`;
  }

各要素を監視して実行してるため、同じ要素で何度も実行してしまわないように<!--marked-->というコメントを入れてそれがあれば無視するようにしています。

あとpとかdivとかをあれこれしていますが、ツイート本文はpタグで囲まれているため、Markdownを変換後は色々なタグがそのpタグの子要素になってしまい正常ではなくなるため、divに変更してあります。ついでにCSSが反映されるようにmarkdown-bodyクラスを追加しています。

URLの自動リンクを削除

ツイートにURLを書くと自動的にリンクされてしまいます。そのためMarkdownのリンク最初からリンクされている状態になってしまうため正常に変換できません。そのため予め自動的に付与されたリンクを削除しておきます。

function stripUrlTags(body) {
  const regex = /\[([^\]]*)\]\((<a.+?twitter-timeline-link.+?>.+?<\/a>)[]*\)/;
  let match = body.match(regex);
  while (match) {
    const url = match[2]
      .replace(/<[^>]+>/g, "")
      .replace(/[]/g, "")
      .replace(/&nbsp;/g, "")
      .trim();
    console.log(url);
    const replaced = `[${match[1]}](${url})`;
    body = body.replace(match[0], replaced);
    match = body.match(regex);
  }
  return body;
}

ハッシュタグの自動リンクを削除

ハッシュタグと、あと$で始まるキャッシュタグも自動リンクされてしまいます。PHPの変数を含めたコードを書きたい場合などに支障が出るためこちらも予め削除しておきます。

function stripDollarHashLinks(body) {
  const lines = body.split(/\n/);
  const regex = /<a.+?twitter-(c|h)ashtag.+?>.+?<\/a>/g;
  let isCode = false;
  lines.forEach((line, i) => {
    if (line.substr(0, 3) == "```") {
      isCode = !isCode;
      return;
    }
    if (!isCode) {
      return;
    }

    const matches = line.match(regex);
    if (!matches) {
      return;
    }
    matches.forEach(match => {
      const replaced = match.replace(/<[^>]+>/g, "");
      body = body.replace(match, replaced);
    });
    lines[i] = line;
  });
  return body;
}

使い方

さて、次に使い方ですが、下記のChrome拡張をインストールするだけです。これでMarkdownライフがエンジョイできます。

Twitter Markdown

注意点

Chrome拡張なので当然インストールしているブラウザでしか変換されません。他の人が見るとこんな感じになっています…。

ツイートのmarkdownが変換されずそのまま表示されている様子

ということで全員がこのChrome拡張のインストールをお願いします!!! (あとMarkdown変換に対応したスマホ用のTwitterクライアントの開発もどなたかお願いします)

関連リンク