💪

Deno向けに静的サイトジェネレーターを作ってみた!

2023/10/06に公開
  • 2023/10/07 更新

はじめに

ブンブン🐝ハロー・・・という冗談はさておき、Deno用に静的サイトジェネレーターというものを作ってみた。

機能の概要と工夫した所とかを書いていくよ。

(Denoの静的サイトジェネレーターってlume以外にウワサを聴いたことがない気がする)

https://ikasoba.github.io/dejamu/

名前はなんなんだい

Dejamuって名前をつけてみました。
名前の由来は筆者がよく見る予知夢から(´・ω・`)

そのまま使うよりちょっといじったほうがユニークになるよね。

以下はDejamuのリポジトリへのリンクです。
この記事を書いてる時点で、バージョン 0.0.14 ですね。

最近まで雑に書いてたのでコミット履歴とかが悲惨なことになってるよ。

https://github.com/ikasoba/dejamu/

機能

Preactを使って簡単に静的なサイトを作れるよ。

  • Markdown
  • PostCSS

に対応してるので、すこし遊べるかも。

Markdownはmarkedjsを使っていて、特にこれといった機能はないです。(´・ω・`)

lumeとの違い

lumeはいろんな書き方(nunjucksとかjsx)に対応してるけど、Dejamuはpreact一筋だよ。

初期化スクリプト

これから Dejamu を始めるぜ!って人向けに現在のディレクトリにプロジェクトを作ってくれるやつを用意しているよ。

deno run -rA https://raw.githubusercontent.com/ikasoba/dejamu/main/scripts/init.ts

ってやると、最新のバージョンで遊べるよ。

deno task serveとかでサーバーを立ち上げたり、deno task buildでビルドしたりできるよ。

Deno.landにはまだ怖くてうpしてないよ。

設定ファイル

使う人には dejamu.config.ts というファイルに設定を書いてもらいます。
初期化スクリプトを実行した場合はこんな風になってるよ。

dejamu.config.ts
import type { Config } from "dejamu/mod.ts";
import PreactPlugin from "dejamu/plugins/preact/mod.ts";
import MarkdownPlugin from "dejamu/plugins/md/mod.ts";

export default {
  entryPoints: ["pages/*.{jsx,tsx,md}"],
  plugins: [
    PreactPlugin(),
    // このlayoutsは後で解説するよ
    MarkdownPlugin("layouts/"),
  ],
} satisfies Config;

pages直下にjsxとか作れば読んでくれるらしい。

PostCSSを使うならこう書き足してね。

dejamu.config.ts
import type { Config } from "dejamu/mod.ts";
import PreactPlugin from "dejamu/plugins/preact/mod.ts";
import MarkdownPlugin from "dejamu/plugins/md/mod.ts";
import PostCssPlugin from "dejamu/plugins/postcss/mod.ts";

export default {
  entryPoints: [
    "pages/*.{jsx,tsx,md}",
    // styles直下に置くことが多いので適当に
    "styles/*.css"
  ],
  plugins: [
    PreactPlugin(),
    MarkdownPlugin("layouts/"),
    // ".css" という拡張子のファイルをpostcssで処理させるよ。
    // 配列なので他の拡張子(scssとか)も足せるよ。
    // 第2引数にpostcss用のプラグインを配列で渡せたりするよ。
    PostCssPlugin([".css"])
  ],
} satisfies Config;

Markdownを使う時のポイント

pages配下にmarkdownを作ったら動くよ。

layoutという機能でデザインを使いまわしたりできるよ。
以下はlayoutの例だよ。

layouts/HogePage.tsx
import { Head } from "dejamu/mod.ts";
import { Markdown } from "dejamu/plugins/md/Markdown.tsx";
import type { LayoutComponent } from "dejamu/plugins/md/MarkdownPlugin.tsx";

export default (function HogePage({ children }) {
  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <link
          rel="stylesheet"
          href={
	    // この projectRoot は特殊なグローバル変数で、相対パスでプロジェクトのルート(dejamu.config.tsがあるところ)を示すようになってるよ。
	    // ビルドするときに動的に変わるので安心して使ってね。
	    `${projectRoot}/styles/index.css`}
        />
        <title>ほげほげサイト</title>
      </Head>
      <Markdown>
        {children}
      </Markdown>
    </>
  );
}) satisfies LayoutComponent;

こっちは、layoutを使うmarkdownの例

pages/index.md
---
# MarkdownPluginの引数で渡したディレクトリが基準だよ。
# デフォルトでは layouts/
# 豆知識: ここを消すと、内部で用意してある空っぽのlayoutにフォールバックされるよ
layout: HogePage.tsx
---

ここに内容

工夫したところ

はい、工夫しました。

試作した時、そこそこページの読み込みが遅かったのでパフォーマンスを良くしました。

アイランドアーキテクチャという、なんかすごい横文字のアレをdenoのfreshくんは採用しています。
これは、必要なJSだけクライアントに読ませることで高速化するぜ!みたいなやつっぽいですね。

自作のソフトが遅いのは嫌なので、それを採用してみました。

ファイル名を *.islands.tsx みたいな形式にするとHooksとかが使えるようになります。
JSだったら *.islands.jsx です。

下に書いてあるような雰囲気で書けるよ。

components/counter.islands.tsx
import { useState } from "npm:preact/hooks";

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount((count) => count + 1)}>
      {count == 0 ? "click me!" : `count: ${count}`}
    </button>
  );
}

得た知見

⚠ これはふんわりした頭で得た知見なので間違ってる可能性が大きいです。

freshは、 preactOption Hooksで公開されてないフックを使ってislandsなコンポーネントをいい感じにビルドできるようにしてるみたいだったよ。
ここらへんとか

__rとか__bとか・・・よくわからん・・・。

これは他人のコードを見ない限りなんなのかさっぱりだから僕も勘で書いてるよ。

感想

たのしかった。

勢いで書いたので雑になってるかも、ごめんね。

Discussion