2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactをまともに使ったことないエンジニアがBun+HonoでSSGを試してみた

Last updated at Posted at 2024-02-20

Bun+Honoは今日初めて触ったので備忘録を残します。
Reactはほとんど触ってないのでちょっとしたことに躓いて牛歩状態です。
この記事はチュートリアルなコードと、参照したドキュメントへのリンクを含めて後で辿れるように書きました。

試した環境

  • MacOS Sonoma 14.3
  • Bun 1.0.6
  • Hono 4.0.4

Bun インストール

コマンドラインからインストールしました。
sudoは使わないのでたぶん怖くない筈です。

$ curl -fsSL https://bun.sh/install | bash

削除したい時はホームフォルダの.bunを削除すれば良いそうです。

$ rm -rf ~/.bun

Project作成

ドキュメントに習ってHonoのプロジェクトを作成

$ bunx create-hono my-app

何も知らずにコマンドを叩くと12からあるTemplateを選べと言われてドン引きしました。
今回はbunを選択。

$ bunx create-hono my-app

create-hono version 0.4.0
✔ Using target directory … my-app
✔ Which template do you want to use? › bun
cloned honojs/starter#main to /Users/o2/work/tmp/my-app
✔ Copied project files

VSCodeでプロジェクトを開くとあちこちアンダーラインが引かれます。
最初にライブラリ導入しないとね。
無事Hello Hono!と表示できました。👏

$ code my-app
$ cd my-app
$ bun i
$ bun run dev

$ bun run --hot src/index.ts
Started server http://localhost:3000

スクリーンショット 2024-02-21 0.48.59.png

SSGを試す

ドキュメントにあるようにSSGを設定。
import app from "./src/index";と設定。
(なお、この書き方だとindex.*, index/* と様々なファイルを参照するそうです。)

build.ts
import { toSSG } from "hono/bun";
import app from "./src/index";

toSSG(app)

実行するとstaticフォルダにファイルが出力できました。

$ bun build.ts
$ tree --gitignore 
.
├── README.md
├── build.ts
├── bun.lockb
├── package.json
├── src
│   └── index.ts
├── static
│   └── index.txt
└── tsconfig.json

3 directories, 7 files

ssgParamsで複数のページを出力する

同ページのドキュメントのssgParamsを試していきます。
ここでは静的ページの作成にはコードに直書きのコンテンツデータを利用します。

最初にjsxを利用しますので現在のindex.tsのファイルの拡張子を変更します。

$ mv index.ts index.tsx

index.tsx
import { Hono } from 'hono'
import { ssgParams } from 'hono/ssg'

const app = new Hono()

const ShopsData = [
  { id: "1", name: "hoge",}, 
  { id: "2", name: "fuga" }
]

function getShop(id: string){
  return ShopsData.filter(shop => id == shop.id)
}

app.get(
  '/shops/:id',
  ssgParams(async () => {
    return ShopsData.map((shop) => ({ id: shop.id }))
  }),
  async (c) => {
    const shops = await getShop(c.req.param('id'))
    if (shops.length < 1) {
      return c.notFound()
    }
    const shop = shops[0]
    return c.render(
      <div>
        <h1>{shop.name}</h1>
      </div>
    )
  }
)

export default app

package.json.ts.tsxに修正して実行

package.json
{
  "scripts": {
    "dev": "bun run --hot src/index.ts"
    
    "dev": "bun run --hot src/index.tsx"
  },
...
$ bun run dev

localhost:3000/shops/2にアクセス
shops/[id]にIDを指定してページにアクセスできることが確認できます。

スクリーンショット 2024-02-21 1.04.41.png

再度SSGを出力しなおします。

$ bun build.ts 
$ tree  --gitignore
.
├── README.md
├── build.ts
├── bun.lockb
├── package.json
├── src
│   └── index.tsx
├── static
│   ├── index.txt
│   └── shops
│       ├── 1.html
│       └── 2.html
└── tsconfig.json

staticフォルダに設定した[id].htmlのファイルが出力できていることが確認できました。
上記の出力まで見れば何を行う処理なのか解りやすいですね。


const ShopsData = [
  { id: "1", name: "hoge",}, 
  { id: "2", name: "fuga" }
]

app.get(
  '/shops/:id',
  ssgParams(async () => {
    return ShopsData.map((shop) => ({ id: shop.id }))
  }),

ここでの肝ですが、

  • /shops/:idではapp.getにパラメータidを取得するように指定
  • ssgParamsに生成するページのIDをShopsDataを展開して与えています

これによりstaticフォルダに出力するファイルを制御できます。

静的ファイルへのリンクに対応する

index.tsx
import { Hono } from 'hono'
import { ssgParams } from 'hono/ssg'

const app = new Hono()

const ShopsData = [
  { id: "1", name: "hoge",}, 
  { id: "2", name: "fuga" }
]

function getShop(id: string){
  return ShopsData.filter(shop => id == shop.id)
}

app.get(
  '/shops/:id',
  ssgParams(async () => {
    return ShopsData.map((ShopsData) => ({ id: ShopsData.id }))
  }),
  async (c) => {
    const shops = getShop(c.req.param('id'))
    if (shops.length < 1) {
      return c.notFound()
    }
    const shop = shops[0]

    const contents = (
      <ul>
        { ShopsData.map( shop => (<li><a href={
          Bun.env.NODE_ENV === "development" ? `/shops/${shop.id}` : `/shops/${shop.id}.html`
        }>{shop.name}</a></li> )) }
      </ul>
    )
    
    return c.html(`
      <div>
        <h1>${shop.name}</h1>
      </div>
      ${contents}
    `)
  }
)

export default app

package.jsonbuildコマンドを追記

package.json
{
  "scripts": {
    "dev": "bun run --hot src/index.tsx",
    "build": "NODE_ENV='production' bun build.ts"
  },
...

ここではコマンドをbuild.tsではなくpackage.jsonに追記したbuildコマンドを実行します。
この修正により出力するファイルとbun run devで実行時と静的ページを閲覧してもリンクが機能します。

$ bun run build
$ bun run dev

スクリーンショット 2024-02-21 1.17.46.png

これでSSGマスター

まだまだ機能は沢山ありますが基本のループとIF文が書けるようになりました。
Bun+Honoを試した感想としては、随分簡単に静的ファイルの出力処理が書けるのだなと思いました。
これでとっかかりが出来たのであとはドキュメントを読み進めるだけです。

初学者ですのでもっと良い方法等あれば是非コメントを残していただければ幸いです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?