Open12

独自ブログへ移行のための静的サイトシステム選定~制作時の記録(整理用)

KasamiKasami

静的サイトジェネレータを探す

欲しい機能メモ:

  • Markdownで書ける
  • SEOに強い
  • サイト表示が高速
  • テーマ設定(用意されてあるやつから選ぶ程度でOK)
  • Googleアナリティクスとか埋め込みできる
  • 記事作成~デプロイまでがスムーズだと嬉しい
KasamiKasami

どうせならTypeScript、React.jsを使いたい
https://lume.land/docs/configuration/using-typescript/

TypeScriptを使う

Lume is built on top of Deno so it has native support for TypeScript and comes with built-in types for core features and plugins. It also creates the deno.json file importing the Lume types using the compilerOptions.types array.

  • LumeはDenoの上に構築されているため、TypeScriptをネイティブにサポートしている
  • コア機能とプラグイン用の組み込み型が付属している
  • compilerOptions.types配列を使用して、Lumeの型をインポートしたdeno.jsonファイルを作成する

だそうだ。つまり初めから使える。

TSXを使う

TSX ページを作成するにはLume JSX ( React ) またはJSX Preact ( Preact ) プラグインを使用する。
https://lume.land/docs/configuration/using-typescript/#tsx-pages

JSXプラグインのインストール方法:
https://lume.land/plugins/jsx/

上のページに書いてある通りだが、設定を追記する。

_config.ts
import lume from "lume/mod.ts";
+ import jsx from "lume/plugins/jsx.ts";

const site = lume();

+ site.use(jsx(/* Options */));

export default site;
deno.json
{
  // ...
  "compilerOptions": {
  // ...
+    "jsx": "react-jsx",
+    "jsxImportSource": "npm:react",
+    "jsxImportSourceTypes": "npm:@types/react"
  }
}

適当にtsxで書いたページ_includeディレクトリへ追加。headerとfooterはコンポーネント化して表示できた。
(こちらを参考にさせていただきました)

leyout.tsx
import Header from './header.tsx';
import Footer from './footer.tsx';

export default ({ title, author, children }: Lume.Data, helpers: Lume.Helpers) => (
  <html>
    <Header title={title} />
    <body>
      {children}
    </body>
    
    <Footer author={author} />
    
  </html>
);
header.tsx
const Header = ({ title }) => {
  return (
    <head>
      <title>{title}</title>
    </head>
  )
}

export default Header
footer.tsx
const Footer = ({ author }) => {
  return (
    <footer>
      <p>&copy; {new Date().getFullYear()} {author}</p>
    </footer>
  )
}

export default Footer

埋め込む値はmd形式ファイルで持たせられるようだ。記事コンテンツはこの形式で管理すると楽かも。

index.md
---
layout: layout.tsx
title: This is my website top
siteName: "My Tech Blog"
author: "Your Name"
authorBio: "A passionate web developer and tech enthusiast."
categories: ["JavaScript", "Deno", "Web Development"]
github: "your-github-username"
twitter: "your-twitter-handle"
---
# Welcome to My Blog

🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

  BENVIDO - WELCOME! 🎉🎉🎉

  Lume has configured successfully!

🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

Here are some of the topics I'll be covering in future posts:

- JavaScript and modern web development
- Deno and its ecosystem
- Best practices for writing clean and maintainable code
KasamiKasami

Lumeプラグイン Navを利用して TSX形式でサイドメニューを作る

なかなか参考記事が見つからなくて意外と詰まりやすかった...

  • プラグインのインストールはnav.tsを追加してsite.useに加えるだけでいい
_config.ts
import lume from "lume/mod.ts";
+ import nav from "lume/plugins/nav.ts";

const site = lume();

+ site.use(nav(/* Options */));

export default site;
  • nav.menu() でツリー構造で表現されたメニューのオブジェクトを取得する
  • ({ nav }: Lume.Data) => {}のように使いたいコンポーネント関数の引数として受け取ることができる
  • 再帰的にリスト表示させる:
