Hono is a single-package repo (no monorepo tooling) that uses Bun as its package manager and script runner. Everything you need for a first contribution is bun, git, and about five minutes of installs.

Setup

The contribution guide's own commands, verified against package.json:

docs/CONTRIBUTING.md
git clone git@github.com:honojs/hono.git && cd hono && bun install --frozen-lockfile

Then run the main test suite. test first type-checks the spec tsconfig, then runs Vitest:

package.json
"test": "tsc -p tsconfig.spec.json && vitest --run",
bun run test

This runs the main Vitest project plus two JSX-runtime projects and the runtime projects configured under runtime-tests/*/vitest.config.ts (node, fastly, workerd, lambda, lambda-edge). Deno and Bun have their own native runners (bun test:deno, bun test:bun); you rarely need them locally unless you touch an adapter -- CI runs everything.

Repo tour

  • src/ -- the entire shipped library. Core files sit at the top level (hono.ts, hono-base.ts, context.ts, request.ts, compose.ts, router.ts); subsystems live in directories: router/ (five router implementations), middleware/ (26 built-in middleware), helper/ (cookie, streaming, testing, factory, ...), adapter/ (per-runtime glue), jsx/, client/, validator/, utils/.
  • Tests are colocated: src/hono.test.ts next to src/hono.ts. Every behavior change is expected to come with a colocated test.
  • runtime-tests/ -- one directory per runtime (node, bun, deno, workerd, fastly, lambda, lambda-edge, deno-jsx). These boot the real runtime (workerd via wrangler's unstable_dev, for example) and assert end-to-end behavior.
  • benchmarks/ -- router and HTTP-server benchmarks comparing Hono's routers against find-my-way, express, koa-router and others.
  • perf-measures/ -- CI-tracked regression checks for type-check time and minified bundle size.
  • build/ -- the esbuild-based build script (build/build.ts) that turns each src/**/*.ts file into ESM and CJS outputs.
  • docs/CONTRIBUTING.md -- includes the project's AI-usage policy and the rule that third-party middleware lives in the separate honojs/middleware repo, not here.

Your first change

A low-risk, well-contained place to work is src/utils/ -- small pure functions, each with a colocated test file. For example, src/utils/url.ts (path parsing, mergePath, query helpers) is fully covered by src/utils/url.test.ts. The loop:

  1. Add a failing case to src/utils/url.test.ts.
  2. Run just that file: bunx vitest --run src/utils/url.test.ts --project main.
  3. Make it pass in src/utils/url.ts.
  4. Run the full gate before pushing:
bun run test
bun run lint
bun run format

lint and format (ESLint with @hono/eslint-config, Prettier check mode) are enforced in CI; format:fix and lint:fix exist for cleanup.

Useful selective test commands

package.json
"test:node": "vitest --run --project node",
"test:workerd": "vitest --run --project workerd",
"test:bun": "bun test --jsx-import-source ../../src/jsx runtime-tests/bun/*",

If you change a router, also look at src/router/common.case.test.ts -- a shared spec that all routers must pass, which is how the five implementations stay behaviorally interchangeable.

What not to do

  • Do not add a runtime dependency. The package has none, and that is a design constraint, not an accident (see Zero dependencies by design).
  • Do not build third-party integrations here; they belong in honojs/middleware.
  • PRs must pass bun run test before review (stated in docs/CONTRIBUTING.md).

Sources: docs/CONTRIBUTING.md, package.json, vitest.config.ts · last synced 2026-08-10 · 26de731 · version 4.13.1