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:
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:
"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.tsnext tosrc/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'sunstable_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 eachsrc/**/*.tsfile 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 separatehonojs/middlewarerepo, 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:
- Add a failing case to
src/utils/url.test.ts. - Run just that file:
bunx vitest --run src/utils/url.test.ts --project main. - Make it pass in
src/utils/url.ts. - 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
"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 testbefore review (stated indocs/CONTRIBUTING.md).