sideMenu.tsx
import MenuItem from "./menuItem.tsx"

const SideMenu = ({ nav }) => {
  return (
    <nav>
      <ul class="menu">
        {nav.menu().children.map((item, index) => (
          <li>
            <MenuItem item={item} />
          </li>
        ))}
      </ul>
    </nav>
  )
}

export default SideMenu
menuItem.tsx
const MenuItem = ({ item }) => {
  let itemHeader;

  if (item.data.url) {
    itemHeader = (
      <a href={ item.data.url }>
        {item.data.title}
      </a>
    )
  } else {
    itemHeader = (<span>{item.data.basename}</span>)
  }

  let items;
  if (item.children && item.children.length > 0) {
    items = (
      <ul>
        {item.children.map((child) => (
          <li>
            <MenuItem item={child} />
          </li>
        ))}
      </ul>
    )
  }

  return (
    <>
      {itemHeader}
      {items}
    </>
  )
}

export default MenuItem

https://lume.land/plugins/nav/

KasamiKasami

コメントの仕組みを導入するには?

https://www.freecodecamp.org/news/how-to-create-a-static-blog-with-lume/#heading-how-to-enable-comments:~:text=can learn more.-,How to Enable Comments,utterance comments in lume and then read the GitHub discussion.,-How to Use
で紹介されていた。
utterancesというGitHub Appをインストールして、scriptタグを埋め込むと自分のブログやWikiにGitHub issue風のコメント欄を追加できる。
GitHub issue search APIが使われている。

導入方法

KasamiKasami

検索機能を有効にする方法

  • Pagefindというプラグインが用意されている
  • UIも標準で割といい感じに動いてくれる
  • 参考記事

インストール

_config.tsに以下を追記するだけ。

_config.ts
import lume from "lume/mod.ts";
+ import pagefind from "lume/plugins/pagefind.ts";

const site = lume();

+ site.use(pagefind(/* Options */));

export default site;

UIコンポーネント例

最小構成はid属性に"search"をつけるだけ。めちゃくちゃ簡単だった。
つまり↓になる。

const Pagefind = () => <div id="search"></div>

export default Pagefind

オプションの設定がある

一旦デフォルトで実装しておいて必要になったらカスタマイズする。
https://lume.land/plugins/pagefind/#ui-configuration

多分CSSでデザインもいじれる。これもまた後で。
https://lume.land/plugins/pagefind/#customising-the-styles

KasamiKasami

カテゴリ一覧、タグ一覧 ページを作る

カテゴリ一覧

  • 記事にcategoryキーの値を用意しておくことでsearch.values("category")から値を配列で取得できる
  • 取得した配列はfor ofmap()で展開してカテゴリの一覧として表示することができた
  • 実践例
export default ({ title, author, search, nav}: Lume.Data, helpers: Lume.Helpers) => {
  return (
    <html lang="ja">
      <body>
        <Main >
          <ul>{search.values("category")
            .map((category, index) => (
              <li data-key={index}>
                <a href={`/category/${category}`} data-category={category}>{category}</a>
              </li>
            )
          )}</ul>
        </Main>
      </body>
    </html>
  );
};

タグ一覧

  • 記事にtagsキーの値を用意しておくことでcategoryのようにsearch.values("tags")から値を配列で取得できる
  • ↓のように1つの記事に複数設定する場合はtagsの方が適切かも
tags:
  - "docker"
  - "error"
  - "dockercompose"
  - "post"
export default ({ title, author, search, nav}: Lume.Data, helpers: Lume.Helpers) => {
  return (
    <html lang="ja">
      <body>
        <Main >
          <ul>{search.values("tags")
            .map((tag, index) => (
              <li data-key={index}>
                <a href={`/tag/${tag}`}>{tag}</a>
              </li>
            )
          )}</ul>
        </Main>
      </body>
    </html>
  );
};