🔥

Hono CLI 爆誕

に公開

これまでHonoは数々の新しいことを提供してきました。正規表現を活かしたルーター、サーバーサイドの軽量JSX、TypeScriptの型によるRPC、Web Standardを使ったマルチランタイム対応などなど。アイデアと実装力で世界と戦って来たわけです。

本日私達が紹介するのは「Hono CLI」です。

Hono CLIは全く新しいコンセプトのコマンドラインインターフェースです。

  • create-* ではありません
  • ただの開発用(dev&build&deploy)のコマンドではありません
  • Viteのラッパーではありません

人間とAIのためのCLIです。インストールすると

hono --help

のようにhonoコマンドを使うことができます。5つのサブコマンドがあります。

  • hono docs
  • hono search
  • hono request
  • hono serve
  • hono optimize

では一つ一つを見ていきましょう。

hono docs

最初はhono docsです。このように実行します。

hono docs [path]

pathにはHonoのWebサイト(https://hono.dev)のパスを指定します。例えば「Routing」のページを指定したければこのようにします。

hono docs /docs/api/routing

これでドキュメントを見ることができるわけです。

でもあなたは思ったでしょう。「これだけですか?」と。そうこれだけです。しかしポイントは「標準出力にMarkdownが出力される」ことです。

Terminal

勘のいい方はお分かりでしょう。これはAIにとって優しい仕組みです。Webのページをフェッチした時についてくるHTMLタグがありません。標準出力なのでAIコーディングエージェントが読むことができます。

次に紹介するのはhono searchです。

hono search <query>

queryには検索語を指定します。例えば、ミドルウェアに関するドキュメントのありかを調べたければ、こうします。

hono search middleware

すると、検索語に関係のあるWebサイトのURLとパスがJSONフォーマットで標準出力に表示されます。

Terminal

ということは、hono docsと組み合わせると「AIが自律してドキュメントを検索して読む」ことができるわけです。例えば、ベーシック認証のドキュメントを調べて読むにはこのような流れになります。

  • hono search "basic auth"
  • /docs/middleware/builtin/basic-auth というパスを取得
  • hono docsの引数に渡す
  • 該当のドキュメントのMarkdownが表示される

AI向けのコマンドはこれだけではありません。次に行きましょう。

hono request

次のコマンドはhono requestです。

hono request [file]

fileのデフォルト値はsrc/index.tsなのですが、明示的に指定するにはこうします。

hono request src/index.ts

このコマンドを理解するために、まずHonoのAPIである「app.request()」をイメージしてください。app.request()を使うとサーバーを立ち上げずに、Honoアプリにリクエストを飛ばし、そのレスポンスを取得することができます。これは動作確認やテストなどに非常に便利です。

以下はパス/にGETリクエストを送り、返ってきたResponseオブジェクトがresに入るというコードです。

const res = await app.request('/')

hono requestは引数で指定したファイル内にあるHonoのアプリに対して、コマンドラインで指定したリクエストを飛ばしてその結果を印字します。

hono request src/index.ts

とすると、

const res = await app.request('/')

が実行されるわけです。例えば、Hello Worldアプリがあります。

src/index.ts
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello World'))

export default app

ではこのファイルにhono requestしてみましょう。

Terminal

-Pでリクエストするパス/を指定してます。デフォルトのメソッドはGETです。標準出力にはJSONフォーマットで「ステータス・コードは200、ボディはHello World」という結果が印字されています。より複雑なことができます。curlライクに-Xでメソッド指定、-dでボディを指定することができます。

hono request \
  -P /api/users \
  -X POST \
  -d '{"name":"Alice"}' \
  src/index.ts

このコマンドは人間にとって非常に便利です。これまでアプリの試験をする場合はWranglerやDeno、Bun、Node.jsのコマンドを使ってサーバーを立ち上げ、Webブラウザやcurlなどのクライアントで叩いてました。ところがこのhono requestを使うとサーバーを立ち上げずにアプリを試験することができるのです!

AIも使うことができます。これは非常に強力です。コーディングエージェントがサーバーを立ち上げて、curlを叩く様子をあなたも見たことがあるでしょう。サーバーのシャットダウンがうまくいかずにポートが被って、何個もサーバーを立ち上げる様子も... hono requestを使うと、サーバーすら立ち上げる必要がないので、非常に高速で、正確で、クリーンにアプリをテストすることができます。

そして、AIは先程でてきた2つのコマンドとこのコマンドを組み合わせて使うことができます。つまりこういうことです。

  • hono search => ドキュメントを探す
  • hono docs => ドキュメントを読む
  • hono request => アプリを試験する

このワークフローをAGENTS.mdCLAUD.mdに手順として書いておくことができます。これはそのまま使えます。

# My App Development Guidelines

## Hono Development

Use the `hono` CLI for efficient development. View all commands with `hono --help`.

### Core Commands

- **`hono docs [path]`** - Browse Hono documentation
- **`hono search <query>`** - Search documentation
- **`hono request [file]`** - Test app requests without starting a server

### Quick Examples

```bash
# Search for topics
hono search middleware
hono search "getting started"

# View documentation
hono docs /docs/api/context
hono docs /docs/guides/middleware

# Test your app
hono request -P /api/users src/index.ts
hono request -P /api/users -X POST -d '{"name":"Alice"}' src/index.ts
```

### Workflow

1. Search documentation: `hono search <query>`
2. Read relevant docs: `hono docs [path]`
3. Test implementation: `hono request [file]`

デモを見てください。コーディングエージェントがhonoコマンドを活用して、アプリを作成している様子が分かるでしょう。

Demo

hono serve

これまではAIのためのコマンドでした。次に人間のためのコマンドを紹介しましょう。最初のコマンドはhono serveです。

hono search index.ts

すると、index.tsのHonoアプリがhttp://localhost:7070で立ち上がります。

??それだけですか??それってNode.js Adapterを挟む必要がなくなっただけで、WranglerやDeno、Bunでも同じ事できますよね?

いえ、--useというオプションがあります。app.use()を想像してみてください。

app.use(middleware)

app.use()は渡されたミドルウェアをアプリに適応します。例えば、ビルドインのロガーミドルウェアはこのように使います。

app.use(logger())

そこからlogger()を取り出してみます。hono serveでは--useオプションの値にそれを指定して、アプリのコードを変えずともミドルウェアを適応することができます。

hono --use 'logger()' src/index.ts

このようにすると、ロガーを指定していないアプリでもログが出てきます。

Terminal

これを応用すると、コードを書き換えずにBasic認証を追加する、なんてこともできます。

hono serve \
 --use "logger()" \
 --use "basicAuth({username:'foo',password:'bar'})" \
  src/index.ts

さて、このサブコマンドではsrc/index.tsなどのエントリパスを渡さないと空のアプリが使われます。

Terminal

http://localhost:7070にアクセスすると404になります。これを応用すると、手元にファイルがなくてもミドルウェアを組み合わせたサーバーを立ち上げることができます。

例えば、手元のファイルをサーブするサーバーを立ち上げられます。

hono serve \
 --use "serveStatic({root:'./'})"

ヘルパーを使うことも可能なので、Proxyヘルパーのproxyを利用してRamen APIのリバースプロキシを立てることもできます。

hono serve \
 --use '(c) => proxy(`https://ramen-api.dev${new URL(c.req.url).pathname}`)'

hono optimize

最後のコマンドはhono optimizeです。

hono optimize [entry]

コマンドの説明の前にHonoのルーターについて話しましょう。RegExpRouterはJavaScript界の高速なルーターのうちの一つです。条件によっては1位の速さをほこります。RegExpRouterのアイデアはダイナミックルートの情報を一つの大きな正規表現にして、リクエストが来たら一度にマッチさせるということです。これには弱点があります。

  1. 初期化が遅い
  2. ファイルサイズは大きくなる

それぞれを解決するために、Honoでは2つのルーターが発明され、それぞれプリセットという形で提供されています。

  1. LinearRouter - hono/quick
  2. PatternRouter - hono/tiny

確かに、LinearRouterは初期化込みのベンチでは速く、PatternRouterのバンドルサイズは小さいです。しかし、私はRegExpRouterを使いたいのです!

そこで今日は7つの目の(6個は今ある5個のルーターと廃止になったStaticRouter)ルーターを紹介します。PreparedRegExpRouterです。

PreparedRegExpRouterのアイデアは、RegExpRouterの時に作る正規表現相当のルート情報を予め文字列にしてハードコードするというものです。

このようなアプリがあったとします。

import { Hono } from 'hono'

const app = new Hono()

app.get('/posts', (c) => c.json({}))
app.get('/posts/:id', (c) => c.json({}))
app.put('/posts', (c) => c.json({}))

export default app

このルート情報を予め文字列にしてルーターのコンストラクタに渡します。

const routerParams = [
  { ALL: [/^\/posts\/([^/]+)$()/, [0, 0, []], { '/posts': [[], []] }] },
  { '/posts': [[['']]], '/posts/:id': [[[2], { id: 1 }]] }
]
const router = new PreparedRegExpRouter(...routerParams)

これでまず初期化が速くなりました。初期化を含めたルーターのパフォーマンスを測るベンチマーク結果を見てみましょう。

Benchmark

これを並べ替えると...

Bechmark

LinearRouterは流石に一位ですが、PreparedRegExpRouterが2位につけています。RegExpRouterはだいぶ後ろです。比べるとPrepareRegExpRouterはRegExpRouterに比べて(初期化を含んだベンチマークで)16.5倍速くなっています。

Hono CLIの話に戻りましょう。

hono optimizeは「あなたのアプリの"Honoを"最適化します」。では最適化されたHonoとは一体なんでしょうか?それはこれです。

class Hono extends HonoBase {
  constructor(options = {}) {
    super(options)
    const routerParams = [
      { ALL: [/^\/posts\/([^/]+)$()/, [0, 0, []], { '/posts': [[], []] }] },
      { '/posts': [[['']]], '/posts/:id': [[[2], { id: 1 }]] }
    ]
    this.router = new PreparedRegExpRouter(...routerParams)
  }
}

つまり、そのアプリに合わせたPreparedRegExpRouterが実装されたHonoになります。

hono optimizeを実行してみましょう。

Terminal

すると、dist/index.jssrc/index.tsの最適化された結果が書き出されます。これをそのままwrangler deployするイメージです。

hono optimize src/index.ts
wrangler deploy dist/index.js

ではどのくらい小さくなったのか?最適化なしでビルドしてミニファイしたものと、hono optimizeminifyオプションで実行したファイル、更にhono/tinyのプリセットを使ったものと比べてみます。

Compare

hono/tinyのサイズには及ばないものの、元アプリの18.0KBに対して、11.14KBとかなり小さくなっています。値にすると38%分小さくなりました。

つまりhono optimizeはあなたのアプリを速く、小さくするのです。このようにHono CLIはhono servehono optimizeという人間向けのシンプルなサブコマンドを提供します。

まとめ

以上、Hono CLIは5つのサブコマンドを提供します。

  1. hono docs
  2. hono search
  3. hono request
  4. hono serve
  5. hono optimize

これらは人間とAIのためのコマンドです。

Hono CLIのレポジトリは公開されています。

https://github.com/honojs/cli

そして、今すぐあなたもHono CLIを試すことができます。

npm i @hono/cli

では、楽しんでください。

Discussion