飽き性の頭の中

VS Codeのスニペット機能を使って内部リンクを簡単に生成する

VS Codeのスニペット機能を使って内部リンクを簡単に生成する

tawachan
tawachan

本ブログは、Gatsby.js を使っているが、基本的に記事はローカルで Markdown で書いている。

そのため、ブログ内の他の記事へのリンクを貼る際に、Markdown のリンク記法を使う必要があるが、リンク先の記事のタイトルを毎回調べて、リンクを貼るのは面倒という問題があった。

そこで、VS Code のスニペットを使って、簡単にリンクを貼れるようにしたので、その方法をメモする。

前提

ローカルに記事情報を Markdown として管理している場合をここでは想定している。Contentful などの CMS を使っている場合は、また勝手が変わってくるともうので注意されたい。

VS Code のスニペット

VS Code のスニペットは、次のように設定する。

   {
  "Internal Link": {
    "prefix": "link",
    "body": "[${1:link text}](${2:https://example.com})",
    "description": "Internal Link"
  }
}

詳しくは次の記事を参照。

要するに、上記のフォーマットで適切なスニペットを生成できれば、VS Code のスニペットを使って、簡単にリンクを貼ることができる。

スニペットの生成

各ブログの Markdown ファイルにフロントマターを参照して、スニペットを生成する。

スニペットをするために次のようなスクリプトを書いた。

   const fs = require("fs");
const fm = require("front-matter");

/**
 * 指定フォルダ内のファイルを再帰的にすべて取得する
 */
const listFiles = (dir) => fs.readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => (dirent.isFile() ? [`${dir}/${dirent.name}`] : listFiles(`${dir}/${dirent.name}`)));

// ブログ記事が格納されているフォルダを指定し、すべてのファイルのパスを取得する
const blogPosts = [...listFiles("content/blog"), ...listFiles("content/hatena")];

// 指定したMarkdonwのフロントマターを取得する
const blogFrontMatter = blogPosts
  .map((path) => {
    const file = fs.readFileSync(path, "utf8");
    const { attributes } = fm(file);
    return attributes;
  })
  .filter((a) => a.title);

// フロントマターからスニペットを生成する
const postsSnippets = blogFrontMatter.reduce((prev, cur) => {
  const { title, description, slug, date } = cur;
  const d = new Date(date);
  const formattedDate = `${d.getFullYear()}${(d.getMonth() + 1).toString().padStart(2, "0")}${d.getDate().toString().padStart(2, "0")}`;
  prev[title] = {
    scope: "markdown",
    prefix: `@blog:${formattedDate}/${slug}`,
    body: [`[${title} | 飽き性の頭の中](/entry/${slug})`],
    description: description ?? "",
  };
  return prev;
}, {});

// タグとカテゴリのスニペットを生成する(※ optional)
const tagsSnippets = Array.from(new Set(blogFrontMatter.map((fm) => fm.tags).flat()))
  .filter((t) => t)
  .reduce((prev, cur) => {
    prev[cur] = {
      scope: "markdown",
      prefix: `@tag:${cur}`,
      body: [cur],
      description: cur ?? "",
    };
    return prev;
  }, {});
const categoriesSnippets = Array.from(new Set(blogFrontMatter.map((fm) => fm.category)))
  .filter((c) => c)
  .reduce((prev, cur) => {
    prev[cur] = {
      scope: "markdown",
      prefix: `@category:${cur}`,
      body: [cur],
      description: cur ?? "",
    };
    return prev;
  }, {});

fs.writeFileSync(".vscode/blog-posts.code-snippets", JSON.stringify({ ...postsSnippets, ...tagsSnippets, ...categoriesSnippets }));

これを実行すると次のようなファイルが生成できる(見やすくするためにフォーマットしている)。

私の場合は、@を最初につけることで、必要なときだけスニペットが呼び出されるようにしている。

この当たりは使い勝手に合わせて、カスタマイズしてもらえればと思う。

タグやカテゴリーを本ブログでは使っているので、それも合わせてスニペットにしている。表記ゆれやすでに何のタグやカテゴリーが使われているのかわからなくなるので、スニペットがあると適切なタグやカテゴリーが分かるので意外と助かる。

まとめ

あとは、必要なときにこのスクリプトを走らせれば、すぐにスニペットを最新に更新していける。

こうすることで、ブログ記事のリンクを貼るときに、手間を省けるようになった。